mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2025-07-13 14:34:28 +02:00
Compare commits
115 Commits
Author | SHA1 | Date | |
---|---|---|---|
0c1122b689 | |||
d4f8dd46b5 | |||
986c5f0438 | |||
a81907bdb9 | |||
63a7a2cdbd | |||
f040cc41c2 | |||
23883268e3 | |||
a1e14503fd | |||
0766869ad1 | |||
6c1db141a1 | |||
b117959725 | |||
e882622233 | |||
c13b5a5e90 | |||
cd1406e158 | |||
59deee020a | |||
476013706e | |||
a43a9d9e32 | |||
abc69b3361 | |||
83bfb135c6 | |||
2dfb560f8b | |||
7330cc35ba | |||
5d760e7315 | |||
2a85baaf81 | |||
2b92044504 | |||
f4aa951165 | |||
038fd8686b | |||
7084105dc4 | |||
9f06f22281 | |||
cb6b493df6 | |||
2753a82eb9 | |||
ddb8ddff9c | |||
c992b2ba89 | |||
ed723a46dc | |||
6906a9a708 | |||
6a381a55fe | |||
fc3580a948 | |||
6b5c30b4be | |||
a0cdb261ff | |||
34dbf6c2fe | |||
86d842167a | |||
16c8b830b8 | |||
7ff051fc8b | |||
927452d1eb | |||
f1ad982fd1 | |||
c188cb8dad | |||
4919783da5 | |||
86281750e5 | |||
d92b72ed02 | |||
a195c023af | |||
43000ff01f | |||
c6f17cc20f | |||
41146adbdf | |||
cd80c3037d | |||
97934ab995 | |||
088646e1cf | |||
3f0aad7564 | |||
d36df63b57 | |||
a5dfac7495 | |||
0258384d53 | |||
9fec2195d0 | |||
8ccbdaed94 | |||
4804db38a5 | |||
ddd984215a | |||
efe7c01015 | |||
600fa4ce59 | |||
688810f143 | |||
858cc101b6 | |||
43114f43bf | |||
c4abceef79 | |||
2e91e05d7b | |||
7e3db6b639 | |||
acb6b66bdb | |||
b0a66eef78 | |||
ed88322496 | |||
59959bf0e5 | |||
19c45871fa | |||
b0e4646f9d | |||
12ef2fe2c3 | |||
4630060ee7 | |||
74a6fb1860 | |||
a3ea0a99ff | |||
fdfa700442 | |||
d759baf11e | |||
7f6ffcda22 | |||
87ca4db73f | |||
a83f7f50ff | |||
6a4ce57d09 | |||
d4c594cec4 | |||
35c674b633 | |||
97d4955666 | |||
e138232a93 | |||
8d8225f4a1 | |||
7045d9dad9 | |||
4f10021e7e | |||
57d5894f33 | |||
2b8a1c8481 | |||
948ee16b06 | |||
c7101ae9dd | |||
599640eda1 | |||
a3f3bbfe11 | |||
26d485cfc5 | |||
64d306a322 | |||
067683cbcd | |||
50f27974cf | |||
ae9a91719b | |||
1a17933635 | |||
acea6fdbed | |||
66fa12f928 | |||
f4b8983c61 | |||
ed4e2fc38a | |||
1ed7f0089c | |||
563d53edd4 | |||
316b0ad559 | |||
f8b471acfd | |||
cedd7d104f |
22
.github/codeql.yml
vendored
Normal file
22
.github/codeql.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
paths-ignore:
|
||||||
|
- testpdfio.c
|
||||||
|
|
||||||
|
query-filters:
|
||||||
|
- exclude:
|
||||||
|
id: cpp/commented-out-code
|
||||||
|
- exclude:
|
||||||
|
id: cpp/toctou-race-condition
|
||||||
|
- exclude:
|
||||||
|
id: cpp/weak-cryptographic-algorithm
|
||||||
|
- exclude:
|
||||||
|
id: cpp/world-writable-file-creation
|
||||||
|
- exclude:
|
||||||
|
id: cpp/uncontrolled-allocation-size
|
||||||
|
- exclude:
|
||||||
|
id: cpp/path-injection
|
||||||
|
- exclude:
|
||||||
|
id: cpp/stack-address-escape
|
||||||
|
- exclude:
|
||||||
|
id: cpp/loop-variable-changed
|
||||||
|
- exclude:
|
||||||
|
id: cpp/long-switch
|
41
.github/workflows/build.yml
vendored
41
.github/workflows/build.yml
vendored
@ -9,23 +9,26 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-linux:
|
build-linux:
|
||||||
|
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Checkout PDFio sources
|
||||||
- name: update build environment
|
uses: actions/checkout@v4
|
||||||
|
- name: Update Build Environment
|
||||||
run: sudo apt-get update --fix-missing -y
|
run: sudo apt-get update --fix-missing -y
|
||||||
- name: install prerequisites
|
- name: Install Prerequisites
|
||||||
run: sudo apt-get install -y cppcheck zlib1g-dev
|
run: sudo apt-get install -y cppcheck zlib1g-dev
|
||||||
- name: make
|
- name: Configure PDFio
|
||||||
|
run: ./configure --enable-debug --enable-sanitizer --enable-maintainer
|
||||||
|
- name: Build PDFio
|
||||||
run: make "COMMONFLAGS=-g -fsanitize=address"
|
run: make "COMMONFLAGS=-g -fsanitize=address"
|
||||||
- name: test
|
- name: Test PDFio
|
||||||
env:
|
env:
|
||||||
ASAN_OPTIONS: leak_check_at_exit=false
|
ASAN_OPTIONS: leak_check_at_exit=false
|
||||||
run: make test
|
run: make test
|
||||||
- name: clang static analyzer
|
- name: Run Clang Static Analyzer
|
||||||
run: make CC=clang "GHA_ERROR=::error::" clang
|
run: make CC=clang "GHA_ERROR=::error::" clang
|
||||||
- name: cppcheck
|
- name: Run cppcheck
|
||||||
run: make "GHA_ERROR=::error::" cppcheck
|
run: make "GHA_ERROR=::error::" cppcheck
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
@ -33,12 +36,15 @@ jobs:
|
|||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Checkout PDFio sources
|
||||||
- name: make
|
uses: actions/checkout@v4
|
||||||
|
- name: Configure PDFio
|
||||||
|
run: ./configure --enable-debug --enable-sanitizer --enable-maintainer
|
||||||
|
- name: Build PDFio
|
||||||
run: make "COMMONFLAGS=-g -fsanitize=address"
|
run: make "COMMONFLAGS=-g -fsanitize=address"
|
||||||
- name: test
|
- name: Test PDFio
|
||||||
run: make test
|
run: make test
|
||||||
- name: clang static analyzer
|
- name: Run Clang Static Analyzer
|
||||||
run: make CC=clang "GHA_ERROR=::error::" clang
|
run: make CC=clang "GHA_ERROR=::error::" clang
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
@ -46,10 +52,13 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Checkout PDFio sources
|
||||||
- name: setup-msbuild
|
uses: actions/checkout@v4
|
||||||
|
- name: Setup MSBuild
|
||||||
uses: microsoft/setup-msbuild@v1.0.2
|
uses: microsoft/setup-msbuild@v1.0.2
|
||||||
- name: nuget restore
|
- name: Nuget Restore
|
||||||
run: nuget restore pdfio.sln
|
run: nuget restore pdfio.sln
|
||||||
- name: msbuild
|
- name: Build PDFio
|
||||||
run: msbuild pdfio.sln
|
run: msbuild pdfio.sln
|
||||||
|
- name: Test PDFio
|
||||||
|
run: .\runtests.bat x64\Debug
|
||||||
|
50
.github/workflows/codeql.yml
vendored
Normal file
50
.github/workflows/codeql.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
schedule:
|
||||||
|
- cron: "46 3 * * 0"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ cpp ]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout PDFio sources
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Update Build Environment
|
||||||
|
run: sudo apt-get update --fix-missing -y
|
||||||
|
|
||||||
|
- name: Install Prerequisites
|
||||||
|
run: sudo apt-get install -y zlib1g-dev
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
config-file: ./.github/codeql.yml
|
||||||
|
queries: +security-and-quality
|
||||||
|
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
|
with:
|
||||||
|
category: "/language:${{ matrix.language }}"
|
15
.github/workflows/coverity.yml
vendored
15
.github/workflows/coverity.yml
vendored
@ -7,10 +7,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: Coverity
|
environment: Coverity
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Checkout PDFio sources
|
||||||
- name: update build environment
|
uses: actions/checkout@v4
|
||||||
|
- name: Update Build Environment
|
||||||
run: sudo apt-get update --fix-missing -y
|
run: sudo apt-get update --fix-missing -y
|
||||||
- name: install prerequisites
|
- name: Install Prerequisites
|
||||||
run: sudo apt-get install -y zlib1g-dev
|
run: sudo apt-get install -y zlib1g-dev
|
||||||
- name: Download Coverity Build Tool
|
- name: Download Coverity Build Tool
|
||||||
run: |
|
run: |
|
||||||
@ -20,11 +21,15 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
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: |
|
run: |
|
||||||
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
|
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
|
||||||
cov-build --dir cov-int make
|
cov-build --dir cov-int make
|
||||||
- name: Submit the result to Coverity Scan
|
|
||||||
|
- name: Submit the Result to Coverity Scan
|
||||||
run: |
|
run: |
|
||||||
tar czvf cov.tgz cov-int
|
tar czvf cov.tgz cov-int
|
||||||
curl \
|
curl \
|
||||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -5,10 +5,18 @@
|
|||||||
*.so.1
|
*.so.1
|
||||||
/.vs
|
/.vs
|
||||||
/afl-output
|
/afl-output
|
||||||
|
/autom4te.cache
|
||||||
|
/config.log
|
||||||
|
/config.status
|
||||||
/doc/pdfio.epub
|
/doc/pdfio.epub
|
||||||
|
/Makefile
|
||||||
/packages
|
/packages
|
||||||
|
/pdfio.pc
|
||||||
/pdfio.xcodeproj/xcshareddata
|
/pdfio.xcodeproj/xcshareddata
|
||||||
|
/pdfio-*.tar.gz*
|
||||||
|
/pdfio-*.zip*
|
||||||
/pdfiototext
|
/pdfiototext
|
||||||
/testpdfio
|
/testpdfio
|
||||||
/testpdfio-*.pdf
|
/testpdfio-*.pdf
|
||||||
|
/testttf
|
||||||
/x64
|
/x64
|
||||||
|
91
CHANGES.md
91
CHANGES.md
@ -2,9 +2,98 @@ Changes in PDFio
|
|||||||
================
|
================
|
||||||
|
|
||||||
|
|
||||||
v1.0.1 (Month DD, YYYY)
|
v1.3.0 (June 28, 2024)
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- Added `pdfioFileGetCatalog` API for accessing the root/catalog object of a
|
||||||
|
PDF file (Issue #67)
|
||||||
|
- Updated number support to avoid locale issues (Issue #61)
|
||||||
|
- Updated the PDFio private header to allow compilation with MingW; note that
|
||||||
|
MingW is NOT a supported toolchain for PDFio (Issue #66)
|
||||||
|
- Optimized string pool code.
|
||||||
|
|
||||||
|
|
||||||
|
v1.2.0 (January 24, 2024)
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
- Now use autoconf to configure the PDFio sources (Issue #54)
|
||||||
|
- Added `pdfioFileCreateNumberObj` and `pdfioFileCreateStringObj` functions
|
||||||
|
(Issue #14)
|
||||||
|
- Added `pdfioContentTextMeasure` function (Issue #17)
|
||||||
|
- Added `pdfioContentTextNewLineShow` and `pdfioContentTextNewLineShowf`
|
||||||
|
functions (Issue #24)
|
||||||
|
- Renamed `pdfioContentTextNextLine` to `pdfioContentTextNewLine`.
|
||||||
|
- Updated the maximum number of object streams in a single file from 4096 to
|
||||||
|
8192 (Issue #58)
|
||||||
|
- Updated the token reading code to protect against some obvious abuses of the
|
||||||
|
PDF format.
|
||||||
|
- Updated the xref reading code to protect against loops.
|
||||||
|
- Updated the object handling code to use a binary insertion algorithm -
|
||||||
|
provides a significant (~800x) improvement in open times.
|
||||||
|
- Fixed handling of encrypted PDFs with per-object file IDs (Issue #42)
|
||||||
|
- Fixed handling of of trailer dictionaries that started immediately after the
|
||||||
|
"trailer" keyword (Issue #58)
|
||||||
|
- Fixed handling of invalid, but common, PDF files with a generation number of
|
||||||
|
65536 in the xref table (Issue #59)
|
||||||
|
|
||||||
|
|
||||||
|
v1.1.4 (December 3, 2023)
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
- 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)
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
- Updated `pdfioContentSetDashPattern` to support setting a solid (0 length)
|
||||||
|
dash pattern (Issue #41)
|
||||||
|
- Fixed an issue with broken PDF files containing extra CR and/or LF separators
|
||||||
|
after the object stream token (Issue #40)
|
||||||
|
- Fixed an issue with PDF files produced by Crystal Reports (Issue #45)
|
||||||
|
- Fixed an issue with PDF files produced by Microsoft Reporting Services
|
||||||
|
(Issue #46)
|
||||||
|
- Fixed support for compound filters where the filter array consists of a
|
||||||
|
single named filter (Issue #47)
|
||||||
|
- Fixed builds on Windows - needed windows.h header for temporary files
|
||||||
|
(Issue #48)
|
||||||
|
|
||||||
|
|
||||||
|
v1.1.1 (March 20, 2023)
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
- CVE-2023-28428: Fixed a potential denial-of-service with corrupt PDF files.
|
||||||
|
- Fixed a few build issues.
|
||||||
|
|
||||||
|
|
||||||
|
v1.1.0 (February 6, 2023)
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
- CVE-2023-24808: Fixed a potential denial-of-service with corrupt PDF files.
|
||||||
|
- Added `pdfioFileCreateTemporary` function (Issue #29)
|
||||||
|
- Added `pdfioDictIterateKeys` function (Issue #31)
|
||||||
|
- Added `pdfioContentPathEnd` function.
|
||||||
|
- Added protection against opening multiple streams in the same file at the
|
||||||
|
same time.
|
||||||
|
- Documentation updates (Issue #37)
|
||||||
|
- Fixed "install-shared" target (Issue #32)
|
||||||
|
- Fixed `pdfioFileGet...` metadata APIs (Issue #33)
|
||||||
|
- Fixed `pdfioContentMatrixRotate` function.
|
||||||
|
|
||||||
|
|
||||||
|
v1.0.1 (March 2, 2022)
|
||||||
|
----------------------
|
||||||
|
|
||||||
- Added missing `pdfioPageGetNumStreams` and `pdfioPageOpenStream` functions.
|
- Added missing `pdfioPageGetNumStreams` and `pdfioPageOpenStream` functions.
|
||||||
- Added demo pdfiototext utility.
|
- Added demo pdfiototext utility.
|
||||||
- Fixed bug in `pdfioStreamGetToken`.
|
- Fixed bug in `pdfioStreamGetToken`.
|
||||||
|
@ -43,14 +43,17 @@ Contents
|
|||||||
Build System
|
Build System
|
||||||
------------
|
------------
|
||||||
|
|
||||||
The build system uses a simple POSIX makefile to build a static or shared
|
The build system uses [GNU autoconf][AUTOCONF] to create a simple POSIX makefile
|
||||||
library. To improve portability, makefiles *must not* make use of features
|
to build static and/or shared libraries. To improve portability, makefiles
|
||||||
unique to GNU make. See the [Makefile Guidelines](#makefile-guidelines) section
|
*must not* make use of features unique to GNU make. See the
|
||||||
for a description of the allowed make features and makefile guidelines.
|
[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
|
An Xcode project is provided for macOS/iOS developers, and a Visual Studio
|
||||||
solution and projects for Windows developers.
|
solution and projects for Windows developers.
|
||||||
|
|
||||||
|
[AUTOCONF]: https://www.gnu.org/software/autoconf/
|
||||||
|
|
||||||
|
|
||||||
Version Numbering
|
Version Numbering
|
||||||
-----------------
|
-----------------
|
||||||
@ -118,7 +121,7 @@ the source file and the copyright and licensing notice:
|
|||||||
//
|
//
|
||||||
// Description of file contents.
|
// Description of file contents.
|
||||||
//
|
//
|
||||||
// Copyright YYYY by AUTHOR.
|
// Copyright © YYYY by AUTHOR.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
@ -330,7 +333,7 @@ typedef, for example:
|
|||||||
|
|
||||||
All constant names are uppercase with underscores between words, e.g.,
|
All constant names are uppercase with underscores between words, e.g.,
|
||||||
`PDFIO_THIS_CONSTANT`, `PDFIO_THAT_CONSTANT`, etc. Constants begin with the
|
`PDFIO_THIS_CONSTANT`, `PDFIO_THAT_CONSTANT`, etc. Constants begin with the
|
||||||
"PDFio\_" prefix to avoid conflicts with system constants. Private constants
|
"PDFIO\_" prefix to avoid conflicts with system constants. Private constants
|
||||||
start with an underscore, e.g., `_PDFIO_THIS_CONSTANT`,
|
start with an underscore, e.g., `_PDFIO_THIS_CONSTANT`,
|
||||||
`_PDFIO_THAT_CONSTANT`, etc.
|
`_PDFIO_THAT_CONSTANT`, etc.
|
||||||
|
|
||||||
@ -369,17 +372,19 @@ extensions MUST NOT be used.
|
|||||||
The following variables are defined in the makefile:
|
The following variables are defined in the makefile:
|
||||||
|
|
||||||
- `AR`; the static library archiver command,
|
- `AR`; the static library archiver command,
|
||||||
- `ARFLAGS`; options for the static library archiver command,
|
- `ARFLAGS`; options for the static library archiver,
|
||||||
- `CC`; the C compiler command,
|
- `CC`; the C compiler command,
|
||||||
- `CFLAGS`; options for the C compiler command,
|
- `CFLAGS`; options for the C compiler,
|
||||||
- `CODESIGN_IDENTITY`: the code signing identity,
|
- `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.
|
- `DESTDIR`/`DSTROOT`: the destination root directory when installing.
|
||||||
- `DSO`; the shared library building command,
|
- `DSO`; the shared library building command,
|
||||||
- `DSOFLAGS`; options for 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,
|
- `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,
|
- `LIBS`; libraries for all programs,
|
||||||
|
- `OPTIM`; common compiler optimization options,
|
||||||
- `prefix`; the installation prefix directory,
|
- `prefix`; the installation prefix directory,
|
||||||
- `RANLIB`; the static library indexing command,
|
- `RANLIB`; the static library indexing command,
|
||||||
- `SHELL`; the sh (POSIX shell) command,
|
- `SHELL`; the sh (POSIX shell) command,
|
||||||
@ -388,11 +393,7 @@ The following variables are defined in the makefile:
|
|||||||
The following standard targets are defined in the makefile:
|
The following standard targets are defined in the makefile:
|
||||||
|
|
||||||
- `all`; creates the static library and unit test program.
|
- `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
|
- `clean`; removes all target programs libraries, documentation files, and
|
||||||
object files,
|
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`; installs all distribution files in their corresponding locations.
|
||||||
- `install-shared`; same as `install` but also installs the shared library.
|
|
||||||
- `test`; runs the unit test program, building it as needed.
|
- `test`; runs the unit test program, building it as needed.
|
||||||
|
209
Makefile
209
Makefile
@ -1,209 +0,0 @@
|
|||||||
#
|
|
||||||
# Makefile for PDFio.
|
|
||||||
#
|
|
||||||
# Copyright © 2021-2022 by Michael R Sweet.
|
|
||||||
#
|
|
||||||
# Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|
||||||
# information.
|
|
||||||
#
|
|
||||||
|
|
||||||
# POSIX makefile
|
|
||||||
.POSIX:
|
|
||||||
|
|
||||||
# Variables...
|
|
||||||
AR = ar
|
|
||||||
ARFLAGS = cr
|
|
||||||
CC = cc
|
|
||||||
CFLAGS =
|
|
||||||
CODESIGN_IDENTITY = Developer ID
|
|
||||||
COMMONFLAGS = -Os -g
|
|
||||||
#COMMONFLAGS = -O0 -g -fsanitize=address
|
|
||||||
CPPFLAGS = '-DPDFIO_VERSION="$(VERSION)"'
|
|
||||||
DESTDIR = $(DSTROOT)
|
|
||||||
DSO = cc
|
|
||||||
DSOFLAGS =
|
|
||||||
DSONAME =
|
|
||||||
LDFLAGS =
|
|
||||||
LIBS = -lm -lz
|
|
||||||
RANLIB = ranlib
|
|
||||||
VERSION = 1.0.1
|
|
||||||
prefix = /usr/local
|
|
||||||
|
|
||||||
|
|
||||||
# Base rules
|
|
||||||
.SUFFIXES: .c .h .o
|
|
||||||
.c.o:
|
|
||||||
$(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=10.14 -arch x86_64 -arch arm64" clean all
|
|
||||||
|
|
||||||
|
|
||||||
# Clean everything
|
|
||||||
clean:
|
|
||||||
rm -f $(TARGETS) $(OBJS)
|
|
||||||
|
|
||||||
|
|
||||||
# Install everything
|
|
||||||
install: $(TARGETS)
|
|
||||||
-mkdir -p $(DESTDIR)$(prefix)/include
|
|
||||||
cp $(PUBHEADERS) $(DESTDIR)$(prefix)/include
|
|
||||||
-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
|
|
||||||
-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
|
|
||||||
-mkdir -p $(DESTDIR)$(prefix)/share/doc/pdfio
|
|
||||||
cp doc/pdfio.html doc/pdfio-512.png LICENSE NOTICE $(DESTDIR)$(prefix)/share/doc/pdfio
|
|
||||||
-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
|
|
||||||
|
|
||||||
valgrind: testpdfio
|
|
||||||
valgrind --leak-check=full ./testpdfio
|
|
||||||
|
|
||||||
|
|
||||||
# pdfio library
|
|
||||||
libpdfio.a: $(LIBOBJS)
|
|
||||||
$(AR) $(ARFLAGS) $@ $(LIBOBJS)
|
|
||||||
$(RANLIB) $@
|
|
||||||
|
|
||||||
libpdfio.so.1: $(LIBOBJS)
|
|
||||||
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -shared -o $@ -Wl,-soname,$@ $(LIBOBJS) $(LIBS)
|
|
||||||
|
|
||||||
libpdfio.1.dylib: $(LIBOBJS)
|
|
||||||
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -dynamiclib -o $@ -install_name $(prefix)/lib/$@ -current_version $(VERSION) -compatibility_version 1.0 $(LIBOBJS) $(LIBS)
|
|
||||||
|
|
||||||
|
|
||||||
# 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
|
|
||||||
$(CC) $(LDFLAGS) $(COMMONFLAGS) -o $@ pdfiototext.o libpdfio.a $(LIBS)
|
|
||||||
|
|
||||||
|
|
||||||
# pdfio test program
|
|
||||||
testpdfio: testpdfio.o libpdfio.a
|
|
||||||
$(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-2022 by Michael R Sweet" \
|
|
||||||
--docversion $(VERSION)
|
|
||||||
|
|
||||||
.PHONY: doc
|
|
||||||
doc:
|
|
||||||
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" $(PUBHEADERS) $(PUBOBJS:.o=.c) --body doc/pdfio.md --coverimage doc/pdfio-512.png pdfio.xml >doc/pdfio.html
|
|
||||||
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" --body doc/pdfio.md --coverimage doc/pdfio-epub.png pdfio.xml --epub doc/pdfio.epub
|
|
||||||
codedoc $(DOCFLAGS) --title "pdf read/write library" --man pdfio --section 3 --body doc/pdfio.md pdfio.xml >doc/pdfio.3
|
|
||||||
rm -f pdfio.xml
|
|
||||||
|
|
||||||
|
|
||||||
# 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
|
|
262
Makefile.in
Normal file
262
Makefile.in
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
#
|
||||||
|
# Makefile for PDFio.
|
||||||
|
#
|
||||||
|
# Copyright © 2021-2024 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
|
||||||
|
LANG=fr_FR.UTF-8 ./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
2
NOTICE
@ -1,6 +1,6 @@
|
|||||||
PDFio - PDF Read/Write Library
|
PDFio - PDF Read/Write Library
|
||||||
|
|
||||||
Copyright © 2021 by Michael R Sweet.
|
Copyright © 2021-2024 by Michael R Sweet.
|
||||||
|
|
||||||
(Optional) Exceptions to the Apache 2.0 License:
|
(Optional) Exceptions to the Apache 2.0 License:
|
||||||
================================================
|
================================================
|
||||||
|
74
README.md
74
README.md
@ -3,10 +3,8 @@ pdfio - PDF Read/Write Library
|
|||||||
|
|
||||||

|

|
||||||

|

|
||||||
[](https://github.com/michaelrsweet/pdfio/actions/workflows/build.yml)
|
[](https://github.com/michaelrsweet/pdfio/actions/workflows/build.yml)
|
||||||
[](https://scan.coverity.com/projects/michaelrsweet-pdfio)
|
[](https://scan.coverity.com/projects/michaelrsweet-pdfio)
|
||||||
[](https://lgtm.com/projects/g/michaelrsweet/pdfio/context:cpp)
|
|
||||||
[](https://lgtm.com/projects/g/michaelrsweet/pdfio/)
|
|
||||||
|
|
||||||
PDFio is a simple C library for reading and writing PDF files. The primary
|
PDFio is a simple C library for reading and writing PDF files. The primary
|
||||||
goals of PDFio are:
|
goals of PDFio are:
|
||||||
@ -30,7 +28,7 @@ PDFio requires the following to build the software:
|
|||||||
|
|
||||||
- A C99 compiler such as Clang, GCC, or MS Visual C
|
- A C99 compiler such as Clang, GCC, or MS Visual C
|
||||||
- A POSIX-compliant `make` program
|
- A POSIX-compliant `make` program
|
||||||
- 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.
|
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
|
||||||
|
|
||||||
@ -38,15 +36,27 @@ IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
|
|||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
See the man page (`pdfio.3`), frequently ask questions (`FAQ.md`), and full HTML
|
See the man page (`pdfio.3`) and full HTML documentation (`pdfio.html`) for
|
||||||
documentation (`pdfio.html`) for information on using PDFio.
|
information on using PDFio.
|
||||||
|
|
||||||
|
|
||||||
Installing pdfio
|
Installing PDFio
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
PDFio comes with a portable makefile that will work on any POSIX-compliant
|
PDFio uses a configure script on Unix systems to generate a makefile:
|
||||||
system with ZLIB installed. To make it, run:
|
|
||||||
|
./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
|
make all
|
||||||
|
|
||||||
@ -56,45 +66,7 @@ To test it, run:
|
|||||||
|
|
||||||
To install it, run:
|
To install it, run:
|
||||||
|
|
||||||
make install
|
sudo 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")
|
|
||||||
|
|
||||||
|
|
||||||
Visual Studio Project
|
Visual Studio Project
|
||||||
@ -113,15 +85,11 @@ generates a static library that will be installed under "/usr/local" with:
|
|||||||
|
|
||||||
sudo xcodebuild install
|
sudo xcodebuild install
|
||||||
|
|
||||||
You can reproduce this with the makefile using:
|
|
||||||
|
|
||||||
sudo make COMMONFLAGS="-Os -mmacosx-version-min=10.14 -arch x86_64 -arch arm64" install
|
|
||||||
|
|
||||||
|
|
||||||
Legal Stuff
|
Legal Stuff
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
PDFio is Copyright © 2021 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
|
This software is licensed under the Apache License Version 2.0 with an
|
||||||
(optional) exception to allow linking against GPL2/LGPL2 software. See the
|
(optional) exception to allow linking against GPL2/LGPL2 software. See the
|
||||||
|
29
SECURITY.md
29
SECURITY.md
@ -5,6 +5,25 @@ This file describes how security issues are reported and handled, and what the
|
|||||||
expectations are for security issues reported to this project.
|
expectations are for security issues reported to this project.
|
||||||
|
|
||||||
|
|
||||||
|
Reporting a Security Bug
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
For the purposes of this project, a security bug is a software defect that
|
||||||
|
allows a *local or remote user* to gain unauthorized access or privileges on the
|
||||||
|
host computer or to cause the software to crash. Such defects should be
|
||||||
|
reported to the project security advisory page at
|
||||||
|
<https://github.com/michaelrsweet/pdfio/security/advisories>.
|
||||||
|
|
||||||
|
Alternately, security bugs can be reported to "security AT msweet.org" using the
|
||||||
|
PGP public key below. Expect a response within 5 business days. Any proposed
|
||||||
|
embargo date should be at least 30 days and no more than 90 days in the future.
|
||||||
|
|
||||||
|
> *Note:* If you've found a software defect that allows a *program* to gain
|
||||||
|
> unauthorized access or privileges on the host computer or causes the program
|
||||||
|
> to crash, that defect should be reported as an ordinary project issue at
|
||||||
|
> <https://github.com/michaelrsweet/pdfio/issues>.
|
||||||
|
|
||||||
|
|
||||||
Responsible Disclosure
|
Responsible Disclosure
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -35,7 +54,7 @@ starting at 0. A feature release has a "PATCH" value of 0, for example:
|
|||||||
1.1.0
|
1.1.0
|
||||||
2.0.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:
|
semantic version numbers of the form:
|
||||||
|
|
||||||
MAJOR.MINORbNUMBER
|
MAJOR.MINORbNUMBER
|
||||||
@ -50,14 +69,6 @@ example:
|
|||||||
1.0rc1
|
1.0rc1
|
||||||
|
|
||||||
|
|
||||||
Reporting a Vulnerability
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Report all security issues to "security AT msweet.org". Expect a response
|
|
||||||
within 5 business days. Any proposed embargo date should be at least 30 days
|
|
||||||
and no more than 90 days in the future.
|
|
||||||
|
|
||||||
|
|
||||||
PGP Public Key
|
PGP Public Key
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
1774
config.guess
vendored
Executable file
1774
config.guess
vendored
Executable file
File diff suppressed because it is too large
Load Diff
1907
config.sub
vendored
Executable file
1907
config.sub
vendored
Executable file
File diff suppressed because it is too large
Load Diff
273
configure.ac
Normal file
273
configure.ac
Normal 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.3.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
|
313
doc/pdfio.3
313
doc/pdfio.3
@ -1,4 +1,4 @@
|
|||||||
.TH pdfio 3 "pdf read/write library" "2022-03-02" "pdf read/write library"
|
.TH pdfio 3 "pdf read/write library" "2024-06-24" "pdf read/write library"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
pdfio \- pdf read/write library
|
pdfio \- pdf read/write library
|
||||||
.SH Introduction
|
.SH Introduction
|
||||||
@ -34,7 +34,7 @@ PDFio is
|
|||||||
.I not
|
.I not
|
||||||
concerned with rendering or viewing a PDF file, although a PDF RIP or viewer could be written using it.
|
concerned with rendering or viewing a PDF file, although a PDF RIP or viewer could be written using it.
|
||||||
.PP
|
.PP
|
||||||
PDFio is Copyright \[co] 2021\-2022 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
|
.SS Requirements
|
||||||
.PP
|
.PP
|
||||||
PDFio requires the following to build the software:
|
PDFio requires the following to build the software:
|
||||||
@ -46,6 +46,10 @@ A C99 compiler such as Clang, GCC, or MS Visual C
|
|||||||
.PP
|
.PP
|
||||||
A POSIX\-compliant make program
|
A POSIX\-compliant make program
|
||||||
|
|
||||||
|
.IP \(bu 5
|
||||||
|
.PP
|
||||||
|
A POSIX\-compliant sh program
|
||||||
|
|
||||||
.IP \(bu 5
|
.IP \(bu 5
|
||||||
.PP
|
.PP
|
||||||
ZLIB (https://www.zlib.net) 1.0 or higher
|
ZLIB (https://www.zlib.net) 1.0 or higher
|
||||||
@ -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.
|
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
|
||||||
.SS Installing pdfio
|
.SS Installing pdfio
|
||||||
.PP
|
.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
|
.nf
|
||||||
|
|
||||||
make all
|
\./configure
|
||||||
|
make
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.PP
|
||||||
To test it, run:
|
To test it, run:
|
||||||
@ -70,78 +75,28 @@ To test it, run:
|
|||||||
To install it, run:
|
To install it, run:
|
||||||
.nf
|
.nf
|
||||||
|
|
||||||
make install
|
sudo make install
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.PP
|
||||||
If you want a shared library, run:
|
If you want a shared library, run:
|
||||||
.nf
|
.nf
|
||||||
|
|
||||||
make all\-shared
|
\./configure \-\-enable\-shared
|
||||||
make install\-shared
|
make
|
||||||
|
sudo make install
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.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
|
.nf
|
||||||
|
|
||||||
make install prefix=/some/other/directory
|
\./configure \-\-prefix=/some/other/directory
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.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".
|
Other configure options can be found using the \-\-help option:
|
||||||
.PP
|
.nf
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
|
\./configure \-\-help
|
||||||
|
.fi
|
||||||
.SS Visual Studio Project
|
.SS Visual Studio Project
|
||||||
.PP
|
.PP
|
||||||
The Visual Studio solution ("pdfio.sln") is provided for Windows developers and generates both a static library and DLL.
|
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
|
sudo xcodebuild install
|
||||||
.fi
|
.fi
|
||||||
.PP
|
|
||||||
You can reproduce this with the makefile using:
|
|
||||||
.nf
|
|
||||||
|
|
||||||
sudo make macos install
|
|
||||||
.fi
|
|
||||||
.SS Detecting PDFio
|
.SS Detecting PDFio
|
||||||
.PP
|
.PP
|
||||||
PDFio can be detected using the pkg\-config command, for example:
|
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:
|
You open an existing PDF file using the pdfioFileOpen function:
|
||||||
.nf
|
.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
|
.fi
|
||||||
.PP
|
.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
|
.nf
|
||||||
|
|
||||||
bool
|
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.
|
When you are done writing the stream, call pdfioStreamCLose to close both the stream and the object.
|
||||||
.SS PDF Content Helper Functions
|
.SS PDF Content Helper Functions
|
||||||
.PP
|
.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
|
.IP \(bu 5
|
||||||
.PP
|
.PP
|
||||||
Color Space Functions
|
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.
|
will embed the NotoSansJP Regular OpenType font with full support for Unicode.
|
||||||
.PP
|
.PP
|
||||||
Note: Not all fonts support Unicode.
|
Note: Not all fonts support Unicode.
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
Image Object Functions
|
Image Object Functions
|
||||||
.PP
|
.PP
|
||||||
@ -799,7 +764,15 @@ pdfioContentTextMoveTo moves within the current line in a text block
|
|||||||
|
|
||||||
.IP \(bu 5
|
.IP \(bu 5
|
||||||
.PP
|
.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
|
.IP \(bu 5
|
||||||
.PP
|
.PP
|
||||||
@ -839,7 +812,7 @@ PDFIO_ENCRYPTION_AES_128
|
|||||||
.TP 5
|
.TP 5
|
||||||
PDFIO_ENCRYPTION_AES_256
|
PDFIO_ENCRYPTION_AES_256
|
||||||
.br
|
.br
|
||||||
256-bit AES encryption (PDF 2.0)
|
256-bit AES encryption (PDF 2.0)
|
||||||
.TP 5
|
.TP 5
|
||||||
PDFIO_ENCRYPTION_NONE
|
PDFIO_ENCRYPTION_NONE
|
||||||
.br
|
.br
|
||||||
@ -1357,7 +1330,7 @@ bool pdfioContentFillAndStroke (
|
|||||||
.fi
|
.fi
|
||||||
.SS pdfioContentMatrixConcat
|
.SS pdfioContentMatrixConcat
|
||||||
Concatenate a matrix to the current graphics
|
Concatenate a matrix to the current graphics
|
||||||
state.
|
state.
|
||||||
.PP
|
.PP
|
||||||
.nf
|
.nf
|
||||||
bool pdfioContentMatrixConcat (
|
bool pdfioContentMatrixConcat (
|
||||||
@ -1440,6 +1413,14 @@ bool pdfioContentPathCurve23 (
|
|||||||
double y3
|
double y3
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
|
.SS pdfioContentPathEnd
|
||||||
|
Clear the current path.
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
bool pdfioContentPathEnd (
|
||||||
|
pdfio_stream_t *st
|
||||||
|
);
|
||||||
|
.fi
|
||||||
.SS pdfioContentPathLineTo
|
.SS pdfioContentPathLineTo
|
||||||
Add a straight line to the current path.
|
Add a straight line to the current path.
|
||||||
.PP
|
.PP
|
||||||
@ -1499,6 +1480,9 @@ bool pdfioContentSetDashPattern (
|
|||||||
double off
|
double off
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
|
.PP
|
||||||
|
This function sets the stroke pattern when drawing lines. If "on" and "off"
|
||||||
|
are 0, a solid line is drawn.
|
||||||
.SS pdfioContentSetFillColorDeviceCMYK
|
.SS pdfioContentSetFillColorDeviceCMYK
|
||||||
Set device CMYK fill color.
|
Set device CMYK fill color.
|
||||||
.PP
|
.PP
|
||||||
@ -1763,6 +1747,20 @@ bool pdfioContentTextEnd (
|
|||||||
pdfio_stream_t *st
|
pdfio_stream_t *st
|
||||||
);
|
);
|
||||||
.fi
|
.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
|
.SS pdfioContentTextMoveLine
|
||||||
Move to the next line and offset.
|
Move to the next line and offset.
|
||||||
.PP
|
.PP
|
||||||
@ -1783,14 +1781,49 @@ bool pdfioContentTextMoveTo (
|
|||||||
double ty
|
double ty
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
.SS pdfioContentTextNextLine
|
.SS pdfioContentTextNewLine
|
||||||
Move to the next line.
|
Move to the next line.
|
||||||
.PP
|
.PP
|
||||||
.nf
|
.nf
|
||||||
bool pdfioContentTextNextLine (
|
bool pdfioContentTextNewLine (
|
||||||
pdfio_stream_t *st
|
pdfio_stream_t *st
|
||||||
);
|
);
|
||||||
.fi
|
.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
|
.SS pdfioContentTextShow
|
||||||
Show text.
|
Show text.
|
||||||
.PP
|
.PP
|
||||||
@ -1950,6 +1983,32 @@ pdfio_valtype_t pdfioDictGetType (
|
|||||||
const char *key
|
const char *key
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
|
.SS pdfioDictIterateKeys
|
||||||
|
Iterate the keys in a dictionary.
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
void pdfioDictIterateKeys (
|
||||||
|
pdfio_dict_t *dict,
|
||||||
|
pdfio_dict_cb_t cb,
|
||||||
|
void *cb_data
|
||||||
|
);
|
||||||
|
.fi
|
||||||
|
.PP
|
||||||
|
This function iterates the keys in a dictionary, calling the supplied
|
||||||
|
function "cb":
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
bool
|
||||||
|
my_dict_cb(pdfio_dict_t *dict, const char *key, void *cb_data)
|
||||||
|
{
|
||||||
|
... "key" contains the dictionary key ...
|
||||||
|
... return true to continue or false to stop ...
|
||||||
|
}
|
||||||
|
|
||||||
|
.fi
|
||||||
|
|
||||||
|
The iteration continues as long as the callback returns \fBtrue\fR or all keys
|
||||||
|
have been iterated.
|
||||||
.SS pdfioDictSetArray
|
.SS pdfioDictSetArray
|
||||||
Set a key array in a dictionary.
|
Set a key array in a dictionary.
|
||||||
.PP
|
.PP
|
||||||
@ -2089,7 +2148,7 @@ pdfio_file_t * pdfioFileCreate (
|
|||||||
pdfio_rect_t *media_box,
|
pdfio_rect_t *media_box,
|
||||||
pdfio_rect_t *crop_box,
|
pdfio_rect_t *crop_box,
|
||||||
pdfio_error_cb_t error_cb,
|
pdfio_error_cb_t error_cb,
|
||||||
void *error_data
|
void *error_cbdata
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.PP
|
||||||
@ -2103,7 +2162,7 @@ The "media_box" and "crop_box" arguments specify the default MediaBox and
|
|||||||
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
|
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
|
||||||
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
||||||
.PP
|
.PP
|
||||||
The "error_cb" and "error_data" arguments specify an error handler callback
|
The "error_cb" and "error_cbdata" arguments specify an error handler callback
|
||||||
and its data pointer - if \fBNULL\fR the default error handler is used that
|
and its data pointer - if \fBNULL\fR the default error handler is used that
|
||||||
writes error messages to \fBstderr\fR.
|
writes error messages to \fBstderr\fR.
|
||||||
.SS pdfioFileCreateArrayObj
|
.SS pdfioFileCreateArrayObj
|
||||||
@ -2239,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
|
without interlacing or alpha. Transparency (masking) based on color/index
|
||||||
.IP 5
|
.IP 5
|
||||||
is supported.
|
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
|
.SS pdfioFileCreateObj
|
||||||
Create a new object in a PDF file.
|
Create a new object in a PDF file.
|
||||||
.PP
|
.PP
|
||||||
@ -2254,28 +2325,29 @@ Create a PDF file through an output callback.
|
|||||||
.nf
|
.nf
|
||||||
pdfio_file_t * pdfioFileCreateOutput (
|
pdfio_file_t * pdfioFileCreateOutput (
|
||||||
pdfio_output_cb_t output_cb,
|
pdfio_output_cb_t output_cb,
|
||||||
void *output_ctx,
|
void *output_cbdata,
|
||||||
const char *version,
|
const char *version,
|
||||||
pdfio_rect_t *media_box,
|
pdfio_rect_t *media_box,
|
||||||
pdfio_rect_t *crop_box,
|
pdfio_rect_t *crop_box,
|
||||||
pdfio_error_cb_t error_cb,
|
pdfio_error_cb_t error_cb,
|
||||||
void *error_data
|
void *error_cbdata
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.PP
|
||||||
This function creates a new PDF file that is streamed though an output
|
This function creates a new PDF file that is streamed though an output
|
||||||
callback. The "output_cb" and "output_ctx" arguments specify the output
|
callback. The "output_cb" and "output_cbdata" arguments specify the output
|
||||||
callback and its context pointer which is called whenever data needs to be
|
callback and its data pointer which is called whenever data needs to be
|
||||||
written:
|
written:
|
||||||
.PP
|
.PP
|
||||||
.nf
|
.nf
|
||||||
ssize_t
|
ssize_t
|
||||||
output_cb(void *output_ctx, const void *buffer, size_t bytes)
|
output_cb(void *output_cbdata, const void *buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
// Write buffer to output and return the number of bytes written
|
// Write buffer to output and return the number of bytes written
|
||||||
}
|
}
|
||||||
|
|
||||||
.fi
|
.fi
|
||||||
|
|
||||||
The "version" argument specifies the PDF version number for the file or
|
The "version" argument specifies the PDF version number for the file or
|
||||||
\fBNULL\fR for the default ("2.0").
|
\fBNULL\fR for the default ("2.0").
|
||||||
.PP
|
.PP
|
||||||
@ -2283,7 +2355,7 @@ The "media_box" and "crop_box" arguments specify the default MediaBox and
|
|||||||
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
|
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
|
||||||
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
||||||
.PP
|
.PP
|
||||||
The "error_cb" and "error_data" arguments specify an error handler callback
|
The "error_cb" and "error_cbdata" arguments specify an error handler callback
|
||||||
and its data pointer - if \fBNULL\fR the default error handler is used that
|
and its data pointer - if \fBNULL\fR the default error handler is used that
|
||||||
writes error messages to \fBstderr\fR.
|
writes error messages to \fBstderr\fR.
|
||||||
.PP
|
.PP
|
||||||
@ -2302,6 +2374,32 @@ pdfio_stream_t * pdfioFileCreatePage (
|
|||||||
pdfio_dict_t *dict
|
pdfio_dict_t *dict
|
||||||
);
|
);
|
||||||
.fi
|
.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
|
||||||
|
.nf
|
||||||
|
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_cbdata
|
||||||
|
);
|
||||||
|
.fi
|
||||||
.SS pdfioFileFindObj
|
.SS pdfioFileFindObj
|
||||||
Find an object using its object number.
|
Find an object using its object number.
|
||||||
.PP
|
.PP
|
||||||
@ -2322,6 +2420,16 @@ const char * pdfioFileGetAuthor (
|
|||||||
pdfio_file_t *pdf
|
pdfio_file_t *pdf
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
|
.SS pdfioFileGetCatalog
|
||||||
|
Get the document catalog dictionary.
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
pdfio_dict_t * pdfioFileGetCatalog (
|
||||||
|
pdfio_file_t *pdf
|
||||||
|
);
|
||||||
|
.fi
|
||||||
|
.PP
|
||||||
|
|
||||||
.SS pdfioFileGetCreationDate
|
.SS pdfioFileGetCreationDate
|
||||||
Get the creation date for a PDF file.
|
Get the creation date for a PDF file.
|
||||||
.PP
|
.PP
|
||||||
@ -2447,22 +2555,22 @@ Open a PDF file for reading.
|
|||||||
pdfio_file_t * pdfioFileOpen (
|
pdfio_file_t * pdfioFileOpen (
|
||||||
const char *filename,
|
const char *filename,
|
||||||
pdfio_password_cb_t password_cb,
|
pdfio_password_cb_t password_cb,
|
||||||
void *password_data,
|
void *password_cbdata,
|
||||||
pdfio_error_cb_t error_cb,
|
pdfio_error_cb_t error_cb,
|
||||||
void *error_data
|
void *error_cbdata
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.PP
|
||||||
This function opens an existing PDF file. The "filename" argument specifies
|
This function opens an existing PDF file. The "filename" argument specifies
|
||||||
the name of the PDF file to create.
|
the name of the PDF file to create.
|
||||||
.PP
|
.PP
|
||||||
The "password_cb" and "password_data" arguments specify a password callback
|
The "password_cb" and "password_cbdata" arguments specify a password callback
|
||||||
and its data pointer for PDF files that use one of the standard Adobe
|
and its data pointer for PDF files that use one of the standard Adobe
|
||||||
"security" handlers. The callback returns a password string or \fBNULL\fR to
|
"security" handlers. The callback returns a password string or \fBNULL\fR to
|
||||||
cancel the open. If \fBNULL\fR is specified for the callback function and the
|
cancel the open. If \fBNULL\fR is specified for the callback function and the
|
||||||
PDF file requires a password, the open will always fail.
|
PDF file requires a password, the open will always fail.
|
||||||
.PP
|
.PP
|
||||||
The "error_cb" and "error_data" arguments specify an error handler callback
|
The "error_cb" and "error_cbdata" arguments specify an error handler callback
|
||||||
and its data pointer - if \fBNULL\fR the default error handler is used that
|
and its data pointer - if \fBNULL\fR the default error handler is used that
|
||||||
writes error messages to \fBstderr\fR.
|
writes error messages to \fBstderr\fR.
|
||||||
.SS pdfioFileSetAuthor
|
.SS pdfioFileSetAuthor
|
||||||
@ -2569,7 +2677,7 @@ double pdfioImageGetWidth (
|
|||||||
.fi
|
.fi
|
||||||
.SS pdfioObjClose
|
.SS pdfioObjClose
|
||||||
Close an object, writing any data as needed to the PDF
|
Close an object, writing any data as needed to the PDF
|
||||||
file.
|
file.
|
||||||
.PP
|
.PP
|
||||||
.nf
|
.nf
|
||||||
bool pdfioObjClose (
|
bool pdfioObjClose (
|
||||||
@ -2755,6 +2863,13 @@ bool pdfioStreamGetToken (
|
|||||||
size_t bufsize
|
size_t bufsize
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
|
.PP
|
||||||
|
This function reads a single PDF token from a stream. Operator tokens,
|
||||||
|
boolean values, and numbers are returned as-is in the provided string buffer.
|
||||||
|
String values start with the opening parenthesis ('(') but have all escaping
|
||||||
|
resolved and the terminating parenthesis removed. Hexadecimal string values
|
||||||
|
start with the opening angle bracket ('<') and have all whitespace and the
|
||||||
|
terminating angle bracket removed.
|
||||||
.SS pdfioStreamPeek
|
.SS pdfioStreamPeek
|
||||||
Peek at data in a stream.
|
Peek at data in a stream.
|
||||||
.PP
|
.PP
|
||||||
@ -2875,6 +2990,12 @@ Standard color spaces
|
|||||||
.nf
|
.nf
|
||||||
typedef enum pdfio_cs_e pdfio_cs_t;
|
typedef enum pdfio_cs_e pdfio_cs_t;
|
||||||
.fi
|
.fi
|
||||||
|
.SS pdfio_dict_cb_t
|
||||||
|
Dictionary iterator callback
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
typedef bool(*)(pdfio_dict_t *dict, const char *key, void *cb_data) pdfio_dict_cb_t;
|
||||||
|
.fi
|
||||||
.SS pdfio_dict_t
|
.SS pdfio_dict_t
|
||||||
Key/value dictionary
|
Key/value dictionary
|
||||||
.PP
|
.PP
|
||||||
@ -2971,15 +3092,9 @@ PDF value types
|
|||||||
.nf
|
.nf
|
||||||
typedef enum pdfio_valtype_e pdfio_valtype_t;
|
typedef enum pdfio_valtype_e pdfio_valtype_t;
|
||||||
.fi
|
.fi
|
||||||
.SS state_t[4][4]
|
|
||||||
4x4 AES state table
|
|
||||||
.PP
|
|
||||||
.nf
|
|
||||||
typedef uint8_t state_t[4][4];
|
|
||||||
.fi
|
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
.PP
|
.PP
|
||||||
Michael R Sweet
|
Michael R Sweet
|
||||||
.SH COPYRIGHT
|
.SH COPYRIGHT
|
||||||
.PP
|
.PP
|
||||||
Copyright (c) 2021-2022 by Michael R Sweet
|
Copyright (c) 2021-2024 by Michael R Sweet
|
||||||
|
647
doc/pdfio.html
647
doc/pdfio.html
File diff suppressed because it is too large
Load Diff
92
doc/pdfio.md
92
doc/pdfio.md
@ -15,7 +15,7 @@ goals of pdfio are:
|
|||||||
PDFio is *not* concerned with rendering or viewing a PDF file, although a PDF
|
PDFio is *not* concerned with rendering or viewing a PDF file, although a PDF
|
||||||
RIP or viewer could be written using it.
|
RIP or viewer could be written using it.
|
||||||
|
|
||||||
PDFio is Copyright © 2021-2022 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
|
Apache License Version 2.0 with an (optional) exception to allow linking against
|
||||||
GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
|
GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ PDFio requires the following to build the software:
|
|||||||
|
|
||||||
- A C99 compiler such as Clang, GCC, or MS Visual C
|
- A C99 compiler such as Clang, GCC, or MS Visual C
|
||||||
- A POSIX-compliant `make` program
|
- A POSIX-compliant `make` program
|
||||||
|
- A POSIX-compliant `sh` program
|
||||||
- ZLIB (<https://www.zlib.net>) 1.0 or higher
|
- ZLIB (<https://www.zlib.net>) 1.0 or higher
|
||||||
|
|
||||||
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
|
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
|
||||||
@ -35,10 +36,11 @@ IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
|
|||||||
Installing pdfio
|
Installing pdfio
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
PDFio comes with a portable makefile that will work on any POSIX-compliant
|
PDFio comes with a configure script that creates a portable makefile that will
|
||||||
system with ZLIB installed. To make it, run:
|
work on any POSIX-compliant system with ZLIB installed. To make it, run:
|
||||||
|
|
||||||
make all
|
./configure
|
||||||
|
make
|
||||||
|
|
||||||
To test it, run:
|
To test it, run:
|
||||||
|
|
||||||
@ -46,45 +48,22 @@ To test it, run:
|
|||||||
|
|
||||||
To install it, run:
|
To install it, run:
|
||||||
|
|
||||||
make install
|
sudo make install
|
||||||
|
|
||||||
If you want a shared library, run:
|
If you want a shared library, run:
|
||||||
|
|
||||||
make all-shared
|
./configure --enable-shared
|
||||||
make install-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:
|
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
|
Other configure options can be found using the `--help` option:
|
||||||
"${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
|
./configure --help
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
Visual Studio Project
|
Visual Studio Project
|
||||||
@ -102,10 +81,6 @@ generates a static library that will be installed under "/usr/local" with:
|
|||||||
|
|
||||||
sudo xcodebuild install
|
sudo xcodebuild install
|
||||||
|
|
||||||
You can reproduce this with the makefile using:
|
|
||||||
|
|
||||||
sudo make macos install
|
|
||||||
|
|
||||||
|
|
||||||
Detecting PDFio
|
Detecting PDFio
|
||||||
---------------
|
---------------
|
||||||
@ -163,15 +138,32 @@ Reading PDF Files
|
|||||||
You open an existing PDF file using the [`pdfioFileOpen`](@@) function:
|
You open an existing PDF file using the [`pdfioFileOpen`](@@) function:
|
||||||
|
|
||||||
```c
|
```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"),
|
where the five arguments to the function are the filename ("myinputfile.pdf"),
|
||||||
an optional error callback function (`error_cb`), and an optional pointer value
|
an optional password callback function (`password_cb`) and data pointer value
|
||||||
for the error callback function (`error_data`). The error callback is called
|
(`password_data`), and an optional error callback function (`error_cb`) and data
|
||||||
for both errors and warnings and accepts the `pdfio_file_t` pointer, a message
|
pointer value (`error_data`). The password callback is called for encrypted PDF
|
||||||
string, and the callback pointer value, for example:
|
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
|
```c
|
||||||
bool
|
bool
|
||||||
@ -361,7 +353,7 @@ PDF Content Helper Functions
|
|||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
PDFio includes many helper functions for embedding or writing specific kinds of
|
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:
|
categories:
|
||||||
|
|
||||||
- [Color Space Functions](@)
|
- [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.
|
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
|
### Image Object Functions
|
||||||
@ -584,8 +576,12 @@ escaping, as needed:
|
|||||||
- [`pdfioContentTextMoveLine`](@@) moves to the next line with an offset in a
|
- [`pdfioContentTextMoveLine`](@@) moves to the next line with an offset in a
|
||||||
text block
|
text block
|
||||||
- [`pdfioContentTextMoveTo`](@@) moves within the current line 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
|
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
|
- [`pdfioContentTextShow`](@@) draws a literal string in a text block
|
||||||
- [`pdfioContentTextShowf`](@@) draws a formatted string in a text block
|
- [`pdfioContentTextShowf`](@@) draws a formatted string in a text block
|
||||||
- [`pdfioContentTextShowJustified`](@@) draws an array of literal strings with
|
- [`pdfioContentTextShowJustified`](@@) draws an array of literal strings with
|
||||||
|
232
install-sh
Executable file
232
install-sh
Executable 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
|
53
makesrcdist
53
makesrcdist
@ -2,18 +2,61 @@
|
|||||||
#
|
#
|
||||||
# makesrcdist - make a source distribution of pdfio.
|
# makesrcdist - make a source distribution of pdfio.
|
||||||
#
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# ./makesrcdist [--snapshot] VERSION
|
||||||
|
#
|
||||||
|
|
||||||
|
# Support "--snapshot" option...
|
||||||
|
if test "$1" == "--snapshot"; then
|
||||||
|
shift
|
||||||
|
snapshot=1
|
||||||
|
else
|
||||||
|
snapshot=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get version...
|
||||||
if test $# != 1; then
|
if test $# != 1; then
|
||||||
echo "Usage: ./makesrcdist version"
|
echo "Usage: ./makesrcdist [--snapshot] VERSION"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
version=$1
|
version=$1
|
||||||
|
|
||||||
echo Creating tag for release...
|
# Check that version number has been updated everywhere...
|
||||||
git tag -m "Tag $version" v$version
|
if test $(grep AC_INIT configure.ac | awk '{print $2}') != "[$version],"; then
|
||||||
git push origin v$version
|
echo "Still need to update AC_INIT version in 'configure.ac'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $(grep PDFIO_VERSION= configure | awk -F \" '{print $2}') != "$version"; then
|
||||||
|
echo "Still need to run 'autoconf -f'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $(grep '<version>' pdfio_native.nuspec | sed -E -e '1,$s/^.*<version>([0-9.]+).*$/\1/') != "$version"; then
|
||||||
|
echo "Still need to update version in 'pdfio_native.nuspec'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $(grep '<version>' pdfio_native.redist.nuspec | sed -E -e '1,$s/^.*<version>([0-9.]+).*$/\1/') != "$version"; then
|
||||||
|
echo "Still need to update version in 'pdfio_native.redist.nuspec'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $(grep PDFIO_VERSION pdfio.h | awk -F \" '{print $2}') != "$version"; then
|
||||||
|
echo "Still need to update PDFIO_VERSION in 'pdfio.h'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tag release...
|
||||||
|
if test $snapshot = 0; then
|
||||||
|
echo Creating tag for release...
|
||||||
|
git tag -m "Tag $version" v$version
|
||||||
|
git push origin v$version
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make source archives...
|
||||||
echo Creating pdfio-$version.tar.gz...
|
echo Creating pdfio-$version.tar.gz...
|
||||||
git archive --format tar --prefix=pdfio-$version/ HEAD | gzip -v9 >pdfio-$version.tar.gz
|
git archive --format tar --prefix=pdfio-$version/ HEAD | gzip -v9 >pdfio-$version.tar.gz
|
||||||
gpg --detach-sign pdfio-$version.tar.gz
|
gpg --detach-sign pdfio-$version.tar.gz
|
||||||
|
@ -10,10 +10,6 @@
|
|||||||
// (<https://github.com/kokke/tiny-AES-c>)
|
// (<https://github.com/kokke/tiny-AES-c>)
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#include "pdfio-private.h"
|
||||||
|
|
||||||
|
|
||||||
@ -21,7 +17,7 @@
|
|||||||
// Local types...
|
// 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@
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -7,10 +7,6 @@
|
|||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#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.
|
// '_pdfioArrayDebug()' - Print the contents of an array.
|
||||||
//
|
//
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
//
|
//
|
||||||
// Common support functions for pdfio.
|
// Common support functions for pdfio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021 by Michael R Sweet.
|
// Copyright © 2021-2024 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#include "pdfio-private.h"
|
||||||
|
|
||||||
|
|
||||||
@ -38,6 +34,8 @@ _pdfioFileConsume(pdfio_file_t *pdf, // I - PDF file
|
|||||||
else if (_pdfioFileSeek(pdf, (off_t)bytes, SEEK_CUR) < 0)
|
else if (_pdfioFileSeek(pdf, (off_t)bytes, SEEK_CUR) < 0)
|
||||||
return (false);
|
return (false);
|
||||||
|
|
||||||
|
PDFIO_DEBUG("_pdfioFileConsume: pos=%ld\n", (long)(pdf->bufpos + pdf->bufptr - pdf->buffer));
|
||||||
|
|
||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +141,7 @@ _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
|
|||||||
*bufend = buffer + bufsize - 1; // Pointer to end of buffer
|
*bufend = buffer + bufsize - 1; // Pointer to end of buffer
|
||||||
|
|
||||||
|
|
||||||
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p\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)
|
while (!eol)
|
||||||
{
|
{
|
||||||
@ -263,7 +261,7 @@ _pdfioFilePrintf(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
// Format the string...
|
// Format the string...
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
vsnprintf(buffer, sizeof(buffer), format, ap);
|
_pdfio_vsnprintf(pdf, buffer, sizeof(buffer), format, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
// Write it...
|
// Write it...
|
||||||
@ -358,12 +356,12 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
|
|||||||
off_t offset, // I - Offset
|
off_t offset, // I - Offset
|
||||||
int whence) // I - Offset base
|
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...
|
// Adjust offset for relative seeks...
|
||||||
if (whence == SEEK_CUR)
|
if (whence == SEEK_CUR)
|
||||||
{
|
{
|
||||||
offset += pdf->bufpos;
|
offset += pdf->bufpos + (pdf->bufptr - pdf->buffer);
|
||||||
whence = SEEK_SET;
|
whence = SEEK_SET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,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))
|
if (whence != SEEK_END && offset >= pdf->bufpos && offset < (pdf->bufpos + pdf->bufend - pdf->buffer))
|
||||||
{
|
{
|
||||||
// Yes, seek within existing buffer...
|
// Yes, seek within existing buffer...
|
||||||
pdf->bufptr = pdf->buffer + offset - pdf->bufpos;
|
pdf->bufptr = pdf->buffer + (offset - pdf->bufpos);
|
||||||
PDFIO_DEBUG("_pdfioFileSeek: Seek within buffer, bufpos=%ld.\n", (long)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);
|
return (offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +404,7 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
|
|||||||
return (-1);
|
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);
|
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p, bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufend);
|
||||||
|
|
||||||
pdf->bufpos = offset;
|
pdf->bufpos = offset;
|
||||||
@ -525,7 +523,7 @@ read_buffer(pdfio_file_t *pdf, // I - PDF file
|
|||||||
return (rbytes);
|
return (rbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 'write_buffer()' - Write a buffer to a PDF file.
|
// 'write_buffer()' - Write a buffer to a PDF file.
|
||||||
//
|
//
|
||||||
|
480
pdfio-content.c
480
pdfio-content.c
@ -1,16 +1,12 @@
|
|||||||
//
|
//
|
||||||
// Content helper functions for PDFio.
|
// Content helper 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
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#include "pdfio-private.h"
|
||||||
#include "pdfio-content.h"
|
#include "pdfio-content.h"
|
||||||
#include "ttf.h"
|
#include "ttf.h"
|
||||||
@ -489,8 +485,11 @@ pdfioContentMatrixRotate(
|
|||||||
pdfio_stream_t *st, // I - Stream
|
pdfio_stream_t *st, // I - Stream
|
||||||
double degrees) // I - Rotation angle in degrees counter-clockwise
|
double degrees) // I - Rotation angle in degrees counter-clockwise
|
||||||
{
|
{
|
||||||
double dcos = cos(degrees / M_PI); // Cosine
|
double dcos = cos(M_PI * degrees / 180.0);
|
||||||
double dsin = sin(degrees / M_PI); // Sine
|
// Cosine
|
||||||
|
double dsin = sin(M_PI * degrees / 180.0);
|
||||||
|
// Sine
|
||||||
|
|
||||||
|
|
||||||
return (pdfioStreamPrintf(st, "%g %g %g %g 0 0 cm\n", dcos, -dsin, dsin, dcos));
|
return (pdfioStreamPrintf(st, "%g %g %g %g 0 0 cm\n", dcos, -dsin, dsin, dcos));
|
||||||
}
|
}
|
||||||
@ -586,6 +585,17 @@ pdfioContentPathCurve23(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'pdfioContentPathEnd()' - Clear the current path.
|
||||||
|
//
|
||||||
|
|
||||||
|
bool // O - `true` on success, `false` on failure
|
||||||
|
pdfioContentPathEnd(pdfio_stream_t *st) // I - Stream
|
||||||
|
{
|
||||||
|
return (pdfioStreamPuts(st, "n\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 'pdfioContentPathLineTo()' - Add a straight line to the current path.
|
// 'pdfioContentPathLineTo()' - Add a straight line to the current path.
|
||||||
//
|
//
|
||||||
@ -656,6 +666,9 @@ pdfioContentSave(pdfio_stream_t *st) // I - Stream
|
|||||||
//
|
//
|
||||||
// 'pdfioContentSetDashPattern()' - Set the stroke pattern.
|
// 'pdfioContentSetDashPattern()' - Set the stroke pattern.
|
||||||
//
|
//
|
||||||
|
// This function sets the stroke pattern when drawing lines. If "on" and "off"
|
||||||
|
// are 0, a solid line is drawn.
|
||||||
|
//
|
||||||
|
|
||||||
bool // O - `true` on success, `false` on failure
|
bool // O - `true` on success, `false` on failure
|
||||||
pdfioContentSetDashPattern(
|
pdfioContentSetDashPattern(
|
||||||
@ -664,7 +677,12 @@ pdfioContentSetDashPattern(
|
|||||||
double on, // I - On length
|
double on, // I - On length
|
||||||
double off) // I - Off length
|
double off) // I - Off length
|
||||||
{
|
{
|
||||||
return (pdfioStreamPrintf(st, "[%g %g] %g d\n", on, off, phase));
|
if (on <= 0.0 && off <= 0.0)
|
||||||
|
return (pdfioStreamPrintf(st, "[] %g d\n", phase));
|
||||||
|
else if (fabs(on - off) < 0.001)
|
||||||
|
return (pdfioStreamPrintf(st, "[%g] %g d\n", on, phase));
|
||||||
|
else
|
||||||
|
return (pdfioStreamPrintf(st, "[%g %g] %g d\n", on, off, phase));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1042,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.
|
// 'pdfioContentTextMoveLine()' - Move to the next line and offset.
|
||||||
//
|
//
|
||||||
@ -1071,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
|
bool // O - `true` on success, `false` on failure
|
||||||
@ -1082,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.
|
// 'pdfioContentTextShow()' - Show text.
|
||||||
//
|
//
|
||||||
@ -1114,9 +1324,9 @@ pdfioContentTextShow(
|
|||||||
//
|
//
|
||||||
// 'pdfioContentTextShowf()' - Show formatted text.
|
// 'pdfioContentTextShowf()' - Show formatted text.
|
||||||
//
|
//
|
||||||
// This function shows some text in a PDF content stream. The "unicode" argument
|
// This function shows some formatted text in a PDF content stream. The
|
||||||
// specifies that the current font maps to full Unicode. The "format" argument
|
// "unicode" argument specifies that the current font maps to full Unicode.
|
||||||
// specifies a UTF-8 encoded `printf`-style format string.
|
// The "format" argument specifies a UTF-8 encoded `printf`-style format string.
|
||||||
//
|
//
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@ -1267,13 +1477,13 @@ pdfioFileCreateFontObjFromFile(
|
|||||||
pdfio_dict_t *dict, // Font dictionary
|
pdfio_dict_t *dict, // Font dictionary
|
||||||
*desc, // Font descriptor
|
*desc, // Font descriptor
|
||||||
*file; // Font file dictionary
|
*file; // Font file dictionary
|
||||||
pdfio_obj_t *obj, // Font object
|
pdfio_obj_t *obj = NULL, // Font object
|
||||||
*desc_obj, // Font descriptor object
|
*desc_obj, // Font descriptor object
|
||||||
*file_obj; // Font file object
|
*file_obj; // Font file object
|
||||||
const char *basefont; // Base font name
|
const char *basefont; // Base font name
|
||||||
pdfio_array_t *bbox; // Font bounding box array
|
pdfio_array_t *bbox; // Font bounding box array
|
||||||
pdfio_stream_t *st; // Font stream
|
pdfio_stream_t *st; // Font stream
|
||||||
int fd; // File
|
int fd = -1; // File
|
||||||
unsigned char buffer[16384]; // Read buffer
|
unsigned char buffer[16384]; // Read buffer
|
||||||
ssize_t bytes; // Bytes read
|
ssize_t bytes; // Bytes read
|
||||||
|
|
||||||
@ -1302,48 +1512,32 @@ pdfioFileCreateFontObjFromFile(
|
|||||||
|
|
||||||
// Create the font file dictionary and object...
|
// Create the font file dictionary and object...
|
||||||
if ((file = pdfioDictCreate(pdf)) == NULL)
|
if ((file = pdfioDictCreate(pdf)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
close(fd);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
pdfioDictSetName(file, "Filter", "FlateDecode");
|
pdfioDictSetName(file, "Filter", "FlateDecode");
|
||||||
|
|
||||||
if ((file_obj = pdfioFileCreateObj(pdf, file)) == NULL)
|
if ((file_obj = pdfioFileCreateObj(pdf, file)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
close(fd);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((st = pdfioObjCreateStream(file_obj, PDFIO_FILTER_FLATE)) == NULL)
|
if ((st = pdfioObjCreateStream(file_obj, PDFIO_FILTER_FLATE)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
close(fd);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
|
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
|
||||||
{
|
{
|
||||||
if (!pdfioStreamWrite(st, buffer, (size_t)bytes))
|
if (!pdfioStreamWrite(st, buffer, (size_t)bytes))
|
||||||
{
|
{
|
||||||
ttfDelete(font);
|
|
||||||
close(fd);
|
|
||||||
pdfioStreamClose(st);
|
pdfioStreamClose(st);
|
||||||
return (NULL);
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
pdfioStreamClose(st);
|
pdfioStreamClose(st);
|
||||||
|
|
||||||
// Create the font descriptor dictionary and object...
|
// Create the font descriptor dictionary and object...
|
||||||
if ((bbox = pdfioArrayCreate(pdf)) == NULL)
|
if ((bbox = pdfioArrayCreate(pdf)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
ttfGetBounds(font, &bounds);
|
ttfGetBounds(font, &bounds);
|
||||||
|
|
||||||
@ -1353,10 +1547,7 @@ pdfioFileCreateFontObjFromFile(
|
|||||||
pdfioArrayAppendNumber(bbox, bounds.top);
|
pdfioArrayAppendNumber(bbox, bounds.top);
|
||||||
|
|
||||||
if ((desc = pdfioDictCreate(pdf)) == NULL)
|
if ((desc = pdfioDictCreate(pdf)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
basefont = pdfioStringCreate(pdf, ttfGetPostScriptName(font));
|
basefont = pdfioStringCreate(pdf, ttfGetPostScriptName(font));
|
||||||
|
|
||||||
@ -1375,59 +1566,70 @@ pdfioFileCreateFontObjFromFile(
|
|||||||
pdfioDictSetNumber(desc, "StemV", ttfGetWeight(font) / 4 + 25);
|
pdfioDictSetNumber(desc, "StemV", ttfGetWeight(font) / 4 + 25);
|
||||||
|
|
||||||
if ((desc_obj = pdfioFileCreateObj(pdf, desc)) == NULL)
|
if ((desc_obj = pdfioFileCreateObj(pdf, desc)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
pdfioObjClose(desc_obj);
|
pdfioObjClose(desc_obj);
|
||||||
|
|
||||||
if (unicode)
|
if (unicode)
|
||||||
{
|
{
|
||||||
// Unicode (CID) font...
|
// Unicode (CID) font...
|
||||||
pdfio_dict_t *cid2gid; // CIDToGIDMap dictionary
|
pdfio_dict_t *cid2gid, // CIDToGIDMap dictionary
|
||||||
pdfio_obj_t *cid2gid_obj; // CIDToGIDMap object
|
*to_unicode; // ToUnicode dictionary
|
||||||
|
pdfio_obj_t *cid2gid_obj, // CIDToGIDMap object
|
||||||
|
*to_unicode_obj;// ToUnicode object
|
||||||
size_t i, // Looping var
|
size_t i, // Looping var
|
||||||
|
start, // Start character
|
||||||
num_cmap; // Number of CMap entries
|
num_cmap; // Number of CMap entries
|
||||||
const int *cmap; // 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
|
unsigned char *bufptr, // Pointer into buffer
|
||||||
*bufend; // End of buffer
|
*bufend; // End of buffer
|
||||||
pdfio_dict_t *type2; // CIDFontType2 font dictionary
|
pdfio_dict_t *type2; // CIDFontType2 font dictionary
|
||||||
pdfio_obj_t *type2_obj; // CIDFontType2 font object
|
pdfio_obj_t *type2_obj; // CIDFontType2 font object
|
||||||
pdfio_array_t *descendants; // Decendant font list
|
pdfio_array_t *descendants; // Decendant font list
|
||||||
pdfio_dict_t *sidict; // CIDSystemInfo dictionary
|
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...
|
// Create a CIDToGIDMap object for the Unicode font...
|
||||||
if ((cid2gid = pdfioDictCreate(pdf)) == NULL)
|
if ((cid2gid = pdfioDictCreate(pdf)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef DEBUG
|
#ifndef DEBUG
|
||||||
pdfioDictSetName(cid2gid, "Filter", "FlateDecode");
|
pdfioDictSetName(cid2gid, "Filter", "FlateDecode");
|
||||||
#endif // !DEBUG
|
#endif // !DEBUG
|
||||||
|
|
||||||
if ((cid2gid_obj = pdfioFileCreateObj(pdf, cid2gid)) == NULL)
|
if ((cid2gid_obj = pdfioFileCreateObj(pdf, cid2gid)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_NONE)) == NULL)
|
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_NONE)) == NULL)
|
||||||
#else
|
#else
|
||||||
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_FLATE)) == NULL)
|
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_FLATE)) == NULL)
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
for (i = 0, bufptr = buffer, bufend = buffer + sizeof(buffer); i < num_cmap; i ++)
|
for (i = 0, bufptr = buffer, bufend = buffer + sizeof(buffer); i < num_cmap; i ++)
|
||||||
{
|
{
|
||||||
|
PDFIO_DEBUG("pdfioFileCreateFontObjFromFile: cmap[%u]=%d\n", (unsigned)i, cmap[i]);
|
||||||
if (cmap[i] < 0)
|
if (cmap[i] < 0)
|
||||||
{
|
{
|
||||||
// Map undefined glyph to .notdef...
|
// Map undefined glyph to .notdef...
|
||||||
@ -1439,6 +1641,12 @@ pdfioFileCreateFontObjFromFile(
|
|||||||
// Map to specified glyph...
|
// Map to specified glyph...
|
||||||
*bufptr++ = (unsigned char)(cmap[i] >> 8);
|
*bufptr++ = (unsigned char)(cmap[i] >> 8);
|
||||||
*bufptr++ = (unsigned char)(cmap[i] & 255);
|
*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)
|
if (bufptr >= bufend)
|
||||||
@ -1447,8 +1655,7 @@ pdfioFileCreateFontObjFromFile(
|
|||||||
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
|
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
|
||||||
{
|
{
|
||||||
pdfioStreamClose(st);
|
pdfioStreamClose(st);
|
||||||
ttfDelete(font);
|
goto done;
|
||||||
return (NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bufptr = buffer;
|
bufptr = buffer;
|
||||||
@ -1461,30 +1668,102 @@ pdfioFileCreateFontObjFromFile(
|
|||||||
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
|
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
|
||||||
{
|
{
|
||||||
pdfioStreamClose(st);
|
pdfioStreamClose(st);
|
||||||
ttfDelete(font);
|
goto done;
|
||||||
return (NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pdfioStreamClose(st);
|
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...
|
// Create a CIDFontType2 dictionary for the Unicode font...
|
||||||
if ((type2 = pdfioDictCreate(pdf)) == NULL)
|
if ((type2 = pdfioDictCreate(pdf)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((sidict = pdfioDictCreate(pdf)) == NULL)
|
// Width array
|
||||||
{
|
if ((w_array = pdfioArrayCreate(pdf)) == NULL)
|
||||||
ttfDelete(font);
|
goto done;
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CIDSystemInfo mapping to Adobe UCS2 v0 (Unicode)
|
for (start = 0, w0 = ttfGetWidth(font, 0), w1 = 0, i = 1; i < 65536; start = i, w0 = w1, i ++)
|
||||||
pdfioDictSetString(sidict, "Registry", "Adobe");
|
{
|
||||||
pdfioDictSetString(sidict, "Ordering", "Identity");
|
while (i < 65536 && (w1 = ttfGetWidth(font, (int)i)) == w0)
|
||||||
pdfioDictSetNumber(sidict, "Supplement", 0);
|
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...
|
// Then the dictionary for the CID base font...
|
||||||
pdfioDictSetName(type2, "Type", "Font");
|
pdfioDictSetName(type2, "Type", "Font");
|
||||||
@ -1493,56 +1772,41 @@ pdfioFileCreateFontObjFromFile(
|
|||||||
pdfioDictSetDict(type2, "CIDSystemInfo", sidict);
|
pdfioDictSetDict(type2, "CIDSystemInfo", sidict);
|
||||||
pdfioDictSetObj(type2, "CIDToGIDMap", cid2gid_obj);
|
pdfioDictSetObj(type2, "CIDToGIDMap", cid2gid_obj);
|
||||||
pdfioDictSetObj(type2, "FontDescriptor", desc_obj);
|
pdfioDictSetObj(type2, "FontDescriptor", desc_obj);
|
||||||
|
pdfioDictSetArray(type2, "W", w_array);
|
||||||
|
|
||||||
if ((type2_obj = pdfioFileCreateObj(pdf, type2)) == NULL)
|
if ((type2_obj = pdfioFileCreateObj(pdf, type2)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
pdfioObjClose(type2_obj);
|
pdfioObjClose(type2_obj);
|
||||||
|
|
||||||
// Create a Type 0 font object...
|
// Create a Type 0 font object...
|
||||||
if ((descendants = pdfioArrayCreate(pdf)) == NULL)
|
if ((descendants = pdfioArrayCreate(pdf)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
pdfioArrayAppendObj(descendants, type2_obj);
|
pdfioArrayAppendObj(descendants, type2_obj);
|
||||||
|
|
||||||
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
pdfioDictSetName(dict, "Type", "Font");
|
pdfioDictSetName(dict, "Type", "Font");
|
||||||
pdfioDictSetName(dict, "Subtype", "Type0");
|
pdfioDictSetName(dict, "Subtype", "Type0");
|
||||||
pdfioDictSetName(dict, "BaseFont", basefont);
|
pdfioDictSetName(dict, "BaseFont", basefont);
|
||||||
pdfioDictSetArray(dict, "DescendantFonts", descendants);
|
pdfioDictSetArray(dict, "DescendantFonts", descendants);
|
||||||
pdfioDictSetName(dict, "Encoding", "Identity-H");
|
pdfioDictSetName(dict, "Encoding", "Identity-H");
|
||||||
|
pdfioDictSetObj(dict, "ToUnicode", to_unicode_obj);
|
||||||
|
|
||||||
if ((obj = pdfioFileCreateObj(pdf, dict)) == NULL)
|
if ((obj = pdfioFileCreateObj(pdf, dict)) != NULL)
|
||||||
return (NULL);
|
pdfioObjClose(obj);
|
||||||
|
|
||||||
pdfioObjClose(obj);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Simple (CP1282 or custom encoding) 8-bit font...
|
// Simple (CP1282 or custom encoding) 8-bit font...
|
||||||
if (ttfGetMaxChar(font) >= 255 && !pdf->cp1252_obj && !create_cp1252(pdf))
|
if (ttfGetMaxChar(font) >= 255 && !pdf->cp1252_obj && !create_cp1252(pdf))
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a TrueType font object...
|
// Create a TrueType font object...
|
||||||
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
||||||
{
|
goto done;
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
pdfioDictSetName(dict, "Type", "Font");
|
pdfioDictSetName(dict, "Type", "Font");
|
||||||
pdfioDictSetName(dict, "Subtype", "TrueType");
|
pdfioDictSetName(dict, "Subtype", "TrueType");
|
||||||
@ -1552,16 +1816,16 @@ pdfioFileCreateFontObjFromFile(
|
|||||||
|
|
||||||
pdfioDictSetObj(dict, "FontDescriptor", desc_obj);
|
pdfioDictSetObj(dict, "FontDescriptor", desc_obj);
|
||||||
|
|
||||||
if ((obj = pdfioFileCreateObj(pdf, dict)) == NULL)
|
if ((obj = pdfioFileCreateObj(pdf, dict)) != NULL)
|
||||||
{
|
pdfioObjClose(obj);
|
||||||
ttfDelete(font);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
pdfioObjClose(obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ttfDelete(font);
|
done:
|
||||||
|
|
||||||
|
if (fd >= 0)
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
_pdfioObjSetExtension(obj, font, (_pdfio_extfree_t)ttfDelete);
|
||||||
|
|
||||||
return (obj);
|
return (obj);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// Public content header file for PDFio.
|
// Public content header file 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
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
@ -9,18 +9,7 @@
|
|||||||
|
|
||||||
#ifndef PDFIO_CONTENT_H
|
#ifndef PDFIO_CONTENT_H
|
||||||
# define PDFIO_CONTENT_H
|
# define PDFIO_CONTENT_H
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
# include "pdfio.h"
|
# include "pdfio.h"
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// C++ magic...
|
|
||||||
//
|
|
||||||
|
|
||||||
# ifdef __cplusplus
|
# ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
# endif // __cplusplus
|
# endif // __cplusplus
|
||||||
@ -91,6 +80,7 @@ extern bool pdfioContentPathClose(pdfio_stream_t *st) _PDFIO_PUBLIC;
|
|||||||
extern bool pdfioContentPathCurve(pdfio_stream_t *st, double x1, double y1, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC;
|
extern bool pdfioContentPathCurve(pdfio_stream_t *st, double x1, double y1, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentPathCurve13(pdfio_stream_t *st, double x1, double y1, double x3, double y3) _PDFIO_PUBLIC;
|
extern bool pdfioContentPathCurve13(pdfio_stream_t *st, double x1, double y1, double x3, double y3) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentPathCurve23(pdfio_stream_t *st, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC;
|
extern bool pdfioContentPathCurve23(pdfio_stream_t *st, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC;
|
||||||
|
extern bool pdfioContentPathEnd(pdfio_stream_t *st) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentPathLineTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC;
|
extern bool pdfioContentPathLineTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentPathMoveTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC;
|
extern bool pdfioContentPathMoveTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentPathRect(pdfio_stream_t *st, double x, double y, double width, double height) _PDFIO_PUBLIC;
|
extern bool pdfioContentPathRect(pdfio_stream_t *st, double x, double y, double width, double height) _PDFIO_PUBLIC;
|
||||||
@ -125,8 +115,12 @@ extern bool pdfioContentSetTextXScaling(pdfio_stream_t *st, double percent) _PD
|
|||||||
extern bool pdfioContentStroke(pdfio_stream_t *st) _PDFIO_PUBLIC;
|
extern bool pdfioContentStroke(pdfio_stream_t *st) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentTextBegin(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 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 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 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 pdfioContentTextNextLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentTextShow(pdfio_stream_t *st, bool unicode, const char *s) _PDFIO_PUBLIC;
|
extern bool pdfioContentTextShow(pdfio_stream_t *st, bool unicode, const char *s) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4);
|
extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4);
|
||||||
@ -150,10 +144,6 @@ extern bool pdfioPageDictAddFont(pdfio_dict_t *dict, const char *name, pdfio_ob
|
|||||||
extern bool pdfioPageDictAddImage(pdfio_dict_t *dict, const char *name, pdfio_obj_t *obj) _PDFIO_PUBLIC;
|
extern bool pdfioPageDictAddImage(pdfio_dict_t *dict, const char *name, pdfio_obj_t *obj) _PDFIO_PUBLIC;
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// C++ magic...
|
|
||||||
//
|
|
||||||
|
|
||||||
# ifdef __cplusplus
|
# ifdef __cplusplus
|
||||||
}
|
}
|
||||||
# endif // __cplusplus
|
# endif // __cplusplus
|
||||||
|
107
pdfio-crypto.c
107
pdfio-crypto.c
@ -1,16 +1,12 @@
|
|||||||
//
|
//
|
||||||
// Cryptographic support functions for PDFio.
|
// 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
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#include "pdfio-private.h"
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
@ -403,16 +399,23 @@ _pdfioCryptoMakeRandom(uint8_t *buffer, // I - Buffer
|
|||||||
//
|
//
|
||||||
|
|
||||||
_pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
_pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
||||||
_pdfioCryptoMakeReader(
|
_pdfioCryptoMakeReader(
|
||||||
pdfio_file_t *pdf, // I - PDF file
|
pdfio_file_t *pdf, // I - PDF file
|
||||||
pdfio_obj_t *obj, // I - PDF object
|
pdfio_obj_t *obj, // I - PDF object
|
||||||
_pdfio_crypto_ctx_t *ctx, // I - Pointer to crypto context
|
_pdfio_crypto_ctx_t *ctx, // I - Pointer to crypto context
|
||||||
uint8_t *iv, // I - Buffer for initialization vector
|
uint8_t *iv, // I - Buffer for initialization vector
|
||||||
size_t *ivlen) // IO - Size of initialization vector
|
size_t *ivlen) // IO - Size of initialization vector
|
||||||
{
|
{
|
||||||
uint8_t data[21]; /* Key data */
|
uint8_t data[21]; // Key data
|
||||||
_pdfio_md5_t md5; /* MD5 state */
|
_pdfio_md5_t md5; // MD5 state
|
||||||
uint8_t digest[16]; /* MD5 digest value */
|
uint8_t digest[16]; // MD5 digest value
|
||||||
|
#if PDFIO_OBJ_CRYPT
|
||||||
|
pdfio_array_t *id_array; // Object ID array
|
||||||
|
unsigned char *id_value; // Object ID value
|
||||||
|
size_t id_len; // Length of object ID
|
||||||
|
uint8_t temp_key[16]; // File key for object
|
||||||
|
#endif // PDFIO_OBJ_CRYPT
|
||||||
|
uint8_t *file_key; // Computed file key to use
|
||||||
|
|
||||||
|
|
||||||
PDFIO_DEBUG("_pdfioCryptoMakeReader(pdf=%p, obj=%p(%d), ctx=%p, iv=%p, ivlen=%p(%d))\n", pdf, obj, (int)obj->number, ctx, iv, ivlen, (int)*ivlen);
|
PDFIO_DEBUG("_pdfioCryptoMakeReader(pdf=%p, obj=%p(%d), ctx=%p, iv=%p, ivlen=%p(%d))\n", pdf, obj, (int)obj->number, ctx, iv, ivlen, (int)*ivlen);
|
||||||
@ -424,6 +427,59 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PDFIO_OBJ_CRYPT
|
||||||
|
if ((id_array = pdfioDictGetArray(pdfioObjGetDict(obj), "ID")) != NULL)
|
||||||
|
{
|
||||||
|
// Object has its own ID that will get used for encryption...
|
||||||
|
_pdfio_md5_t md5; // MD5 context
|
||||||
|
uint8_t file_digest[16]; // MD5 digest of file ID and pad
|
||||||
|
uint8_t user_pad[32], // Padded user password
|
||||||
|
own_user_key[32], // Calculated user key
|
||||||
|
pdf_user_key[32]; // Decrypted user key
|
||||||
|
|
||||||
|
PDFIO_DEBUG("_pdfioCryptoMakeReader: Per-object file ID.\n");
|
||||||
|
|
||||||
|
if ((id_value = pdfioArrayGetBinary(id_array, 0, &id_len)) == NULL)
|
||||||
|
{
|
||||||
|
*ivlen = 0;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pdfioCryptoMD5Init(&md5);
|
||||||
|
_pdfioCryptoMD5Append(&md5, pdf_passpad, 32);
|
||||||
|
_pdfioCryptoMD5Append(&md5, id_value, id_len);
|
||||||
|
_pdfioCryptoMD5Finish(&md5, file_digest);
|
||||||
|
|
||||||
|
make_owner_key(pdf->encryption, pdf->password, pdf->owner_key, user_pad);
|
||||||
|
make_file_key(pdf->encryption, pdf->permissions, id_value, id_len, user_pad, pdf->owner_key, temp_key);
|
||||||
|
make_user_key(id_value, id_len, own_user_key);
|
||||||
|
|
||||||
|
if (memcmp(own_user_key, pdf->user_key, sizeof(own_user_key)))
|
||||||
|
{
|
||||||
|
PDFIO_DEBUG("_pdfioCryptoMakeReader: Not user password, trying owner password.\n");
|
||||||
|
|
||||||
|
make_file_key(pdf->encryption, pdf->permissions, id_value, id_len, pdf->password, pdf->owner_key, temp_key);
|
||||||
|
make_user_key(id_value, id_len, own_user_key);
|
||||||
|
|
||||||
|
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key));
|
||||||
|
decrypt_user_key(pdf->encryption, temp_key, pdf_user_key);
|
||||||
|
|
||||||
|
if (memcmp(pdf->password, pdf_user_key, 32) && memcmp(own_user_key, pdf_user_key, 16))
|
||||||
|
{
|
||||||
|
*ivlen = 0;
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file_key = temp_key;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif // PDFIO_OBJ_CRYPT
|
||||||
|
{
|
||||||
|
// Use the default file key...
|
||||||
|
file_key = pdf->file_key;
|
||||||
|
}
|
||||||
|
|
||||||
switch (pdf->encryption)
|
switch (pdf->encryption)
|
||||||
{
|
{
|
||||||
default :
|
default :
|
||||||
@ -432,7 +488,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
|||||||
|
|
||||||
case PDFIO_ENCRYPTION_RC4_40 :
|
case PDFIO_ENCRYPTION_RC4_40 :
|
||||||
// Copy the key data for the MD5 hash.
|
// Copy the key data for the MD5 hash.
|
||||||
memcpy(data, pdf->file_key, sizeof(pdf->file_key));
|
memcpy(data, file_key, 16);
|
||||||
data[16] = (uint8_t)obj->number;
|
data[16] = (uint8_t)obj->number;
|
||||||
data[17] = (uint8_t)(obj->number >> 8);
|
data[17] = (uint8_t)(obj->number >> 8);
|
||||||
data[18] = (uint8_t)(obj->number >> 16);
|
data[18] = (uint8_t)(obj->number >> 16);
|
||||||
@ -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...
|
// Initialize the RC4 context using 40 bits of the digest...
|
||||||
_pdfioCryptoRC4Init(&ctx->rc4, digest, 5);
|
_pdfioCryptoRC4Init(&ctx->rc4, digest, 5);
|
||||||
|
*ivlen = 0;
|
||||||
return ((_pdfio_crypto_cb_t)_pdfioCryptoRC4Crypt);
|
return ((_pdfio_crypto_cb_t)_pdfioCryptoRC4Crypt);
|
||||||
|
|
||||||
case PDFIO_ENCRYPTION_RC4_128 :
|
|
||||||
case PDFIO_ENCRYPTION_AES_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.
|
// 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[16] = (uint8_t)obj->number;
|
||||||
data[17] = (uint8_t)(obj->number >> 8);
|
data[17] = (uint8_t)(obj->number >> 8);
|
||||||
data[18] = (uint8_t)(obj->number >> 16);
|
data[18] = (uint8_t)(obj->number >> 16);
|
||||||
@ -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
|
_pdfio_crypto_cb_t // O - Encryption callback or `NULL` for none
|
||||||
_pdfioCryptoMakeWriter(
|
_pdfioCryptoMakeWriter(
|
||||||
pdfio_file_t *pdf, // I - PDF file
|
pdfio_file_t *pdf, // I - PDF file
|
||||||
pdfio_obj_t *obj, // I - PDF object
|
pdfio_obj_t *obj, // I - PDF object
|
||||||
_pdfio_crypto_ctx_t *ctx, // I - Pointer to crypto context
|
_pdfio_crypto_ctx_t *ctx, // I - Pointer to crypto context
|
||||||
@ -592,6 +656,8 @@ _pdfioCryptoUnlock(
|
|||||||
revision = (int)pdfioDictGetNumber(encrypt_dict, "R");
|
revision = (int)pdfioDictGetNumber(encrypt_dict, "R");
|
||||||
length = (int)pdfioDictGetNumber(encrypt_dict, "Length");
|
length = (int)pdfioDictGetNumber(encrypt_dict, "Length");
|
||||||
|
|
||||||
|
PDFIO_DEBUG("_pdfioCryptoUnlock: handler=%p(%s), version=%d, revision=%d, length=%d\n", (void *)handler, handler ? handler : "(null)", version, revision, length);
|
||||||
|
|
||||||
if (!handler || strcmp(handler, "Standard"))
|
if (!handler || strcmp(handler, "Standard"))
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Unsupported security handler '%s'.", handler ? handler : "(null)");
|
_pdfioFileError(pdf, "Unsupported security handler '%s'.", handler ? handler : "(null)");
|
||||||
@ -640,6 +706,8 @@ _pdfioCryptoUnlock(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
PDFIO_DEBUG("_pdfioCryptoUnlock: CFM=\"%s\"\n", cfm);
|
||||||
|
|
||||||
if (length < 40 || length > 128)
|
if (length < 40 || length > 128)
|
||||||
length = 128; // Default to 128 bits
|
length = 128; // Default to 128 bits
|
||||||
|
|
||||||
@ -765,17 +833,16 @@ _pdfioCryptoUnlock(
|
|||||||
{
|
{
|
||||||
// Matches!
|
// Matches!
|
||||||
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
|
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
|
||||||
|
memcpy(pdf->password, pad, sizeof(pdf->password));
|
||||||
|
|
||||||
return (true);
|
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);
|
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]);
|
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));
|
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key));
|
||||||
decrypt_user_key(pdf->encryption, file_key, 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: 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]);
|
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!
|
// Matches!
|
||||||
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
|
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
|
||||||
|
memcpy(pdf->password, pad, sizeof(pdf->password));
|
||||||
|
|
||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
111
pdfio-dict.c
111
pdfio-dict.c
@ -1,16 +1,12 @@
|
|||||||
//
|
//
|
||||||
// PDF dictionary functions for PDFio.
|
// PDF dictionary 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
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#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.
|
// '_pdfioDictDebug()' - Dump a dictionary to stderr.
|
||||||
//
|
//
|
||||||
@ -406,9 +426,27 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
|
|||||||
|
|
||||||
|
|
||||||
if (value && value->type == PDFIO_VALTYPE_STRING)
|
if (value && value->type == PDFIO_VALTYPE_STRING)
|
||||||
|
{
|
||||||
return (value->value.string);
|
return (value->value.string);
|
||||||
|
}
|
||||||
|
else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096)
|
||||||
|
{
|
||||||
|
// Convert binary string to regular string...
|
||||||
|
char temp[4096]; // Temporary string
|
||||||
|
|
||||||
|
memcpy(temp, value->value.binary.data, value->value.binary.datalen);
|
||||||
|
temp[value->value.binary.datalen] = '\0';
|
||||||
|
|
||||||
|
free(value->value.binary.data);
|
||||||
|
value->type = PDFIO_VALTYPE_STRING;
|
||||||
|
value->value.string = pdfioStringCreate(dict->pdf, temp);
|
||||||
|
|
||||||
|
return (value->value.string);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -464,6 +502,47 @@ _pdfioDictGetValue(pdfio_dict_t *dict, // I - Dictionary
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'pdfioDictIterateKeys()' - Iterate the keys in a dictionary.
|
||||||
|
//
|
||||||
|
// This function iterates the keys in a dictionary, calling the supplied
|
||||||
|
// function "cb":
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// bool
|
||||||
|
// my_dict_cb(pdfio_dict_t *dict, const char *key, void *cb_data)
|
||||||
|
// {
|
||||||
|
// ... "key" contains the dictionary key ...
|
||||||
|
// ... return true to continue or false to stop ...
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// The iteration continues as long as the callback returns `true` or all keys
|
||||||
|
// have been iterated.
|
||||||
|
//
|
||||||
|
|
||||||
|
void
|
||||||
|
pdfioDictIterateKeys(
|
||||||
|
pdfio_dict_t *dict, // I - Dictionary
|
||||||
|
pdfio_dict_cb_t cb, // I - Callback function
|
||||||
|
void *cb_data) // I - Callback data
|
||||||
|
{
|
||||||
|
size_t i; // Looping var
|
||||||
|
_pdfio_pair_t *pair; // Current pair
|
||||||
|
|
||||||
|
|
||||||
|
// Range check input...
|
||||||
|
if (!dict || !cb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
|
||||||
|
{
|
||||||
|
if (!(cb)(dict, pair->key, cb_data))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// '_pdfioDictRead()' - Read a dictionary from a PDF file.
|
// '_pdfioDictRead()' - Read a dictionary from a PDF file.
|
||||||
//
|
//
|
||||||
@ -481,7 +560,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
_pdfio_value_t value; // Dictionary value
|
_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...
|
// Create a dictionary and start reading...
|
||||||
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
||||||
@ -493,6 +572,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
if (!strcmp(key, ">>"))
|
if (!strcmp(key, ">>"))
|
||||||
{
|
{
|
||||||
// End of dictionary...
|
// End of dictionary...
|
||||||
|
PDFIO_DEBUG("_pdfioDictRead: Returning dictionary value...\n");
|
||||||
return (dict);
|
return (dict);
|
||||||
}
|
}
|
||||||
else if (key[0] != '/')
|
else if (key[0] != '/')
|
||||||
@ -500,18 +580,25 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
_pdfioFileError(pdf, "Invalid dictionary contents.");
|
_pdfioFileError(pdf, "Invalid dictionary contents.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else if (_pdfioDictGetValue(dict, key + 1))
|
||||||
|
{
|
||||||
|
_pdfioFileError(pdf, "Duplicate dictionary key '%s'.", key + 1);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// Then get the next value...
|
// Then get the next value...
|
||||||
|
PDFIO_DEBUG("_pdfioDictRead: Reading value for '%s'.\n", key + 1);
|
||||||
|
|
||||||
if (!_pdfioValueRead(pdf, obj, tb, &value, depth))
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value))
|
if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value))
|
||||||
break;
|
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
|
// Dictionary is invalid - pdfioFileClose will free the memory, return NULL
|
||||||
@ -707,7 +794,7 @@ pdfioDictSetNull(pdfio_dict_t *dict, // I - Dictionary
|
|||||||
bool // O - `true` on success, `false` on failure
|
bool // O - `true` on success, `false` on failure
|
||||||
pdfioDictSetNumber(pdfio_dict_t *dict, // I - Dictionary
|
pdfioDictSetNumber(pdfio_dict_t *dict, // I - Dictionary
|
||||||
const char *key, // I - Key
|
const char *key, // I - Key
|
||||||
double value) // I - Value
|
double value) // I - Value
|
||||||
{
|
{
|
||||||
_pdfio_value_t temp; // New value
|
_pdfio_value_t temp; // New value
|
||||||
|
|
||||||
@ -891,9 +978,9 @@ _pdfioDictSetValue(
|
|||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
PDFIO_DEBUG("_pdfioDictSetValue(%p): %lu pairs\n", (void *)dict, (unsigned long)dict->num_pairs);
|
PDFIO_DEBUG("_pdfioDictSetValue(%p): %lu pairs\n", (void *)dict, (unsigned long)dict->num_pairs);
|
||||||
PDFIO_DEBUG("_pdfioDictSetValue(%p): ", (void *)dict);
|
// PDFIO_DEBUG("_pdfioDictSetValue(%p): ", (void *)dict);
|
||||||
PDFIO_DEBUG_DICT(dict);
|
// PDFIO_DEBUG_DICT(dict);
|
||||||
PDFIO_DEBUG("\n");
|
// PDFIO_DEBUG("\n");
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
|
||||||
return (true);
|
return (true);
|
||||||
|
902
pdfio-file.c
902
pdfio-file.c
File diff suppressed because it is too large
Load Diff
@ -1,16 +1,12 @@
|
|||||||
//
|
//
|
||||||
// PDF object functions for PDFio.
|
// PDF object 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
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#include "pdfio-private.h"
|
||||||
|
|
||||||
|
|
||||||
@ -33,8 +29,14 @@ pdfioObjClose(pdfio_obj_t *obj) // I - Object
|
|||||||
if (!obj)
|
if (!obj)
|
||||||
return (false);
|
return (false);
|
||||||
|
|
||||||
|
// Clear the current object pointer...
|
||||||
|
obj->pdf->current_obj = NULL;
|
||||||
|
|
||||||
if (obj->pdf->mode != _PDFIO_MODE_WRITE)
|
if (obj->pdf->mode != _PDFIO_MODE_WRITE)
|
||||||
return (true); // Nothing to do when reading
|
{
|
||||||
|
// Nothing to do when reading
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
// Write what remains for the object...
|
// Write what remains for the object...
|
||||||
if (!obj->offset)
|
if (!obj->offset)
|
||||||
@ -165,6 +167,12 @@ pdfioObjCreateStream(
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (obj->pdf->current_obj)
|
||||||
|
{
|
||||||
|
_pdfioFileError(obj->pdf, "Another object (%u) is already open.", (unsigned)obj->pdf->current_obj->number);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// Write the header...
|
// Write the header...
|
||||||
if (!_pdfioDictGetValue(obj->value.value.dict, "Length"))
|
if (!_pdfioDictGetValue(obj->value.value.dict, "Length"))
|
||||||
{
|
{
|
||||||
@ -193,7 +201,8 @@ pdfioObjCreateStream(
|
|||||||
if (!_pdfioFilePuts(obj->pdf, "stream\n"))
|
if (!_pdfioFilePuts(obj->pdf, "stream\n"))
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
obj->stream_offset = _pdfioFileTell(obj->pdf);
|
obj->stream_offset = _pdfioFileTell(obj->pdf);
|
||||||
|
obj->pdf->current_obj = obj;
|
||||||
|
|
||||||
// Return the new stream...
|
// Return the new stream...
|
||||||
return (_pdfioStreamCreate(obj, length_obj, filter));
|
return (_pdfioStreamCreate(obj, length_obj, filter));
|
||||||
@ -208,8 +217,13 @@ void
|
|||||||
_pdfioObjDelete(pdfio_obj_t *obj) // I - Object
|
_pdfioObjDelete(pdfio_obj_t *obj) // I - Object
|
||||||
{
|
{
|
||||||
if (obj)
|
if (obj)
|
||||||
|
{
|
||||||
pdfioStreamClose(obj->stream);
|
pdfioStreamClose(obj->stream);
|
||||||
|
|
||||||
|
if (obj->datafree)
|
||||||
|
(obj->datafree)(obj->data);
|
||||||
|
}
|
||||||
|
|
||||||
free(obj);
|
free(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,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.
|
// 'pdfioObjGetGeneration()' - Get the object's generation number.
|
||||||
//
|
//
|
||||||
@ -407,6 +432,9 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
|
|||||||
}
|
}
|
||||||
|
|
||||||
ptr += 3;
|
ptr += 3;
|
||||||
|
while (*ptr && isspace(*ptr & 255))
|
||||||
|
ptr ++;
|
||||||
|
|
||||||
_pdfioFileConsume(obj->pdf, (size_t)(ptr - line));
|
_pdfioFileConsume(obj->pdf, (size_t)(ptr - line));
|
||||||
|
|
||||||
// Then grab the object value...
|
// Then grab the object value...
|
||||||
@ -425,15 +453,29 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
|
|||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
_pdfioTokenFlush(&tb);
|
||||||
|
|
||||||
if (!strcmp(line, "stream"))
|
if (!strcmp(line, "stream"))
|
||||||
{
|
{
|
||||||
// Yes, save its location...
|
// Yes, this is an embedded stream so save its location...
|
||||||
obj->stream_offset = _pdfioFileTell(obj->pdf);
|
obj->stream_offset = _pdfioFileTell(obj->pdf);
|
||||||
PDFIO_DEBUG("_pdfioObjLoad: stream_offset=%lu.\n", (unsigned long)obj->stream_offset);
|
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("_pdfioObjLoad: ");
|
||||||
PDFIO_DEBUG_VALUE(&obj->value);
|
PDFIO_DEBUG_VALUE(&obj->value);
|
||||||
PDFIO_DEBUG("\n");
|
PDFIO_DEBUG("\n");
|
||||||
@ -454,6 +496,12 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
|
|||||||
if (!obj)
|
if (!obj)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
|
if (obj->pdf->current_obj)
|
||||||
|
{
|
||||||
|
_pdfioFileError(obj->pdf, "Another object (%u) is already open.", (unsigned)obj->pdf->current_obj->number);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure we've loaded the object dictionary...
|
// Make sure we've loaded the object dictionary...
|
||||||
if (!obj->value.type)
|
if (!obj->value.type)
|
||||||
{
|
{
|
||||||
@ -466,10 +514,27 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
// Open the stream...
|
// Open the stream...
|
||||||
|
obj->pdf->current_obj = obj;
|
||||||
|
|
||||||
return (_pdfioStreamOpen(obj, decode));
|
return (_pdfioStreamOpen(obj, decode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// '_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...
|
// 'write_obj_header()' - Write the object header...
|
||||||
//
|
//
|
||||||
|
@ -7,10 +7,6 @@
|
|||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#include "pdfio-private.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// Private header file for PDFio.
|
// Private header file for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021 by Michael R Sweet.
|
// Copyright © 2021-2024 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
@ -9,41 +9,23 @@
|
|||||||
|
|
||||||
#ifndef PDFIO_PRIVATE_H
|
#ifndef PDFIO_PRIVATE_H
|
||||||
# define PDFIO_PRIVATE_H
|
# define PDFIO_PRIVATE_H
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
/*
|
# define _CRT_SECURE_NO_WARNINGS // Disable bogus VS warnings/errors...
|
||||||
* Disable bogus VS warnings/errors...
|
|
||||||
*/
|
|
||||||
|
|
||||||
# define _CRT_SECURE_NO_WARNINGS
|
|
||||||
# endif // _WIN32
|
# endif // _WIN32
|
||||||
|
|
||||||
# include "pdfio.h"
|
# include "pdfio.h"
|
||||||
# include <stdarg.h>
|
# include <stdarg.h>
|
||||||
# include <stdint.h>
|
# include <stdint.h>
|
||||||
# include <string.h>
|
# include <string.h>
|
||||||
|
# include <ctype.h>
|
||||||
# include <errno.h>
|
# include <errno.h>
|
||||||
# include <inttypes.h>
|
# include <inttypes.h>
|
||||||
# include <fcntl.h>
|
# include <fcntl.h>
|
||||||
|
# include <locale.h>
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# include <direct.h>
|
# include <direct.h>
|
||||||
|
# include <windows.h> // GetTempPathA
|
||||||
/*
|
# define access _access // Map standard POSIX/C99 names
|
||||||
* Microsoft renames the POSIX functions to _name, and introduces
|
|
||||||
* a broken compatibility layer using the original names. As a result,
|
|
||||||
* random crashes can occur when, for example, strdup() allocates memory
|
|
||||||
* from a different heap than used by malloc() and free().
|
|
||||||
*
|
|
||||||
* To avoid moronic problems like this, we #define the POSIX function
|
|
||||||
* names to the corresponding non-standard Microsoft names.
|
|
||||||
*/
|
|
||||||
|
|
||||||
# define access _access
|
|
||||||
# define close _close
|
# define close _close
|
||||||
# define fileno _fileno
|
# define fileno _fileno
|
||||||
# define lseek _lseek
|
# define lseek _lseek
|
||||||
@ -56,25 +38,20 @@
|
|||||||
# define unlink _unlink
|
# define unlink _unlink
|
||||||
# define vsnprintf _vsnprintf
|
# define vsnprintf _vsnprintf
|
||||||
# define write _write
|
# define write _write
|
||||||
|
# ifndef F_OK
|
||||||
/*
|
# define F_OK 00 // POSIX parameters/flags
|
||||||
* Map various parameters for POSIX...
|
# define W_OK 02
|
||||||
*/
|
# define R_OK 04
|
||||||
|
# endif // !F_OK
|
||||||
# define F_OK 00
|
# define O_RDONLY _O_RDONLY // Map standard POSIX open flags
|
||||||
# define W_OK 02
|
|
||||||
# define R_OK 04
|
|
||||||
# define O_RDONLY _O_RDONLY
|
|
||||||
# define O_WRONLY _O_WRONLY
|
# define O_WRONLY _O_WRONLY
|
||||||
# define O_CREAT _O_CREAT
|
# define O_CREAT _O_CREAT
|
||||||
# define O_TRUNC _O_TRUNC
|
# define O_TRUNC _O_TRUNC
|
||||||
# define O_BINARY _O_BINARY
|
# define O_BINARY _O_BINARY
|
||||||
# else // !_WIN32
|
# else // !_WIN32
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# define O_BINARY 0
|
# define O_BINARY 0 // Map Windows-specific open flag
|
||||||
# endif // _WIN32
|
# endif // _WIN32
|
||||||
# include <string.h>
|
|
||||||
# include <ctype.h>
|
|
||||||
# include <zlib.h>
|
# include <zlib.h>
|
||||||
|
|
||||||
|
|
||||||
@ -118,6 +95,9 @@
|
|||||||
|
|
||||||
# define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values
|
# 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
|
typedef enum _pdfio_mode_e // Read/write mode
|
||||||
{
|
{
|
||||||
_PDFIO_MODE_READ, // Read a PDF file
|
_PDFIO_MODE_READ, // Read a PDF file
|
||||||
@ -247,6 +227,7 @@ typedef struct _pdfio_objmap_s // PDF object map
|
|||||||
struct _pdfio_file_s // PDF file structure
|
struct _pdfio_file_s // PDF file structure
|
||||||
{
|
{
|
||||||
char *filename; // Filename
|
char *filename; // Filename
|
||||||
|
struct lconv *loc; // Locale data
|
||||||
char *version; // Version number
|
char *version; // Version number
|
||||||
pdfio_rect_t media_box, // Default MediaBox value
|
pdfio_rect_t media_box, // Default MediaBox value
|
||||||
crop_box; // Default CropBox value
|
crop_box; // Default CropBox value
|
||||||
@ -260,7 +241,8 @@ struct _pdfio_file_s // PDF file structure
|
|||||||
pdfio_permission_t permissions; // Access permissions (encrypted PDF files)
|
pdfio_permission_t permissions; // Access permissions (encrypted PDF files)
|
||||||
uint8_t file_key[16], // File encryption key
|
uint8_t file_key[16], // File encryption key
|
||||||
owner_key[32], // Owner 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
|
size_t file_keylen, // Length of file encryption key
|
||||||
owner_keylen, // Length of owner encryption key
|
owner_keylen, // Length of owner encryption key
|
||||||
user_keylen; // Length of user encryption key
|
user_keylen; // Length of user encryption key
|
||||||
@ -288,8 +270,10 @@ struct _pdfio_file_s // PDF file structure
|
|||||||
alloc_dicts; // Allocated dictionaries
|
alloc_dicts; // Allocated dictionaries
|
||||||
pdfio_dict_t **dicts; // Dictionaries
|
pdfio_dict_t **dicts; // Dictionaries
|
||||||
size_t num_objs, // Number of objects
|
size_t num_objs, // Number of objects
|
||||||
alloc_objs; // Allocated objects
|
alloc_objs, // Allocated objects
|
||||||
pdfio_obj_t **objs; // 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
|
size_t num_objmaps, // Number of object maps
|
||||||
alloc_objmaps; // Allocated object maps
|
alloc_objmaps; // Allocated object maps
|
||||||
_pdfio_objmap_t *objmaps; // Object maps
|
_pdfio_objmap_t *objmaps; // Object maps
|
||||||
@ -312,6 +296,8 @@ struct _pdfio_obj_s // Object
|
|||||||
size_t stream_length; // Length of stream, if any
|
size_t stream_length; // Length of stream, if any
|
||||||
_pdfio_value_t value; // Dictionary/number/etc. value
|
_pdfio_value_t value; // Dictionary/number/etc. value
|
||||||
pdfio_stream_t *stream; // Open stream, if any
|
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
|
struct _pdfio_stream_s // Stream
|
||||||
@ -340,6 +326,10 @@ struct _pdfio_stream_s // Stream
|
|||||||
// Functions...
|
// Functions...
|
||||||
//
|
//
|
||||||
|
|
||||||
|
extern double _pdfio_strtod(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
|
||||||
|
extern ssize_t _pdfio_vsnprintf(pdfio_file_t *pdf, char *buffer, size_t bufsize, const char *format, va_list ap) _PDFIO_INTERNAL;
|
||||||
|
|
||||||
|
extern bool _pdfioArrayDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_array_t *a, size_t depth) _PDFIO_INTERNAL;
|
||||||
extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
|
extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
|
||||||
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;
|
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;
|
||||||
extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL;
|
extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL;
|
||||||
@ -364,6 +354,7 @@ extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Dig
|
|||||||
extern bool _pdfioCryptoUnlock(pdfio_file_t *pdf, pdfio_password_cb_t password_cb, void *password_data) _PDFIO_INTERNAL;
|
extern bool _pdfioCryptoUnlock(pdfio_file_t *pdf, pdfio_password_cb_t password_cb, void *password_data) _PDFIO_INTERNAL;
|
||||||
|
|
||||||
extern void _pdfioDictClear(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
|
extern void _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 _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
|
||||||
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
|
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
|
||||||
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
|
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
|
||||||
@ -390,7 +381,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 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 _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 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 *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, pdfio_filter_t compression) _PDFIO_INTERNAL;
|
||||||
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
|
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
|
||||||
@ -405,9 +398,11 @@ 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 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 _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 _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL;
|
||||||
extern void _pdfioValueDelete(_pdfio_value_t *v) _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;
|
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;
|
||||||
extern bool _pdfioValueWrite(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL;
|
extern bool _pdfioValueWrite(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL;
|
||||||
|
|
||||||
|
|
||||||
#endif // !PDFIO_PRIVATE_H
|
#endif // !PDFIO_PRIVATE_H
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// SHA-256 functions for PDFio.
|
// SHA-256 functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021 by Michael R Sweet.
|
// Copyright © 2021-2023 by Michael R Sweet.
|
||||||
// Copyright © 2011 IETF Trust and the persons identified as authors of the
|
// Copyright © 2011 IETF Trust and the persons identified as authors of the
|
||||||
// code. All rights reserved.
|
// code. All rights reserved.
|
||||||
//
|
//
|
||||||
@ -217,7 +217,7 @@ _pdfioCryptoSHA256Append(_pdfio_sha256_t *context, const uint8_t *message_array,
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
_pdfioCryptoSHA256Finish(_pdfio_sha256_t *context,
|
_pdfioCryptoSHA256Finish(_pdfio_sha256_t *context,
|
||||||
uint8_t Message_Digest[SHA256HashSize])
|
uint8_t *Message_Digest)
|
||||||
{
|
{
|
||||||
SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
|
SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
//
|
//
|
||||||
// PDF stream functions for PDFio.
|
// PDF stream functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2022 by Michael R Sweet.
|
// Copyright © 2021-2024 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#include "pdfio-private.h"
|
||||||
|
|
||||||
|
|
||||||
@ -174,6 +170,8 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
|
|||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
||||||
|
st->pdf->current_obj = NULL;
|
||||||
|
|
||||||
free(st->prbuffer);
|
free(st->prbuffer);
|
||||||
free(st->psbuffer);
|
free(st->psbuffer);
|
||||||
free(st);
|
free(st);
|
||||||
@ -364,6 +362,13 @@ pdfioStreamConsume(pdfio_stream_t *st, // I - Stream
|
|||||||
//
|
//
|
||||||
// 'pdfioStreamGetToken()' - Read a single PDF token from a stream.
|
// 'pdfioStreamGetToken()' - Read a single PDF token from a stream.
|
||||||
//
|
//
|
||||||
|
// This function reads a single PDF token from a stream. Operator tokens,
|
||||||
|
// boolean values, and numbers are returned as-is in the provided string buffer.
|
||||||
|
// String values start with the opening parenthesis ('(') but have all escaping
|
||||||
|
// resolved and the terminating parenthesis removed. Hexadecimal string values
|
||||||
|
// start with the opening angle bracket ('<') and have all whitespace and the
|
||||||
|
// terminating angle bracket removed.
|
||||||
|
//
|
||||||
|
|
||||||
bool // O - `true` on success, `false` on EOF
|
bool // O - `true` on success, `false` on EOF
|
||||||
pdfioStreamGetToken(
|
pdfioStreamGetToken(
|
||||||
@ -443,6 +448,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PDFIO_DEBUG("_pdfioStreamOpen: ivlen=%d\n", (int)ivlen);
|
||||||
if (ivlen > 0)
|
if (ivlen > 0)
|
||||||
_pdfioFileConsume(st->pdf, ivlen);
|
_pdfioFileConsume(st->pdf, ivlen);
|
||||||
|
|
||||||
@ -455,11 +461,19 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
|
|||||||
// Try to decode/decompress the contents of this object...
|
// Try to decode/decompress the contents of this object...
|
||||||
const char *filter = pdfioDictGetName(dict, "Filter");
|
const char *filter = pdfioDictGetName(dict, "Filter");
|
||||||
// Filter value
|
// Filter value
|
||||||
|
pdfio_array_t *fa = pdfioDictGetArray(dict, "Filter");
|
||||||
|
// Filter array
|
||||||
|
|
||||||
|
if (!filter && fa && pdfioArrayGetSize(fa) == 1)
|
||||||
|
{
|
||||||
|
// Support single-valued arrays...
|
||||||
|
filter = pdfioArrayGetName(fa, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (!filter)
|
if (!filter)
|
||||||
{
|
{
|
||||||
// No single filter name, do we have a compound filter?
|
// No single filter name, do we have a compound filter?
|
||||||
if (pdfioDictGetArray(dict, "Filter"))
|
if (fa)
|
||||||
{
|
{
|
||||||
// TODO: Implement compound filters...
|
// TODO: Implement compound filters...
|
||||||
_pdfioFileError(st->pdf, "Unsupported compound stream filter.");
|
_pdfioFileError(st->pdf, "Unsupported compound stream filter.");
|
||||||
@ -550,6 +564,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
|
|||||||
else
|
else
|
||||||
st->predictor = _PDFIO_PREDICTOR_NONE;
|
st->predictor = _PDFIO_PREDICTOR_NONE;
|
||||||
|
|
||||||
|
PDFIO_DEBUG("_pdfioStreamOpen: pos=%ld\n", (long)_pdfioFileTell(st->pdf));
|
||||||
if (sizeof(st->cbuffer) > st->remaining)
|
if (sizeof(st->cbuffer) > st->remaining)
|
||||||
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
|
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
|
||||||
else
|
else
|
||||||
@ -570,12 +585,6 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
|
|||||||
st->flate.next_in = (Bytef *)st->cbuffer;
|
st->flate.next_in = (Bytef *)st->cbuffer;
|
||||||
st->flate.avail_in = (uInt)rbytes;
|
st->flate.avail_in = (uInt)rbytes;
|
||||||
|
|
||||||
if (st->cbuffer[0] == 0x0a)
|
|
||||||
{
|
|
||||||
st->flate.next_in ++; // Skip newline
|
|
||||||
st->flate.avail_in --;
|
|
||||||
}
|
|
||||||
|
|
||||||
PDFIO_DEBUG("_pdfioStreamOpen: avail_in=%u, cbuffer=<%02X%02X%02X%02X%02X%02X%02X%02X...>\n", st->flate.avail_in, st->cbuffer[0], st->cbuffer[1], st->cbuffer[2], st->cbuffer[3], st->cbuffer[4], st->cbuffer[5], st->cbuffer[6], st->cbuffer[7]);
|
PDFIO_DEBUG("_pdfioStreamOpen: avail_in=%u, cbuffer=<%02X%02X%02X%02X%02X%02X%02X%02X...>\n", st->flate.avail_in, st->cbuffer[0], st->cbuffer[1], st->cbuffer[2], st->cbuffer[3], st->cbuffer[4], st->cbuffer[5], st->cbuffer[6], st->cbuffer[7]);
|
||||||
|
|
||||||
if ((status = inflateInit(&(st->flate))) != Z_OK)
|
if ((status = inflateInit(&(st->flate))) != Z_OK)
|
||||||
@ -678,7 +687,7 @@ pdfioStreamPrintf(
|
|||||||
|
|
||||||
// Format the string...
|
// Format the string...
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
vsnprintf(buffer, sizeof(buffer), format, ap);
|
_pdfio_vsnprintf(st->pdf, buffer, sizeof(buffer), format, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
// Write the string...
|
// Write the string...
|
||||||
@ -999,6 +1008,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
|
|||||||
size_t bytes) // I - Number of bytes to read
|
size_t bytes) // I - Number of bytes to read
|
||||||
{
|
{
|
||||||
ssize_t rbytes; // Bytes read
|
ssize_t rbytes; // Bytes read
|
||||||
|
uInt avail_in, avail_out; // Previous flate values
|
||||||
|
|
||||||
|
|
||||||
if (st->filter == PDFIO_FILTER_NONE)
|
if (st->filter == PDFIO_FILTER_NONE)
|
||||||
@ -1051,9 +1061,17 @@ stream_read(pdfio_stream_t *st, // I - Stream
|
|||||||
st->flate.next_out = (Bytef *)buffer;
|
st->flate.next_out = (Bytef *)buffer;
|
||||||
st->flate.avail_out = (uInt)bytes;
|
st->flate.avail_out = (uInt)bytes;
|
||||||
|
|
||||||
|
avail_in = st->flate.avail_in;
|
||||||
|
avail_out = st->flate.avail_out;
|
||||||
|
|
||||||
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
|
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)
|
||||||
|
{
|
||||||
|
_pdfioFileError(st->pdf, "Corrupt stream data.");
|
||||||
return (-1);
|
return (-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1104,12 +1122,15 @@ stream_read(pdfio_stream_t *st, // I - Stream
|
|||||||
st->flate.avail_in = (uInt)rbytes;
|
st->flate.avail_in = (uInt)rbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
avail_in = st->flate.avail_in;
|
||||||
|
avail_out = st->flate.avail_out;
|
||||||
|
|
||||||
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
|
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);
|
return (-1);
|
||||||
}
|
}
|
||||||
else if (status == Z_STREAM_END)
|
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1171,12 +1192,15 @@ stream_read(pdfio_stream_t *st, // I - Stream
|
|||||||
st->flate.avail_in = (uInt)rbytes;
|
st->flate.avail_in = (uInt)rbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
avail_in = st->flate.avail_in;
|
||||||
|
avail_out = st->flate.avail_out;
|
||||||
|
|
||||||
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
|
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);
|
return (-1);
|
||||||
}
|
}
|
||||||
else if (status == Z_STREAM_END)
|
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
457
pdfio-string.c
457
pdfio-string.c
@ -1,16 +1,12 @@
|
|||||||
//
|
//
|
||||||
// PDF dictionary functions for PDFio.
|
// PDF string functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021 by Michael R Sweet.
|
// Copyright © 2021-2024 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#include "pdfio-private.h"
|
||||||
|
|
||||||
|
|
||||||
@ -18,7 +14,360 @@
|
|||||||
// Local functions...
|
// Local functions...
|
||||||
//
|
//
|
||||||
|
|
||||||
static int compare_strings(char **a, char **b);
|
static size_t find_string(pdfio_file_t *pdf, const char *s, int *rdiff);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// '_pdfio_strtod()' - Convert a string to a double value.
|
||||||
|
//
|
||||||
|
// This function wraps strtod() to avoid locale issues.
|
||||||
|
//
|
||||||
|
|
||||||
|
double // O - Double value
|
||||||
|
_pdfio_strtod(pdfio_file_t *pdf, // I - PDF file
|
||||||
|
const char *s) // I - String
|
||||||
|
{
|
||||||
|
char temp[64], // Temporary buffer
|
||||||
|
*tempptr; // Pointer into temporary buffer
|
||||||
|
|
||||||
|
|
||||||
|
// See if the locale has a special decimal point string...
|
||||||
|
if (!pdf->loc)
|
||||||
|
return (strtod(s, NULL));
|
||||||
|
|
||||||
|
// Copy leading sign, numbers, period, and then numbers...
|
||||||
|
tempptr = temp;
|
||||||
|
temp[sizeof(temp) - 1] = '\0';
|
||||||
|
|
||||||
|
while (*s && *s != '.')
|
||||||
|
{
|
||||||
|
if (tempptr < (temp + sizeof(temp) - 1))
|
||||||
|
*tempptr++ = *s++;
|
||||||
|
else
|
||||||
|
return (0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*s == '.')
|
||||||
|
{
|
||||||
|
// Convert decimal point to locale equivalent...
|
||||||
|
size_t declen = strlen(pdf->loc->decimal_point);
|
||||||
|
// Length of decimal point
|
||||||
|
s ++;
|
||||||
|
|
||||||
|
if (declen <= (sizeof(temp) - (size_t)(tempptr - temp)))
|
||||||
|
{
|
||||||
|
memcpy(tempptr, pdf->loc->decimal_point, declen);
|
||||||
|
tempptr += declen;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy any remaining characters...
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
if (tempptr < (temp + sizeof(temp) - 1))
|
||||||
|
*tempptr++ = *s++;
|
||||||
|
else
|
||||||
|
return (0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nul-terminate the temporary string and convert the string...
|
||||||
|
*tempptr = '\0';
|
||||||
|
|
||||||
|
return (strtod(temp, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// '_pdfio_vsnprintf()' - Format a string.
|
||||||
|
//
|
||||||
|
// This function emulates vsnprintf() to avoid locale issues.
|
||||||
|
//
|
||||||
|
|
||||||
|
ssize_t // O - Number of bytes
|
||||||
|
_pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
|
||||||
|
char *buffer, // I - Output buffer
|
||||||
|
size_t bufsize, // I - Size of output buffer
|
||||||
|
const char *format, // I - printf-style format string
|
||||||
|
va_list ap) // I - Pointer to additional arguments
|
||||||
|
{
|
||||||
|
char *bufptr, // Pointer to position in buffer
|
||||||
|
*bufend, // Pointer to end of buffer
|
||||||
|
size, // Size character (h, l, L)
|
||||||
|
type; // Format type character
|
||||||
|
int width, // Width of field
|
||||||
|
prec; // Number of characters of precision
|
||||||
|
char tformat[100], // Temporary format string for snprintf()
|
||||||
|
*tptr, // Pointer into temporary format
|
||||||
|
temp[1024], // Buffer for formatted numbers
|
||||||
|
*tempptr; // Pointer into buffer
|
||||||
|
char *s; // Pointer to string
|
||||||
|
ssize_t bytes; // Total number of bytes needed
|
||||||
|
const char *dec = pdf->loc ? pdf->loc->decimal_point : ".";
|
||||||
|
// Decimal point string
|
||||||
|
char *decptr; // Pointer to decimal point
|
||||||
|
|
||||||
|
|
||||||
|
// Loop through the format string, formatting as needed...
|
||||||
|
bufptr = buffer;
|
||||||
|
bufend = buffer + bufsize - 1;
|
||||||
|
*bufend = '\0';
|
||||||
|
bytes = 0;
|
||||||
|
|
||||||
|
while (*format)
|
||||||
|
{
|
||||||
|
if (*format == '%')
|
||||||
|
{
|
||||||
|
// Format character...
|
||||||
|
tptr = tformat;
|
||||||
|
*tptr++ = *format++;
|
||||||
|
|
||||||
|
if (*format == '%')
|
||||||
|
{
|
||||||
|
if (bufptr < bufend)
|
||||||
|
*bufptr++ = *format;
|
||||||
|
bytes ++;
|
||||||
|
format ++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (strchr(" -+#\'", *format))
|
||||||
|
{
|
||||||
|
*tptr++ = *format++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*format == '*')
|
||||||
|
{
|
||||||
|
// Get width from argument...
|
||||||
|
format ++;
|
||||||
|
width = va_arg(ap, int);
|
||||||
|
|
||||||
|
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
|
||||||
|
tptr += strlen(tptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
width = 0;
|
||||||
|
|
||||||
|
while (isdigit(*format & 255))
|
||||||
|
{
|
||||||
|
if (tptr < (tformat + sizeof(tformat) - 1))
|
||||||
|
*tptr++ = *format;
|
||||||
|
|
||||||
|
width = width * 10 + *format++ - '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*format == '.')
|
||||||
|
{
|
||||||
|
if (tptr < (tformat + sizeof(tformat) - 1))
|
||||||
|
*tptr++ = *format;
|
||||||
|
|
||||||
|
format ++;
|
||||||
|
|
||||||
|
if (*format == '*')
|
||||||
|
{
|
||||||
|
// Get precision from argument...
|
||||||
|
format ++;
|
||||||
|
prec = va_arg(ap, int);
|
||||||
|
|
||||||
|
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
|
||||||
|
tptr += strlen(tptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prec = 0;
|
||||||
|
|
||||||
|
while (isdigit(*format & 255))
|
||||||
|
{
|
||||||
|
if (tptr < (tformat + sizeof(tformat) - 1))
|
||||||
|
*tptr++ = *format;
|
||||||
|
|
||||||
|
prec = prec * 10 + *format++ - '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*format == 'l' && format[1] == 'l')
|
||||||
|
{
|
||||||
|
size = 'L';
|
||||||
|
|
||||||
|
if (tptr < (tformat + sizeof(tformat) - 2))
|
||||||
|
{
|
||||||
|
*tptr++ = 'l';
|
||||||
|
*tptr++ = 'l';
|
||||||
|
}
|
||||||
|
|
||||||
|
format += 2;
|
||||||
|
}
|
||||||
|
else if (*format == 'h' || *format == 'l' || *format == 'L')
|
||||||
|
{
|
||||||
|
if (tptr < (tformat + sizeof(tformat) - 1))
|
||||||
|
*tptr++ = *format;
|
||||||
|
|
||||||
|
size = *format++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*format)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (tptr < (tformat + sizeof(tformat) - 1))
|
||||||
|
*tptr++ = *format;
|
||||||
|
|
||||||
|
type = *format++;
|
||||||
|
*tptr = '\0';
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 'E' : // Floating point formats
|
||||||
|
case 'G' :
|
||||||
|
case 'e' :
|
||||||
|
case 'f' :
|
||||||
|
case 'g' :
|
||||||
|
if ((size_t)(width + 2) > sizeof(temp))
|
||||||
|
break;
|
||||||
|
|
||||||
|
snprintf(temp, sizeof(temp), tformat, va_arg(ap, double));
|
||||||
|
|
||||||
|
if ((decptr = strstr(temp, dec)) != NULL)
|
||||||
|
{
|
||||||
|
// Convert locale decimal point to "."
|
||||||
|
PDFIO_DEBUG("_pdfio_vsnprintf: Before \"%s\"\n", temp);
|
||||||
|
tempptr = decptr + strlen(dec);
|
||||||
|
if (tempptr > (decptr + 1))
|
||||||
|
memmove(decptr + 1, tempptr, strlen(tempptr) + 1);
|
||||||
|
*decptr = '.';
|
||||||
|
|
||||||
|
// Strip trailing 0's...
|
||||||
|
for (tempptr = temp + strlen(temp) - 1; tempptr > temp && *tempptr == '0'; tempptr --)
|
||||||
|
*tempptr = '\0';
|
||||||
|
|
||||||
|
if (*tempptr == '.')
|
||||||
|
*tempptr = '\0'; // Strip trailing decimal point
|
||||||
|
|
||||||
|
PDFIO_DEBUG("_pdfio_vsnprintf: After \"%s\"\n", temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy to the output buffer
|
||||||
|
bytes += (int)strlen(temp);
|
||||||
|
|
||||||
|
if (bufptr < bufend)
|
||||||
|
{
|
||||||
|
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
|
||||||
|
bufptr += strlen(bufptr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'B' : // Integer formats
|
||||||
|
case 'X' :
|
||||||
|
case 'b' :
|
||||||
|
case 'd' :
|
||||||
|
case 'i' :
|
||||||
|
case 'o' :
|
||||||
|
case 'u' :
|
||||||
|
case 'x' :
|
||||||
|
if ((size_t)(width + 2) > sizeof(temp))
|
||||||
|
break;
|
||||||
|
|
||||||
|
# ifdef HAVE_LONG_LONG
|
||||||
|
if (size == 'L')
|
||||||
|
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long));
|
||||||
|
else
|
||||||
|
# endif // HAVE_LONG_LONG
|
||||||
|
if (size == 'l')
|
||||||
|
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long));
|
||||||
|
else
|
||||||
|
snprintf(temp, sizeof(temp), tformat, va_arg(ap, int));
|
||||||
|
|
||||||
|
bytes += (int)strlen(temp);
|
||||||
|
|
||||||
|
if (bufptr < bufend)
|
||||||
|
{
|
||||||
|
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
|
||||||
|
bufptr += strlen(bufptr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p' : // Pointer value
|
||||||
|
if ((size_t)(width + 2) > sizeof(temp))
|
||||||
|
break;
|
||||||
|
|
||||||
|
snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *));
|
||||||
|
|
||||||
|
bytes += (int)strlen(temp);
|
||||||
|
|
||||||
|
if (bufptr < bufend)
|
||||||
|
{
|
||||||
|
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
|
||||||
|
bufptr += strlen(bufptr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'c' : // Character or character array
|
||||||
|
bytes += width;
|
||||||
|
|
||||||
|
if (bufptr < bufend)
|
||||||
|
{
|
||||||
|
if (width <= 1)
|
||||||
|
{
|
||||||
|
*bufptr++ = (char)va_arg(ap, int);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((bufptr + width) > bufend)
|
||||||
|
width = (int)(bufend - bufptr);
|
||||||
|
|
||||||
|
memcpy(bufptr, va_arg(ap, char *), (size_t)width);
|
||||||
|
bufptr += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's' : // String
|
||||||
|
if ((s = va_arg(ap, char *)) == NULL)
|
||||||
|
s = "(null)";
|
||||||
|
|
||||||
|
bytes += strlen(s);
|
||||||
|
|
||||||
|
if (bufptr < bufend)
|
||||||
|
{
|
||||||
|
strncpy(bufptr, s, (size_t)(bufend - bufptr - 1));
|
||||||
|
bufptr += strlen(bufptr);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'n' : // Output number of chars so far
|
||||||
|
*(va_arg(ap, int *)) = (int)bytes;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Literal character...
|
||||||
|
bytes ++;
|
||||||
|
|
||||||
|
if (bufptr < bufend)
|
||||||
|
*bufptr++ = *format++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nul-terminate the string and return the number of characters needed.
|
||||||
|
if (bufptr < bufend)
|
||||||
|
{
|
||||||
|
// Everything fit in the buffer...
|
||||||
|
*bufptr = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFIO_DEBUG("_pdfio_vsnprintf: Returning %ld \"%s\"\n", (long)bytes, buffer);
|
||||||
|
|
||||||
|
return (bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -36,8 +385,9 @@ pdfioStringCreate(
|
|||||||
pdfio_file_t *pdf, // I - PDF file
|
pdfio_file_t *pdf, // I - PDF file
|
||||||
const char *s) // I - Nul-terminated string
|
const char *s) // I - Nul-terminated string
|
||||||
{
|
{
|
||||||
char *news; // New string
|
char *news; // New string
|
||||||
char **match; // Matching string
|
size_t idx; // Index into strings
|
||||||
|
int diff; // Different
|
||||||
|
|
||||||
|
|
||||||
PDFIO_DEBUG("pdfioStringCreate(pdf=%p, s=\"%s\")\n", pdf, s);
|
PDFIO_DEBUG("pdfioStringCreate(pdf=%p, s=\"%s\")\n", pdf, s);
|
||||||
@ -47,8 +397,17 @@ pdfioStringCreate(
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
// See if the string has already been added...
|
// See if the string has already been added...
|
||||||
if (pdf->num_strings > 0 && (match = (char **)bsearch(&s, pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings)) != NULL)
|
if (pdf->num_strings > 0)
|
||||||
return (*match);
|
{
|
||||||
|
idx = find_string(pdf, s, &diff);
|
||||||
|
if (diff == 0)
|
||||||
|
return (pdf->strings[idx]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
idx = 0;
|
||||||
|
diff = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Not already added, so add it...
|
// Not already added, so add it...
|
||||||
if ((news = strdup(s)) == NULL)
|
if ((news = strdup(s)) == NULL)
|
||||||
@ -69,11 +428,17 @@ pdfioStringCreate(
|
|||||||
pdf->alloc_strings += 128;
|
pdf->alloc_strings += 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Change to insertion sort as needed...
|
// Insert the string...
|
||||||
pdf->strings[pdf->num_strings ++] = news;
|
if (diff > 0)
|
||||||
|
idx ++;
|
||||||
|
|
||||||
if (pdf->num_strings > 1)
|
PDFIO_DEBUG("pdfioStringCreate: Inserting \"%s\" at %u\n", news, (unsigned)idx);
|
||||||
qsort(pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings);
|
|
||||||
|
if (idx < pdf->num_strings)
|
||||||
|
memmove(pdf->strings + idx + 1, pdf->strings + idx, (pdf->num_strings - idx) * sizeof(char *));
|
||||||
|
|
||||||
|
pdf->strings[idx] = news;
|
||||||
|
pdf->num_strings ++;
|
||||||
|
|
||||||
PDFIO_DEBUG("pdfioStringCreate: %lu strings\n", (unsigned long)pdf->num_strings);
|
PDFIO_DEBUG("pdfioStringCreate: %lu strings\n", (unsigned long)pdf->num_strings);
|
||||||
|
|
||||||
@ -124,17 +489,67 @@ _pdfioStringIsAllocated(
|
|||||||
pdfio_file_t *pdf, // I - PDF file
|
pdfio_file_t *pdf, // I - PDF file
|
||||||
const char *s) // I - String
|
const char *s) // I - String
|
||||||
{
|
{
|
||||||
return (pdf->num_strings > 0 && bsearch(&s, pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings) != NULL);
|
int diff; // Difference
|
||||||
|
|
||||||
|
|
||||||
|
if (pdf->num_strings == 0)
|
||||||
|
return (false);
|
||||||
|
|
||||||
|
find_string(pdf, s, &diff);
|
||||||
|
|
||||||
|
return (diff == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 'compare_strings()' - Compare two strings.
|
// 'find_string()' - Find an element in the array.
|
||||||
//
|
//
|
||||||
|
|
||||||
static int // O - Result of comparison
|
static size_t // O - Index of match
|
||||||
compare_strings(char **a, // I - First string
|
find_string(pdfio_file_t *pdf, // I - PDF file
|
||||||
char **b) // I - Second string
|
const char *s, // I - String to find
|
||||||
|
int *rdiff) // O - Difference of match
|
||||||
{
|
{
|
||||||
return (strcmp(*a, *b));
|
size_t left, // Left side of search
|
||||||
|
right, // Right side of search
|
||||||
|
current; // Current element
|
||||||
|
int diff; // Comparison with current element
|
||||||
|
|
||||||
|
|
||||||
|
// Do a binary search for the string...
|
||||||
|
left = 0;
|
||||||
|
right = pdf->num_strings - 1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
current = (left + right) / 2;
|
||||||
|
diff = strcmp(s, pdf->strings[current]);
|
||||||
|
|
||||||
|
if (diff == 0)
|
||||||
|
break;
|
||||||
|
else if (diff < 0)
|
||||||
|
right = current;
|
||||||
|
else
|
||||||
|
left = current;
|
||||||
|
}
|
||||||
|
while ((right - left) > 1);
|
||||||
|
|
||||||
|
if (diff != 0)
|
||||||
|
{
|
||||||
|
// Check the last 1 or 2 elements...
|
||||||
|
if ((diff = strcmp(s, pdf->strings[left])) <= 0)
|
||||||
|
{
|
||||||
|
current = left;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
diff = strcmp(s, pdf->strings[right]);
|
||||||
|
current = right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the closest string and the difference...
|
||||||
|
*rdiff = diff;
|
||||||
|
|
||||||
|
return (current);
|
||||||
}
|
}
|
||||||
|
101
pdfio-token.c
101
pdfio-token.c
@ -1,16 +1,12 @@
|
|||||||
//
|
//
|
||||||
// PDF token parsing functions for PDFio.
|
// PDF token parsing 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
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#include "pdfio-private.h"
|
||||||
|
|
||||||
|
|
||||||
@ -129,9 +125,20 @@ _pdfioTokenGet(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
if (tb->num_tokens > 0)
|
if (tb->num_tokens > 0)
|
||||||
{
|
{
|
||||||
// Yes, return it...
|
// Yes, return it...
|
||||||
|
size_t len; // Length of token
|
||||||
|
|
||||||
tb->num_tokens --;
|
tb->num_tokens --;
|
||||||
strncpy(buffer, tb->tokens[tb->num_tokens], bufsize - 1);
|
|
||||||
buffer[bufsize - 1] = '\0';
|
if ((len = strlen(tb->tokens[tb->num_tokens])) > (bufsize - 1))
|
||||||
|
{
|
||||||
|
// Value too large...
|
||||||
|
PDFIO_DEBUG("_pdfioTokenGet(tb=%p, buffer=%p, bufsize=%u): Token '%s' from stack too large.\n", tb, buffer, (unsigned)bufsize, tb->tokens[tb->num_tokens]);
|
||||||
|
*buffer = '\0';
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buffer, tb->tokens[tb->num_tokens], len);
|
||||||
|
buffer[len] = '\0';
|
||||||
|
|
||||||
PDFIO_DEBUG("_pdfioTokenGet(tb=%p, buffer=%p, bufsize=%u): Popping '%s' from stack.\n", tb, buffer, (unsigned)bufsize, buffer);
|
PDFIO_DEBUG("_pdfioTokenGet(tb=%p, buffer=%p, bufsize=%u): Popping '%s' from stack.\n", tb, buffer, (unsigned)bufsize, buffer);
|
||||||
|
|
||||||
@ -201,9 +208,10 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
*bufend, // End of buffer
|
*bufend, // End of buffer
|
||||||
state = '\0'; // Current state
|
state = '\0'; // Current state
|
||||||
bool saw_nul = false; // Did we see a nul character?
|
bool saw_nul = false; // Did we see a nul character?
|
||||||
|
size_t count = 0; // Number of whitespace/comment bytes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// "state" is:
|
// "state" is:
|
||||||
//
|
//
|
||||||
// - '\0' for idle
|
// - '\0' for idle
|
||||||
@ -222,21 +230,45 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
// Skip leading whitespace...
|
// Skip leading whitespace...
|
||||||
while ((ch = get_char(tb)) != EOF)
|
while ((ch = get_char(tb)) != EOF)
|
||||||
{
|
{
|
||||||
|
count ++;
|
||||||
|
|
||||||
if (ch == '%')
|
if (ch == '%')
|
||||||
{
|
{
|
||||||
// Skip comment
|
// Skip comment
|
||||||
|
PDFIO_DEBUG("_pdfioTokenRead: Skipping comment...\n");
|
||||||
while ((ch = get_char(tb)) != EOF)
|
while ((ch = get_char(tb)) != EOF)
|
||||||
{
|
{
|
||||||
|
count ++;
|
||||||
|
|
||||||
if (ch == '\n' || ch == '\r')
|
if (ch == '\n' || ch == '\r')
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
else if (count > 2048)
|
||||||
|
{
|
||||||
|
_pdfioFileError(tb->pdf, "Comment too long.");
|
||||||
|
*bufptr = '\0';
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!isspace(ch))
|
else if (!isspace(ch))
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
else if (count > 2048)
|
||||||
|
{
|
||||||
|
_pdfioFileError(tb->pdf, "Too much whitespace.");
|
||||||
|
*bufptr = '\0';
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ch == EOF)
|
if (ch == EOF)
|
||||||
|
{
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
// Check for delimiters...
|
// Check for delimiters...
|
||||||
if (strchr(PDFIO_DELIM_CHARS, ch) != NULL)
|
if (strchr(PDFIO_DELIM_CHARS, ch) != NULL)
|
||||||
@ -256,6 +288,8 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
*bufptr++ = (char)ch;
|
*bufptr++ = (char)ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PDFIO_DEBUG("_pdfioTokenRead: state='%c'\n", state);
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case '(' : // Literal string
|
case '(' : // Literal string
|
||||||
@ -347,6 +381,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
{
|
{
|
||||||
// Out of space
|
// Out of space
|
||||||
_pdfioFileError(tb->pdf, "Token too large.");
|
_pdfioFileError(tb->pdf, "Token too large.");
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -354,6 +389,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
if (ch != ')')
|
if (ch != ')')
|
||||||
{
|
{
|
||||||
_pdfioFileError(tb->pdf, "Unterminated string literal.");
|
_pdfioFileError(tb->pdf, "Unterminated string literal.");
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,6 +409,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
{
|
{
|
||||||
// Out of space...
|
// Out of space...
|
||||||
_pdfioFileError(tb->pdf, "Token too large.");
|
_pdfioFileError(tb->pdf, "Token too large.");
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,9 +443,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
{
|
{
|
||||||
// Out of space...
|
// Out of space...
|
||||||
_pdfioFileError(tb->pdf, "Token too large.");
|
_pdfioFileError(tb->pdf, "Token too large.");
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ch == '\r')
|
||||||
|
{
|
||||||
|
// Look for a trailing LF
|
||||||
|
if ((ch = get_char(tb)) != EOF && ch != '\n')
|
||||||
|
tb->bufptr --;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'N' : // number
|
case 'N' : // number
|
||||||
@ -417,6 +462,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
if (!isdigit(ch) && ch != '.')
|
if (!isdigit(ch) && ch != '.')
|
||||||
{
|
{
|
||||||
// End of number...
|
// End of number...
|
||||||
|
PDFIO_DEBUG("_pdfioTokenRead: End of number with ch=0x%02x\n", ch);
|
||||||
tb->bufptr --;
|
tb->bufptr --;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -429,6 +475,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
{
|
{
|
||||||
// Out of space...
|
// Out of space...
|
||||||
_pdfioFileError(tb->pdf, "Token too large.");
|
_pdfioFileError(tb->pdf, "Token too large.");
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,12 +502,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
if (!isxdigit(tch & 255))
|
if (!isxdigit(tch & 255))
|
||||||
{
|
{
|
||||||
_pdfioFileError(tb->pdf, "Bad # escape in name.");
|
_pdfioFileError(tb->pdf, "Bad # escape in name.");
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
else if (isdigit(tch))
|
else if (isdigit(tch))
|
||||||
|
{
|
||||||
ch = ((ch & 255) << 4) | (tch - '0');
|
ch = ((ch & 255) << 4) | (tch - '0');
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
ch = ((ch & 255) << 4) | (tolower(tch) - 'a' + 10);
|
ch = ((ch & 255) << 4) | (tolower(tch) - 'a' + 10);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,9 +524,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
{
|
{
|
||||||
// Out of space
|
// Out of space
|
||||||
_pdfioFileError(tb->pdf, "Token too large.");
|
_pdfioFileError(tb->pdf, "Token too large.");
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bufptr == (buffer + 1))
|
||||||
|
{
|
||||||
|
_pdfioFileError(tb->pdf, "Empty name.");
|
||||||
|
*bufptr = '\0';
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '<' : // Potential hex string
|
case '<' : // Potential hex string
|
||||||
@ -484,12 +544,22 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
*bufptr++ = (char)ch;
|
*bufptr++ = (char)ch;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else if (ch == '>')
|
||||||
|
{
|
||||||
|
// Issue #46: Empty hex string from Microsoft PDF generator; treat as
|
||||||
|
// empty literal string...
|
||||||
|
*buffer = '(';
|
||||||
|
break;
|
||||||
|
}
|
||||||
else if (!isspace(ch & 255) && !isxdigit(ch & 255))
|
else if (!isspace(ch & 255) && !isxdigit(ch & 255))
|
||||||
{
|
{
|
||||||
_pdfioFileError(tb->pdf, "Syntax error: '<%c'", ch);
|
_pdfioFileError(tb->pdf, "Syntax error: '<%c'", ch);
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (isxdigit(ch))
|
if (isxdigit(ch))
|
||||||
@ -498,25 +568,39 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
{
|
{
|
||||||
// Hex digit
|
// Hex digit
|
||||||
*bufptr++ = (char)ch;
|
*bufptr++ = (char)ch;
|
||||||
|
count = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Too large
|
// Too large
|
||||||
_pdfioFileError(tb->pdf, "Token too large.");
|
_pdfioFileError(tb->pdf, "Token too large.");
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!isspace(ch))
|
else if (!isspace(ch))
|
||||||
{
|
{
|
||||||
_pdfioFileError(tb->pdf, "Invalid hex string character '%c'.", ch);
|
_pdfioFileError(tb->pdf, "Invalid hex string character '%c'.", ch);
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count ++;
|
||||||
|
if (count > 2048)
|
||||||
|
{
|
||||||
|
_pdfioFileError(tb->pdf, "Too much whitespace.");
|
||||||
|
*bufptr = '\0';
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
while ((ch = get_char(tb)) != EOF && ch != '>');
|
while ((ch = get_char(tb)) != EOF && ch != '>');
|
||||||
|
|
||||||
if (ch == EOF)
|
if (ch == EOF)
|
||||||
{
|
{
|
||||||
_pdfioFileError(tb->pdf, "Unterminated hex string.");
|
_pdfioFileError(tb->pdf, "Unterminated hex string.");
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -529,6 +613,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
_pdfioFileError(tb->pdf, "Syntax error: '>%c'.", ch);
|
_pdfioFileError(tb->pdf, "Syntax error: '>%c'.", ch);
|
||||||
|
*bufptr = '\0';
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
249
pdfio-value.c
249
pdfio-value.c
@ -1,17 +1,20 @@
|
|||||||
//
|
//
|
||||||
// PDF value functions for PDFio.
|
// PDF value functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021 by Michael R Sweet.
|
// Copyright © 2021-2024 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// 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.
|
// '_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
|
size_t depth) // I - Depth of value
|
||||||
{
|
{
|
||||||
char token[32768]; // Token buffer
|
char token[32768]; // Token buffer
|
||||||
|
time_t timeval; // Date/time value
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
static const char * const valtypes[] =
|
static const char * const valtypes[] =
|
||||||
{
|
{
|
||||||
@ -219,7 +318,6 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
|
|
||||||
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v);
|
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v);
|
||||||
(void)obj; // TODO: Implement decryption
|
|
||||||
|
|
||||||
if (!_pdfioTokenGet(tb, token, sizeof(token)))
|
if (!_pdfioTokenGet(tb, token, sizeof(token)))
|
||||||
return (NULL);
|
return (NULL);
|
||||||
@ -250,72 +348,10 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
|
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
|
||||||
return (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:
|
v->type = PDFIO_VALTYPE_DATE;
|
||||||
//
|
v->value.date = timeval;
|
||||||
// (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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (token[0] == '(')
|
else if (token[0] == '(')
|
||||||
{
|
{
|
||||||
@ -461,7 +497,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
// If we get here, we have a number...
|
// If we get here, we have a number...
|
||||||
v->type = PDFIO_VALTYPE_NUMBER;
|
v->type = PDFIO_VALTYPE_NUMBER;
|
||||||
v->value.number = (double)strtod(token, NULL);
|
v->value.number = _pdfio_strtod(pdf, token);
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "true") || !strcmp(token, "false"))
|
else if (!strcmp(token, "true") || !strcmp(token, "false"))
|
||||||
{
|
{
|
||||||
@ -702,3 +738,76 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
return (false);
|
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);
|
||||||
|
}
|
||||||
|
35
pdfio.h
35
pdfio.h
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// Public header file for PDFio.
|
// Public header file for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021 by Michael R Sweet.
|
// Copyright © 2021-2024 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
@ -9,27 +9,23 @@
|
|||||||
|
|
||||||
#ifndef PDFIO_H
|
#ifndef PDFIO_H
|
||||||
# define PDFIO_H
|
# define PDFIO_H
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
# include <stdio.h>
|
# include <stdio.h>
|
||||||
# include <stdlib.h>
|
# include <stdlib.h>
|
||||||
# include <stdbool.h>
|
# include <stdbool.h>
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
# include <time.h>
|
# include <time.h>
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// C++ magic...
|
|
||||||
//
|
|
||||||
|
|
||||||
# ifdef __cplusplus
|
# ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
# endif // __cplusplus
|
# endif // __cplusplus
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Version number...
|
||||||
|
//
|
||||||
|
|
||||||
|
# define PDFIO_VERSION "1.3.0"
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Visibility and other annotations...
|
// Visibility and other annotations...
|
||||||
//
|
//
|
||||||
@ -37,9 +33,11 @@ extern "C" {
|
|||||||
# if defined(__has_extension) || defined(__GNUC__)
|
# if defined(__has_extension) || defined(__GNUC__)
|
||||||
# define _PDFIO_PUBLIC __attribute__ ((visibility("default")))
|
# define _PDFIO_PUBLIC __attribute__ ((visibility("default")))
|
||||||
# define _PDFIO_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b)))
|
# define _PDFIO_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b)))
|
||||||
|
# define _PDFIO_DEPRECATED __attribute__ ((deprecated)) _PDFIO_PUBLIC
|
||||||
# else
|
# else
|
||||||
# define _PDFIO_PUBLIC
|
# define _PDFIO_PUBLIC
|
||||||
# define _PDFIO_FORMAT(a,b)
|
# define _PDFIO_FORMAT(a,b)
|
||||||
|
# define _PDFIO_DEPRECATED
|
||||||
# endif // __has_extension || __GNUC__
|
# endif // __has_extension || __GNUC__
|
||||||
|
|
||||||
|
|
||||||
@ -55,6 +53,8 @@ typedef struct _pdfio_array_s pdfio_array_t;
|
|||||||
// Array of PDF values
|
// Array of PDF values
|
||||||
typedef struct _pdfio_dict_s pdfio_dict_t;
|
typedef struct _pdfio_dict_s pdfio_dict_t;
|
||||||
// Key/value dictionary
|
// Key/value dictionary
|
||||||
|
typedef bool (*pdfio_dict_cb_t)(pdfio_dict_t *dict, const char *key, void *cb_data);
|
||||||
|
// Dictionary iterator callback
|
||||||
typedef struct _pdfio_file_s pdfio_file_t;
|
typedef struct _pdfio_file_s pdfio_file_t;
|
||||||
// PDF file
|
// PDF file
|
||||||
typedef bool (*pdfio_error_cb_t)(pdfio_file_t *pdf, const char *message, void *data);
|
typedef bool (*pdfio_error_cb_t)(pdfio_file_t *pdf, const char *message, void *data);
|
||||||
@ -65,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_40, // 40-bit RC4 encryption (PDF 1.3)
|
||||||
PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4)
|
PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4)
|
||||||
PDFIO_ENCRYPTION_AES_128, // 128-bit AES encryption (PDF 1.6)
|
PDFIO_ENCRYPTION_AES_128, // 128-bit AES encryption (PDF 1.6)
|
||||||
PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0)
|
PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0) @exclude all@
|
||||||
} pdfio_encryption_t;
|
} pdfio_encryption_t;
|
||||||
typedef enum pdfio_filter_e // Compression/decompression filters for streams
|
typedef enum pdfio_filter_e // Compression/decompression filters for streams
|
||||||
{
|
{
|
||||||
@ -165,6 +165,7 @@ extern pdfio_obj_t *pdfioDictGetObj(pdfio_dict_t *dict, const char *key) _PDFIO_
|
|||||||
extern pdfio_rect_t *pdfioDictGetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *rect) _PDFIO_PUBLIC;
|
extern pdfio_rect_t *pdfioDictGetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *rect) _PDFIO_PUBLIC;
|
||||||
extern const char *pdfioDictGetString(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
|
extern const char *pdfioDictGetString(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
|
||||||
extern pdfio_valtype_t pdfioDictGetType(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
|
extern pdfio_valtype_t pdfioDictGetType(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
|
||||||
|
extern void pdfioDictIterateKeys(pdfio_dict_t *dict, pdfio_dict_cb_t cb, void *cb_data) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioDictSetArray(pdfio_dict_t *dict, const char *key, pdfio_array_t *value) _PDFIO_PUBLIC;
|
extern bool pdfioDictSetArray(pdfio_dict_t *dict, const char *key, pdfio_array_t *value) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioDictSetBinary(pdfio_dict_t *dict, const char *key, const unsigned char *value, size_t valuelen) _PDFIO_PUBLIC;
|
extern bool pdfioDictSetBinary(pdfio_dict_t *dict, const char *key, const unsigned char *value, size_t valuelen) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioDictSetBoolean(pdfio_dict_t *dict, const char *key, bool value) _PDFIO_PUBLIC;
|
extern bool pdfioDictSetBoolean(pdfio_dict_t *dict, const char *key, bool value) _PDFIO_PUBLIC;
|
||||||
@ -181,12 +182,16 @@ extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char
|
|||||||
extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
||||||
extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
|
extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
|
||||||
extern pdfio_obj_t *pdfioFileCreateArrayObj(pdfio_file_t *pdf, pdfio_array_t *array) _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_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;
|
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?
|
// 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_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 pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC;
|
||||||
extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
||||||
|
extern pdfio_dict_t *pdfioFileGetCatalog(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
||||||
extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
||||||
extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
||||||
extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
||||||
@ -240,10 +245,6 @@ extern char *pdfioStringCreate(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC
|
|||||||
extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_PUBLIC;
|
extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_PUBLIC;
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// C++ magic...
|
|
||||||
//
|
|
||||||
|
|
||||||
# ifdef __cplusplus
|
# ifdef __cplusplus
|
||||||
}
|
}
|
||||||
# endif // __cplusplus
|
# endif // __cplusplus
|
||||||
|
13
pdfio.pc.in
13
pdfio.pc.in
@ -1,6 +1,13 @@
|
|||||||
|
prefix=@prefix@
|
||||||
|
exec_prefix=@exec_prefix@
|
||||||
|
libdir=@libdir@
|
||||||
|
includedir=@includedir@
|
||||||
|
|
||||||
Name: pdfio
|
Name: pdfio
|
||||||
Description: PDF read/write library
|
Description: PDF read/write library
|
||||||
|
Version: @PDFIO_VERSION@
|
||||||
URL: https://www.msweet.org/pdfio
|
URL: https://www.msweet.org/pdfio
|
||||||
Requires: zlib >= 1.0
|
Requires: @PKGCONFIG_REQUIRES@
|
||||||
Libs: -L${prefix}/lib -lpdfio -lm
|
Libs: @PKGCONFIG_LIBS@
|
||||||
Cflags: -I${prefix}/include
|
Libs.private: @PKGCONFIG_LIBS_PRIVATE@
|
||||||
|
Cflags: @PKGCONFIG_CFLAGS@
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>PDFIO_VERSION="1.0.1";WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@ -101,7 +101,7 @@
|
|||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>PDFIO_VERSION="1.0.1";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@ -115,7 +115,7 @@
|
|||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>PDFIO_VERSION="1.0.1";_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@ -130,7 +130,7 @@
|
|||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>PDFIO_VERSION="1.0.1";NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
@ -372,7 +372,7 @@
|
|||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 1.0.1;
|
CURRENT_PROJECT_VERSION = 1.1.2;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
@ -381,7 +381,6 @@
|
|||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"DEBUG=1",
|
"DEBUG=1",
|
||||||
"'PDFIO_VERSION=\"$(CURRENT_PROJECT_VERSION)\"'",
|
|
||||||
);
|
);
|
||||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
@ -397,7 +396,7 @@
|
|||||||
GCC_WARN_UNUSED_LABEL = YES;
|
GCC_WARN_UNUSED_LABEL = YES;
|
||||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
@ -450,7 +449,7 @@
|
|||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 1.0.1;
|
CURRENT_PROJECT_VERSION = 1.1.2;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
@ -459,7 +458,6 @@
|
|||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"'PDFIO_VERSION=\"$(CURRENT_PROJECT_VERSION)\"'",
|
|
||||||
);
|
);
|
||||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
@ -475,7 +473,7 @@
|
|||||||
GCC_WARN_UNUSED_LABEL = YES;
|
GCC_WARN_UNUSED_LABEL = YES;
|
||||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
RUN_CLANG_STATIC_ANALYZER = YES;
|
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||||
@ -515,7 +513,7 @@
|
|||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpdfio;
|
PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpdfio;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -529,7 +527,7 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = "";
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpdfio;
|
PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpdfio;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
13
pdfio1.def
13
pdfio1.def
@ -1,5 +1,5 @@
|
|||||||
LIBRARY pdfio1
|
LIBRARY pdfio1
|
||||||
VERSION 1.0
|
VERSION 1.2
|
||||||
EXPORTS
|
EXPORTS
|
||||||
_pdfioArrayDebug
|
_pdfioArrayDebug
|
||||||
_pdfioArrayDelete
|
_pdfioArrayDelete
|
||||||
@ -47,7 +47,9 @@ _pdfioFileSeek
|
|||||||
_pdfioFileTell
|
_pdfioFileTell
|
||||||
_pdfioFileWrite
|
_pdfioFileWrite
|
||||||
_pdfioObjDelete
|
_pdfioObjDelete
|
||||||
|
_pdfioObjGetExtension
|
||||||
_pdfioObjLoad
|
_pdfioObjLoad
|
||||||
|
_pdfioObjSetExtension
|
||||||
_pdfioStreamCreate
|
_pdfioStreamCreate
|
||||||
_pdfioStreamOpen
|
_pdfioStreamOpen
|
||||||
_pdfioStringIsAllocated
|
_pdfioStringIsAllocated
|
||||||
@ -101,6 +103,7 @@ pdfioContentPathClose
|
|||||||
pdfioContentPathCurve
|
pdfioContentPathCurve
|
||||||
pdfioContentPathCurve13
|
pdfioContentPathCurve13
|
||||||
pdfioContentPathCurve23
|
pdfioContentPathCurve23
|
||||||
|
pdfioContentPathEnd
|
||||||
pdfioContentPathLineTo
|
pdfioContentPathLineTo
|
||||||
pdfioContentPathMoveTo
|
pdfioContentPathMoveTo
|
||||||
pdfioContentPathRect
|
pdfioContentPathRect
|
||||||
@ -135,8 +138,12 @@ pdfioContentSetTextXScaling
|
|||||||
pdfioContentStroke
|
pdfioContentStroke
|
||||||
pdfioContentTextBegin
|
pdfioContentTextBegin
|
||||||
pdfioContentTextEnd
|
pdfioContentTextEnd
|
||||||
|
pdfioContentTextMeasure
|
||||||
pdfioContentTextMoveLine
|
pdfioContentTextMoveLine
|
||||||
pdfioContentTextMoveTo
|
pdfioContentTextMoveTo
|
||||||
|
pdfioContentTextNewLine
|
||||||
|
pdfioContentTextNewLineShow
|
||||||
|
pdfioContentTextNewLineShowf
|
||||||
pdfioContentTextNextLine
|
pdfioContentTextNextLine
|
||||||
pdfioContentTextShow
|
pdfioContentTextShow
|
||||||
pdfioContentTextShowJustified
|
pdfioContentTextShowJustified
|
||||||
@ -154,6 +161,7 @@ pdfioDictGetObj
|
|||||||
pdfioDictGetRect
|
pdfioDictGetRect
|
||||||
pdfioDictGetString
|
pdfioDictGetString
|
||||||
pdfioDictGetType
|
pdfioDictGetType
|
||||||
|
pdfioDictIterateKeys
|
||||||
pdfioDictSetArray
|
pdfioDictSetArray
|
||||||
pdfioDictSetBinary
|
pdfioDictSetBinary
|
||||||
pdfioDictSetBoolean
|
pdfioDictSetBoolean
|
||||||
@ -174,9 +182,12 @@ pdfioFileCreateFontObjFromFile
|
|||||||
pdfioFileCreateICCObjFromFile
|
pdfioFileCreateICCObjFromFile
|
||||||
pdfioFileCreateImageObjFromData
|
pdfioFileCreateImageObjFromData
|
||||||
pdfioFileCreateImageObjFromFile
|
pdfioFileCreateImageObjFromFile
|
||||||
|
pdfioFileCreateNumberObj
|
||||||
pdfioFileCreateObj
|
pdfioFileCreateObj
|
||||||
pdfioFileCreateOutput
|
pdfioFileCreateOutput
|
||||||
pdfioFileCreatePage
|
pdfioFileCreatePage
|
||||||
|
pdfioFileCreateStringObj
|
||||||
|
pdfioFileCreateTemporary
|
||||||
pdfioFileFindObj
|
pdfioFileFindObj
|
||||||
pdfioFileGetAuthor
|
pdfioFileGetAuthor
|
||||||
pdfioFileGetCreationDate
|
pdfioFileGetCreationDate
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<metadata>
|
<metadata>
|
||||||
<id>pdfio_native</id>
|
<id>pdfio_native</id>
|
||||||
<title>PDFio Library for VS2019+</title>
|
<title>PDFio Library for VS2019+</title>
|
||||||
<version>1.0.1</version>
|
<version>1.3.0</version>
|
||||||
<authors>Michael R Sweet</authors>
|
<authors>Michael R Sweet</authors>
|
||||||
<owners>michaelrsweet</owners>
|
<owners>michaelrsweet</owners>
|
||||||
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
|
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
|
||||||
@ -13,10 +13,10 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<description>PDFio Library for VS2019+</description>
|
<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>
|
<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-2022 by Michael R Sweet</copyright>
|
<copyright>Copyright © 2019-2024 by Michael R Sweet</copyright>
|
||||||
<tags>pdf file native</tags>
|
<tags>pdf file native</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="pdfio_native.redist" version="1.0.1" />
|
<dependency id="pdfio_native.redist" version="1.3.0" />
|
||||||
<dependency id="zlib_native.redist" version="1.2.11" />
|
<dependency id="zlib_native.redist" version="1.2.11" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<metadata>
|
<metadata>
|
||||||
<id>pdfio_native.redist</id>
|
<id>pdfio_native.redist</id>
|
||||||
<title>PDFio Library for VS2019+</title>
|
<title>PDFio Library for VS2019+</title>
|
||||||
<version>1.0.1</version>
|
<version>1.3.0</version>
|
||||||
<authors>Michael R Sweet</authors>
|
<authors>Michael R Sweet</authors>
|
||||||
<owners>michaelrsweet</owners>
|
<owners>michaelrsweet</owners>
|
||||||
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
|
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
|
||||||
@ -13,8 +13,11 @@
|
|||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<description>PDFio Library for VS2019+</description>
|
<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>
|
<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-2022 by Michael R Sweet</copyright>
|
<copyright>Copyright © 2019-2024 by Michael R Sweet</copyright>
|
||||||
<tags>pdf file native</tags>
|
<tags>pdf file native</tags>
|
||||||
|
<dependencies>
|
||||||
|
<dependency id="zlib_native.redist" version="1.2.11" />
|
||||||
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
<file src="doc\pdfio-128.png" target="build\native" />
|
<file src="doc\pdfio-128.png" target="build\native" />
|
||||||
|
16
runtests.bat
Normal file
16
runtests.bat
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
:: Script to run unit test program
|
||||||
|
::
|
||||||
|
:: Usage:
|
||||||
|
::
|
||||||
|
:: .\runtests.bat x64\{Debug|Release}
|
||||||
|
::
|
||||||
|
|
||||||
|
:: Copy dependent DLLs to the named build directory
|
||||||
|
echo Copying DLLs
|
||||||
|
copy packages\zlib_native.redist.1.2.11\build\native\bin\x64\Debug\*.dll %1
|
||||||
|
copy packages\zlib_native.redist.1.2.11\build\native\bin\x64\Release\*.dll %1
|
||||||
|
|
||||||
|
:: Run unit test program
|
||||||
|
echo Running %1\testpdfio.exe
|
||||||
|
cd %1
|
||||||
|
testpdfio.exe
|
381
testpdfio.c
381
testpdfio.c
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// Test program for PDFio.
|
// Test program for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021 by Michael R Sweet.
|
// Copyright © 2021-2024 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
@ -10,16 +10,13 @@
|
|||||||
//
|
//
|
||||||
// ./testpdfio
|
// ./testpdfio
|
||||||
//
|
//
|
||||||
// ./testpdfio FILENAME [OBJECT-NUMBER] [FILENAME [OBJECT-NUMBER]] ...
|
// ./testpdfio [--verbose] FILENAME [OBJECT-NUMBER] [FILENAME [OBJECT-NUMBER]] ...
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "pdfio-private.h"
|
#include "pdfio-private.h"
|
||||||
#include "pdfio-content.h"
|
#include "pdfio-content.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <locale.h>
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
# define M_PI 3.14159265358979323846264338327950288
|
# define M_PI 3.14159265358979323846264338327950288
|
||||||
#endif // M_PI
|
#endif // M_PI
|
||||||
@ -34,6 +31,7 @@ static int do_test_file(const char *filename, int objnum, bool verbose);
|
|||||||
static int do_unit_tests(void);
|
static int do_unit_tests(void);
|
||||||
static int draw_image(pdfio_stream_t *st, const char *name, double x, double y, double w, double h, const char *label);
|
static int draw_image(pdfio_stream_t *st, const char *name, double x, double y, double w, double h, const char *label);
|
||||||
static bool error_cb(pdfio_file_t *pdf, const char *message, bool *error);
|
static bool error_cb(pdfio_file_t *pdf, const char *message, bool *error);
|
||||||
|
static bool iterate_cb(pdfio_dict_t *dict, const char *key, void *cb_data);
|
||||||
static ssize_t output_cb(int *fd, const void *buffer, size_t bytes);
|
static ssize_t output_cb(int *fd, const void *buffer, size_t bytes);
|
||||||
static const char *password_cb(void *data, const char *filename);
|
static const char *password_cb(void *data, const char *filename);
|
||||||
static int read_unit_file(const char *filename, size_t num_pages, size_t first_image, bool is_output);
|
static int read_unit_file(const char *filename, size_t num_pages, size_t first_image, bool is_output);
|
||||||
@ -43,14 +41,14 @@ 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_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_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_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 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 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);
|
static int write_images_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
|
||||||
static int write_jpeg_test(pdfio_file_t *pdf, const char *title, int number, pdfio_obj_t *font, pdfio_obj_t *image);
|
static int write_jpeg_test(pdfio_file_t *pdf, const char *title, int number, pdfio_obj_t *font, pdfio_obj_t *image);
|
||||||
static int write_png_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
|
static int write_png_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
|
||||||
static int write_text_test(pdfio_file_t *pdf, int first_page, pdfio_obj_t *font, const char *filename);
|
static int write_text_test(pdfio_file_t *pdf, int first_page, pdfio_obj_t *font, const char *filename);
|
||||||
static int write_unit_file(pdfio_file_t *inpdf, pdfio_file_t *outpdf, size_t *num_pages, size_t *first_image);
|
static int write_unit_file(pdfio_file_t *inpdf, const char *outname, pdfio_file_t *outpdf, size_t *num_pages, size_t *first_image);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -64,6 +62,8 @@ main(int argc, // I - Number of command-line arguments
|
|||||||
int ret = 0; // Return value
|
int ret = 0; // Return value
|
||||||
|
|
||||||
|
|
||||||
|
fprintf(stderr, "testpdfio: Test locale is \"%s\".\n", setlocale(LC_ALL, getenv("LANG")));
|
||||||
|
|
||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
{
|
{
|
||||||
int i; // Looping var
|
int i; // Looping var
|
||||||
@ -405,7 +405,7 @@ do_test_file(const char *filename, // I - PDF filename
|
|||||||
|
|
||||||
filter = pdfioDictGetName(dict, "Filter");
|
filter = pdfioDictGetName(dict, "Filter");
|
||||||
|
|
||||||
if ((st = pdfioObjOpenStream(obj, (filter && !strcmp(filter, "FlateDecode")) ? PDFIO_FILTER_FLATE : PDFIO_FILTER_NONE)) == NULL)
|
if ((st = pdfioObjOpenStream(obj, filter && !strcmp(filter, "FlateDecode"))) == NULL)
|
||||||
{
|
{
|
||||||
_pdfioValueDebug(&obj->value, stdout);
|
_pdfioValueDebug(&obj->value, stdout);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
@ -454,7 +454,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -506,6 +506,9 @@ do_unit_tests(void)
|
|||||||
_pdfio_value_t value; // Value
|
_pdfio_value_t value; // Value
|
||||||
size_t first_image, // First image object
|
size_t first_image, // First image object
|
||||||
num_pages; // Number of pages written
|
num_pages; // Number of pages written
|
||||||
|
char temppdf[1024]; // Temporary PDF file
|
||||||
|
pdfio_dict_t *dict; // Test dictionary
|
||||||
|
int count = 0; // Number of key/value pairs
|
||||||
static const char *complex_dict = // Complex dictionary value
|
static const char *complex_dict = // Complex dictionary value
|
||||||
"<</Annots 5457 0 R/Contents 5469 0 R/CropBox[0 0 595.4 842]/Group 725 0 R"
|
"<</Annots 5457 0 R/Contents 5469 0 R/CropBox[0 0 595.4 842]/Group 725 0 R"
|
||||||
"/MediaBox[0 0 595.4 842]/Parent 23513 0 R/Resources<</ColorSpace<<"
|
"/MediaBox[0 0 595.4 842]/Parent 23513 0 R/Resources<</ColorSpace<<"
|
||||||
@ -967,6 +970,41 @@ do_unit_tests(void)
|
|||||||
|
|
||||||
// TODO: Test for known values in this test file.
|
// TODO: Test for known values in this test file.
|
||||||
|
|
||||||
|
// Test dictionary APIs
|
||||||
|
fputs("pdfioDictCreate: ", stdout);
|
||||||
|
if ((dict = pdfioDictCreate(inpdf)) != NULL)
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
|
||||||
|
fputs("pdfioDictSet*: ", stdout);
|
||||||
|
if (pdfioDictSetBoolean(dict, "Boolean", true) && pdfioDictSetName(dict, "Name", "Name") && pdfioDictSetNumber(dict, "Number", 42.0) && pdfioDictSetString(dict, "String", "String"))
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs("pdfioDictIterateKeys: ", stdout);
|
||||||
|
pdfioDictIterateKeys(dict, iterate_cb, &count);
|
||||||
|
if (count == 4)
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("FAIL (got %d, expected 4)\n", count);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
// Test the value parsers for edge cases...
|
// Test the value parsers for edge cases...
|
||||||
fputs("_pdfioValueRead(complex_dict): ", stdout);
|
fputs("_pdfioValueRead(complex_dict): ", stdout);
|
||||||
s = complex_dict;
|
s = complex_dict;
|
||||||
@ -1006,7 +1044,7 @@ do_unit_tests(void)
|
|||||||
else
|
else
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
|
if (write_unit_file(inpdf, "testpdfio-out.pdf", outpdf, &num_pages, &first_image))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
|
if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
|
||||||
@ -1025,7 +1063,7 @@ do_unit_tests(void)
|
|||||||
else
|
else
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
|
if (write_unit_file(inpdf, "testpdfio-out2.pdf", outpdf, &num_pages, &first_image))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
close(outfd);
|
close(outfd);
|
||||||
@ -1046,7 +1084,7 @@ do_unit_tests(void)
|
|||||||
else
|
else
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
|
if (write_unit_file(inpdf, "testpdfio-rc4.pdf", outpdf, &num_pages, &first_image))
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
if (read_unit_file("testpdfio-rc4.pdf", num_pages, first_image, false))
|
if (read_unit_file("testpdfio-rc4.pdf", num_pages, first_image, false))
|
||||||
@ -1065,7 +1103,7 @@ do_unit_tests(void)
|
|||||||
else
|
else
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
|
if (write_unit_file(inpdf, "testpdfio-rc4p.pdf", outpdf, &num_pages, &first_image))
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
if (read_unit_file("testpdfio-rc4p.pdf", num_pages, first_image, false))
|
if (read_unit_file("testpdfio-rc4p.pdf", num_pages, first_image, false))
|
||||||
@ -1083,7 +1121,7 @@ do_unit_tests(void)
|
|||||||
else
|
else
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
|
if (write_unit_file(inpdf, "testpdfio-aes.pdf", outpdf, &num_pages, &first_image))
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
if (read_unit_file("testpdfio-aes.pdf", num_pages, first_image, false))
|
if (read_unit_file("testpdfio-aes.pdf", num_pages, first_image, false))
|
||||||
@ -1101,12 +1139,24 @@ do_unit_tests(void)
|
|||||||
else
|
else
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
|
if (write_unit_file(inpdf, "testpdfio-aesp.pdf", outpdf, &num_pages, &first_image))
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
if (read_unit_file("testpdfio-aesp.pdf", num_pages, first_image, false))
|
if (read_unit_file("testpdfio-aesp.pdf", num_pages, first_image, false))
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
|
fputs("pdfioFileCreateTemporary: ", stdout);
|
||||||
|
if ((outpdf = pdfioFileCreateTemporary(temppdf, sizeof(temppdf), NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
|
||||||
|
printf("PASS (%s)\n", temppdf);
|
||||||
|
else
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
if (write_unit_file(inpdf, "<temporary>", outpdf, &num_pages, &first_image))
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
if (read_unit_file(temppdf, num_pages, first_image, false))
|
||||||
|
return (1);
|
||||||
|
|
||||||
pdfioFileClose(inpdf);
|
pdfioFileClose(inpdf);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
@ -1199,6 +1249,27 @@ error_cb(pdfio_file_t *pdf, // I - PDF file
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'iterate_cb()' - Test pdfioDictIterateKeys function.
|
||||||
|
//
|
||||||
|
|
||||||
|
static bool // O - `true` to continue, `false` to stop
|
||||||
|
iterate_cb(pdfio_dict_t *dict, // I - Dictionary
|
||||||
|
const char *key, // I - Key
|
||||||
|
void *cb_data) // I - Callback data
|
||||||
|
{
|
||||||
|
int *count = (int *)cb_data; // Pointer to counter
|
||||||
|
|
||||||
|
|
||||||
|
if (!dict || !key || !cb_data)
|
||||||
|
return (false);
|
||||||
|
|
||||||
|
(*count)++;
|
||||||
|
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 'output_cb()' - Write output to a file.
|
// 'output_cb()' - Write output to a file.
|
||||||
//
|
//
|
||||||
@ -1237,7 +1308,9 @@ read_unit_file(const char *filename, // I - File to read
|
|||||||
bool is_output) // I - File written with output callback?
|
bool is_output) // I - File written with output callback?
|
||||||
{
|
{
|
||||||
pdfio_file_t *pdf; // PDF file
|
pdfio_file_t *pdf; // PDF file
|
||||||
|
pdfio_dict_t *catalog; // Catalog dictionary
|
||||||
size_t i; // Looping var
|
size_t i; // Looping var
|
||||||
|
const char *s; // String
|
||||||
bool error = false; // Error callback data
|
bool error = false; // Error callback data
|
||||||
|
|
||||||
|
|
||||||
@ -1248,6 +1321,164 @@ read_unit_file(const char *filename, // I - File to read
|
|||||||
else
|
else
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
|
// Get the root object/catalog dictionary
|
||||||
|
fputs("pdfioFileGetCatalog: ", stdout);
|
||||||
|
if ((catalog = pdfioFileGetCatalog(pdf)) != NULL)
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected dictionary)");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify some catalog values...
|
||||||
|
fputs("pdfioDictGetName(PageLayout): ", stdout);
|
||||||
|
if ((s = pdfioDictGetName(catalog, "PageLayout")) != NULL && !strcmp(s, "SinglePage"))
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else if (s)
|
||||||
|
{
|
||||||
|
printf("FAIL (got '%s', expected 'SinglePage')\n", s);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected 'SinglePage')");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs("pdfioDictGetName(PageLayout): ", stdout);
|
||||||
|
if ((s = pdfioDictGetName(catalog, "PageLayout")) != NULL && !strcmp(s, "SinglePage"))
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else if (s)
|
||||||
|
{
|
||||||
|
printf("FAIL (got '%s', expected 'SinglePage')\n", s);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected 'SinglePage')");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs("pdfioDictGetName(PageMode): ", stdout);
|
||||||
|
if ((s = pdfioDictGetName(catalog, "PageMode")) != NULL && !strcmp(s, "UseThumbs"))
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else if (s)
|
||||||
|
{
|
||||||
|
printf("FAIL (got '%s', expected 'UseThumbs')\n", s);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected 'UseThumbs')");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs("pdfioDictGetString(Lang): ", stdout);
|
||||||
|
if ((s = pdfioDictGetString(catalog, "Lang")) != NULL && !strcmp(s, "en"))
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else if (s)
|
||||||
|
{
|
||||||
|
printf("FAIL (got '%s', expected 'en')\n", s);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected 'en')");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify metadata...
|
||||||
|
fputs("pdfioFileGetAuthor: ", stdout);
|
||||||
|
if ((s = pdfioFileGetAuthor(pdf)) != NULL && !strcmp(s, "Michael R Sweet"))
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else if (s)
|
||||||
|
{
|
||||||
|
printf("FAIL (got '%s', expected 'Michael R Sweet')\n", s);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected 'Michael R Sweet')");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs("pdfioFileGetCreator: ", stdout);
|
||||||
|
if ((s = pdfioFileGetCreator(pdf)) != NULL && !strcmp(s, "testpdfio"))
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else if (s)
|
||||||
|
{
|
||||||
|
printf("FAIL (got '%s', expected 'testpdfio')\n", s);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected 'testpdfio')");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs("pdfioFileGetKeywords: ", stdout);
|
||||||
|
if ((s = pdfioFileGetKeywords(pdf)) != NULL && !strcmp(s, "one fish,two fish,red fish,blue fish"))
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else if (s)
|
||||||
|
{
|
||||||
|
printf("FAIL (got '%s', expected 'one fish,two fish,red fish,blue fish')\n", s);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected 'one fish,two fish,red fish,blue fish')");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs("pdfioFileGetSubject: ", stdout);
|
||||||
|
if ((s = pdfioFileGetSubject(pdf)) != NULL && !strcmp(s, "Unit test document"))
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else if (s)
|
||||||
|
{
|
||||||
|
printf("FAIL (got '%s', expected 'Unit test document')\n", s);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected 'Unit test document')");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs("pdfioFileGetTitle: ", stdout);
|
||||||
|
if ((s = pdfioFileGetTitle(pdf)) != NULL && !strcmp(s, "Test Document"))
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else if (s)
|
||||||
|
{
|
||||||
|
printf("FAIL (got '%s', expected 'Test Document')\n", s);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected 'Test Document')");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
// Verify the number of pages is the same...
|
// Verify the number of pages is the same...
|
||||||
fputs("pdfioFileGetNumPages: ", stdout);
|
fputs("pdfioFileGetNumPages: ", stdout);
|
||||||
if (num_pages == pdfioFileGetNumPages(pdf))
|
if (num_pages == pdfioFileGetNumPages(pdf))
|
||||||
@ -2075,14 +2306,20 @@ write_color_test(pdfio_file_t *pdf, // I - PDF file
|
|||||||
//
|
//
|
||||||
|
|
||||||
static int // O - 1 on failure, 0 on success
|
static int // O - 1 on failure, 0 on success
|
||||||
write_font_test(pdfio_file_t *pdf, // I - PDF file
|
write_font_test(
|
||||||
int number, // I - Page number
|
pdfio_file_t *pdf, // I - PDF file
|
||||||
pdfio_obj_t *font, // I - Page number font
|
int number, // I - Page number
|
||||||
bool unicode) // I - Use Unicode font?
|
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_dict_t *dict; // Page dictionary
|
||||||
pdfio_stream_t *st; // Page contents stream
|
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
|
int i; // Looping var
|
||||||
static const char * const welcomes[] =// "Welcome" in many languages
|
static const char * const welcomes[] =// "Welcome" in many languages
|
||||||
{
|
{
|
||||||
@ -2233,28 +2470,16 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
|
|||||||
"Märr-ŋamathirri",
|
"Märr-ŋamathirri",
|
||||||
"Ẹ ku abọ",
|
"Ẹ ku abọ",
|
||||||
"Kíimak 'oolal",
|
"Kíimak 'oolal",
|
||||||
"Ngiyakwemukela"
|
"Ngiyakwemukela",
|
||||||
|
"いらっしゃいませ"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
printf("pdfioFileCreateFontObjFromFile(%s): ", textfontfile);
|
||||||
if (unicode)
|
if ((textfont = pdfioFileCreateFontObjFromFile(pdf, textfontfile, unicode)) != NULL)
|
||||||
{
|
puts("PASS");
|
||||||
fputs("pdfioFileCreateFontObjFromFile(NotoSansJP-Regular.otf): ", stdout);
|
|
||||||
if ((opensans = pdfioFileCreateFontObjFromFile(pdf, "testfiles/NotoSansJP-Regular.otf", true)) != NULL)
|
|
||||||
puts("PASS");
|
|
||||||
else
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
#endif // 0
|
return (1);
|
||||||
{
|
|
||||||
fputs("pdfioFileCreateFontObjFromFile(OpenSans-Regular.ttf): ", stdout);
|
|
||||||
if ((opensans = pdfioFileCreateFontObjFromFile(pdf, "testfiles/OpenSans-Regular.ttf", unicode)) != NULL)
|
|
||||||
puts("PASS");
|
|
||||||
else
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
fputs("pdfioDictCreate: ", stdout);
|
fputs("pdfioDictCreate: ", stdout);
|
||||||
if ((dict = pdfioDictCreate(pdf)) != NULL)
|
if ((dict = pdfioDictCreate(pdf)) != NULL)
|
||||||
@ -2269,7 +2494,7 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
|
|||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
fputs("pdfioPageDictAddFont(F2): ", stdout);
|
fputs("pdfioPageDictAddFont(F2): ", stdout);
|
||||||
if (pdfioPageDictAddFont(dict, "F2", opensans))
|
if (pdfioPageDictAddFont(dict, "F2", textfont))
|
||||||
puts("PASS");
|
puts("PASS");
|
||||||
else
|
else
|
||||||
return (1);
|
return (1);
|
||||||
@ -2281,7 +2506,21 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
|
|||||||
else
|
else
|
||||||
return (1);
|
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;
|
goto error;
|
||||||
|
|
||||||
fputs("pdfioContentTextBegin(): ", stdout);
|
fputs("pdfioContentTextBegin(): ", stdout);
|
||||||
@ -2302,8 +2541,8 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
|
|||||||
else
|
else
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
fputs("pdfioContentTextMoveTo(36.0, 702.0): ", stdout);
|
fputs("pdfioContentTextMoveTo(198.0, 702.0): ", stdout);
|
||||||
if (pdfioContentTextMoveTo(st, 36.0, 702.0))
|
if (pdfioContentTextMoveTo(st, 198.0, 702.0))
|
||||||
puts("PASS");
|
puts("PASS");
|
||||||
else
|
else
|
||||||
return (1);
|
return (1);
|
||||||
@ -2312,18 +2551,36 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
|
|||||||
{
|
{
|
||||||
if (i > 0 && (i % 50) == 0)
|
if (i > 0 && (i % 50) == 0)
|
||||||
{
|
{
|
||||||
fputs("pdfioContentTextMoveTo(200.0, 600.0): ", stdout);
|
fputs("pdfioContentTextMoveTo(162.0, 600.0): ", stdout);
|
||||||
if (pdfioContentTextMoveTo(st, 200.0, 600.0))
|
if (pdfioContentTextMoveTo(st, 162.0, 600.0))
|
||||||
puts("PASS");
|
puts("PASS");
|
||||||
else
|
else
|
||||||
return (1);
|
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]);
|
printf("pdfioContentTextShowf(\"%s\"): ", welcomes[i]);
|
||||||
if (pdfioContentTextShowf(st, unicode, "%s\n", welcomes[i]))
|
if (pdfioContentTextShowf(st, unicode, "%s\n", welcomes[i]))
|
||||||
puts("PASS");
|
puts("PASS");
|
||||||
else
|
else
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
|
printf("pdfioContextTextMoveTo(%g, 0.0): ", width);
|
||||||
|
if (pdfioContentTextMoveTo(st, width, 0.0))
|
||||||
|
puts("PASS");
|
||||||
|
else
|
||||||
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fputs("pdfioContentTextEnd(): ", stdout);
|
fputs("pdfioContentTextEnd(): ", stdout);
|
||||||
@ -3067,6 +3324,7 @@ write_text_test(pdfio_file_t *pdf, // I - PDF file
|
|||||||
static int // O - Exit status
|
static int // O - Exit status
|
||||||
write_unit_file(
|
write_unit_file(
|
||||||
pdfio_file_t *inpdf, // I - Input PDF file
|
pdfio_file_t *inpdf, // I - Input PDF file
|
||||||
|
const char *outname, // I - Output PDF file name
|
||||||
pdfio_file_t *outpdf, // I - Output PDF file
|
pdfio_file_t *outpdf, // I - Output PDF file
|
||||||
size_t *num_pages, // O - Number of pages
|
size_t *num_pages, // O - Number of pages
|
||||||
size_t *first_image) // O - First image object
|
size_t *first_image) // O - First image object
|
||||||
@ -3076,8 +3334,26 @@ write_unit_file(
|
|||||||
*gray_jpg, // gray.jpg image
|
*gray_jpg, // gray.jpg image
|
||||||
*helvetica, // Helvetica font
|
*helvetica, // Helvetica font
|
||||||
*page; // Page from test PDF file
|
*page; // Page from test PDF file
|
||||||
|
pdfio_dict_t *catalog; // Catalog dictionary
|
||||||
|
|
||||||
|
|
||||||
|
// Get the root object/catalog dictionary
|
||||||
|
fputs("pdfioFileGetCatalog: ", stdout);
|
||||||
|
if ((catalog = pdfioFileGetCatalog(outpdf)) != NULL)
|
||||||
|
{
|
||||||
|
puts("PASS");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
puts("FAIL (got NULL, expected dictionary)");
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set some catalog values...
|
||||||
|
pdfioDictSetName(catalog, "PageLayout", "SinglePage");
|
||||||
|
pdfioDictSetName(catalog, "PageMode", "UseThumbs");
|
||||||
|
pdfioDictSetString(catalog, "Lang", "en");
|
||||||
|
|
||||||
// Set info values...
|
// Set info values...
|
||||||
fputs("pdfioFileGet/SetAuthor: ", stdout);
|
fputs("pdfioFileGet/SetAuthor: ", stdout);
|
||||||
pdfioFileSetAuthor(outpdf, "Michael R Sweet");
|
pdfioFileSetAuthor(outpdf, "Michael R Sweet");
|
||||||
@ -3167,13 +3443,13 @@ write_unit_file(
|
|||||||
// Create some image objects...
|
// Create some image objects...
|
||||||
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/color.jpg\"): ", stdout);
|
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/color.jpg\"): ", stdout);
|
||||||
if ((color_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/color.jpg", true)) != NULL)
|
if ((color_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/color.jpg", true)) != NULL)
|
||||||
puts("PASS");
|
printf("PASS (%u)\n", (unsigned)pdfioObjGetNumber(color_jpg));
|
||||||
else
|
else
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/gray.jpg\"): ", stdout);
|
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/gray.jpg\"): ", stdout);
|
||||||
if ((gray_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/gray.jpg", true)) != NULL)
|
if ((gray_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/gray.jpg", true)) != NULL)
|
||||||
puts("PASS");
|
printf("PASS (%u)\n", (unsigned)pdfioObjGetNumber(gray_jpg));
|
||||||
else
|
else
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
@ -3236,14 +3512,17 @@ write_unit_file(
|
|||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
// Test TrueType fonts...
|
// 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);
|
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);
|
return (1);
|
||||||
|
|
||||||
// Print this text file...
|
// Print this text file...
|
||||||
if (write_text_test(outpdf, 11, helvetica, "README.md"))
|
if (write_text_test(outpdf, 12, helvetica, "README.md"))
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
fputs("pdfioFileGetNumPages: ", stdout);
|
fputs("pdfioFileGetNumPages: ", stdout);
|
||||||
@ -3258,7 +3537,7 @@ write_unit_file(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close the new PDF file...
|
// Close the new PDF file...
|
||||||
fputs("pdfioFileClose(...): ", stdout);
|
printf("pdfioFileClose(\"%s\"): ", outname);
|
||||||
if (pdfioFileClose(outpdf))
|
if (pdfioFileClose(outpdf))
|
||||||
puts("PASS");
|
puts("PASS");
|
||||||
else
|
else
|
||||||
|
304
testttf.c
Normal file
304
testttf.c
Normal 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);
|
||||||
|
}
|
61
ttf.c
61
ttf.c
@ -3,16 +3,12 @@
|
|||||||
//
|
//
|
||||||
// https://github.com/michaelrsweet/ttf
|
// https://github.com/michaelrsweet/ttf
|
||||||
//
|
//
|
||||||
// Copyright © 2018-2021 by Michael R Sweet.
|
// Copyright © 2018-2023 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
//
|
//
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# define _CRT_SECURE_NO_WARNINGS
|
# define _CRT_SECURE_NO_WARNINGS
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
@ -75,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...
|
// DEBUG is defined and is a no-op otherwise...
|
||||||
//
|
//
|
||||||
|
|
||||||
@ -420,7 +416,7 @@ ttfCreate(const char *filename, // I - Filename
|
|||||||
if (read_os_2(font, &os_2))
|
if (read_os_2(font, &os_2))
|
||||||
{
|
{
|
||||||
// Copy key values from OS/2 table...
|
// Copy key values from OS/2 table...
|
||||||
static const ttf_stretch_t widths[] =
|
static const ttf_stretch_t stretches[] =
|
||||||
{
|
{
|
||||||
TTF_STRETCH_ULTRA_CONDENSED, // ultra-condensed
|
TTF_STRETCH_ULTRA_CONDENSED, // ultra-condensed
|
||||||
TTF_STRETCH_EXTRA_CONDENSED, // extra-condensed
|
TTF_STRETCH_EXTRA_CONDENSED, // extra-condensed
|
||||||
@ -433,8 +429,8 @@ ttfCreate(const char *filename, // I - Filename
|
|||||||
TTF_STRETCH_ULTRA_EXPANDED // ultra-expanded
|
TTF_STRETCH_ULTRA_EXPANDED // ultra-expanded
|
||||||
};
|
};
|
||||||
|
|
||||||
if (os_2.usWidthClass >= 1 && os_2.usWidthClass <= (int)(sizeof(widths) / sizeof(widths[0])))
|
if (os_2.usWidthClass >= 1 && os_2.usWidthClass <= (int)(sizeof(stretches) / sizeof(stretches[0])))
|
||||||
font->stretch = widths[os_2.usWidthClass - 1];
|
font->stretch = stretches[os_2.usWidthClass - 1];
|
||||||
|
|
||||||
font->weight = (short)os_2.usWeightClass;
|
font->weight = (short)os_2.usWeightClass;
|
||||||
font->cap_height = os_2.sCapHeight;
|
font->cap_height = os_2.sCapHeight;
|
||||||
@ -452,7 +448,7 @@ ttfCreate(const char *filename, // I - Filename
|
|||||||
font->cap_height = font->ascent;
|
font->cap_height = font->ascent;
|
||||||
|
|
||||||
if (font->x_height == 0)
|
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...
|
// Build a sparse glyph widths table...
|
||||||
font->min_char = -1;
|
font->min_char = -1;
|
||||||
@ -481,6 +477,11 @@ ttfCreate(const char *filename, // I - Filename
|
|||||||
else
|
else
|
||||||
font->widths[bin][i & 255] = widths[glyph];
|
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...
|
// Cleanup and return the font...
|
||||||
@ -737,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.
|
// 'ttfGetItalicAngle()' - Get the italic angle.
|
||||||
//
|
//
|
||||||
@ -856,13 +846,14 @@ int // O - Width in 1000ths
|
|||||||
ttfGetWidth(ttf_t *font, // I - Font
|
ttfGetWidth(ttf_t *font, // I - Font
|
||||||
int ch) // I - Unicode character
|
int ch) // I - Unicode character
|
||||||
{
|
{
|
||||||
int bin = ch >> 8; // Bin in widths array
|
int bin = ch >> 8; // Bin in widths array
|
||||||
|
|
||||||
|
|
||||||
// Range check input...
|
// Range check input...
|
||||||
if (!font || ch < ' ' || ch == 0x7f)
|
if (!font || ch < ' ' || ch == 0x7f)
|
||||||
return (0);
|
return (0);
|
||||||
else if (font->widths[bin])
|
|
||||||
|
if (font->widths[bin])
|
||||||
return ((int)(1000.0f * font->widths[bin][ch & 255].width / font->units));
|
return ((int)(1000.0f * font->widths[bin][ch & 255].width / font->units));
|
||||||
else if (font->widths[0]) // .notdef
|
else if (font->widths[0]) // .notdef
|
||||||
return ((int)(1000.0f * font->widths[0][0].width / font->units));
|
return ((int)(1000.0f * font->widths[0][0].width / font->units));
|
||||||
@ -882,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.
|
// 'copy_name()' - Copy a name string from a font.
|
||||||
//
|
//
|
||||||
@ -1280,7 +1282,7 @@ read_cmap(ttf_t *font) // I - Font
|
|||||||
|
|
||||||
// Based on the end code of the segent table, allocate space for the
|
// Based on the end code of the segent table, allocate space for the
|
||||||
// uncompressed cmap table...
|
// 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->num_cmap = segments[segCount - 1].endCode + 1;
|
||||||
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
|
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
|
||||||
@ -1305,17 +1307,18 @@ read_cmap(ttf_t *font) // I - Font
|
|||||||
{
|
{
|
||||||
// Use an "obscure indexing trick" (words from the spec, not
|
// Use an "obscure indexing trick" (words from the spec, not
|
||||||
// mine) to look up the glyph index...
|
// 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;
|
glyph = -1;
|
||||||
else
|
else
|
||||||
glyph = ((glyphIdArray[temp] + segment->idDelta) & 65535) - 1;
|
glyph = (glyphIdArray[temp] + segment->idDelta) & 65535;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Just use idDelta to compute a glyph index...
|
// Just use idDelta to compute a glyph index...
|
||||||
glyph = ((ch + segment->idDelta) & 65535) - 1;
|
glyph = (ch + segment->idDelta) & 65535;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmapptr[ch] = glyph;
|
cmapptr[ch] = glyph;
|
||||||
@ -1582,6 +1585,8 @@ read_hmtx(ttf_t *font, // I - Font
|
|||||||
{
|
{
|
||||||
widths[i].width = (short)read_ushort(font);
|
widths[i].width = (short)read_ushort(font);
|
||||||
widths[i].left_bearing = (short)read_short(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);
|
return (widths);
|
||||||
|
13
ttf.h
13
ttf.h
@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// https://github.com/michaelrsweet/ttf
|
// https://github.com/michaelrsweet/ttf
|
||||||
//
|
//
|
||||||
// Copyright © 2018-2021 by Michael R Sweet.
|
// Copyright © 2018-2023 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
@ -11,17 +11,11 @@
|
|||||||
|
|
||||||
#ifndef TTF_H
|
#ifndef TTF_H
|
||||||
# define TTF_H
|
# define TTF_H
|
||||||
|
|
||||||
//
|
|
||||||
// Include necessary headers...
|
|
||||||
//
|
|
||||||
|
|
||||||
# include <stdbool.h>
|
# include <stdbool.h>
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
|
|
||||||
# ifdef __cplusplus
|
# ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
# endif //
|
# endif // __cplusplus
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -89,8 +83,8 @@ extern ttf_t *ttfCreate(const char *filename, size_t idx, ttf_err_cb_t err_cb,
|
|||||||
extern void ttfDelete(ttf_t *font);
|
extern void ttfDelete(ttf_t *font);
|
||||||
extern int ttfGetAscent(ttf_t *font);
|
extern int ttfGetAscent(ttf_t *font);
|
||||||
extern ttf_rect_t *ttfGetBounds(ttf_t *font, ttf_rect_t *bounds);
|
extern ttf_rect_t *ttfGetBounds(ttf_t *font, ttf_rect_t *bounds);
|
||||||
extern int ttfGetCapHeight(ttf_t *font);
|
|
||||||
extern const int *ttfGetCMap(ttf_t *font, size_t *num_cmap);
|
extern const int *ttfGetCMap(ttf_t *font, size_t *num_cmap);
|
||||||
|
extern int ttfGetCapHeight(ttf_t *font);
|
||||||
extern const char *ttfGetCopyright(ttf_t *font);
|
extern const char *ttfGetCopyright(ttf_t *font);
|
||||||
extern int ttfGetDescent(ttf_t *font);
|
extern int ttfGetDescent(ttf_t *font);
|
||||||
extern ttf_rect_t *ttfGetExtents(ttf_t *font, float size, const char *s, ttf_rect_t *extents);
|
extern ttf_rect_t *ttfGetExtents(ttf_t *font, float size, const char *s, ttf_rect_t *extents);
|
||||||
@ -112,5 +106,4 @@ extern bool ttfIsFixedPitch(ttf_t *font);
|
|||||||
# ifdef __cplusplus
|
# ifdef __cplusplus
|
||||||
}
|
}
|
||||||
# endif // __cplusplus
|
# endif // __cplusplus
|
||||||
|
|
||||||
#endif // !TTF_H
|
#endif // !TTF_H
|
||||||
|
Reference in New Issue
Block a user