160 Commits

Author SHA1 Message Date
Michael R Sweet
d03e5ee5d9 Update API version number references for consistency, mention GIF and WebP since v1.7. 2026-02-04 16:28:11 -05:00
Michael R Sweet
2837cafd41 When a duplicate object is seen in an xref stream, only replace the object if it has a higher generation number (Issue #155) 2026-01-29 13:01:23 -05:00
Michael R Sweet
c7bf1695fd Tweak docos. 2026-01-29 12:31:50 -05:00
Michael R Sweet
14d844e436 Update documentation. 2026-01-29 12:30:10 -05:00
Michael R Sweet
ee42352228 Move testpdfio output to testfiles subdirectory. 2026-01-29 11:20:08 -05:00
Michael R Sweet
700f7a011b Clean up some compiler warnings.
Add missing file to Xcode project.
2026-01-27 21:04:18 -05:00
Michael R Sweet
3eb9c4a13f Fix pdfioPageGetDate prototype. 2026-01-27 19:47:19 -05:00
Michael R Sweet
c7103e9558 Update debug printfs. 2026-01-27 19:26:30 -05:00
Michael R Sweet
e9ba25c0da Add NULL check for new page contents code. 2026-01-27 19:23:27 -05:00
Michael R Sweet
a6160e7f6f Merge pull request #152 from jeevansridharan/docs-add-ubuntu-deps
docs: add Ubuntu/Debian dependency installation example
2026-01-26 11:15:05 -05:00
Jeevan Sridharan
390e8cef8f docs: clarify and reorder Ubuntu/Debian dependency instructions 2026-01-26 21:37:38 +05:30
Jeevan Sridharan
78c6852413 docs: add Ubuntu/Debian dependency installation example 2026-01-22 22:15:02 +05:30
Michael R Sweet
f4055f0d7a Implement object streams (Issue #101) 2026-01-19 10:22:43 -05:00
Michael R Sweet
a37455c009 Add _pdfioStringPrintf function and support for using a string buffer to collect an object's value (Issue #101) 2026-01-19 10:22:23 -05:00
Michael R Sweet
e61e08b5d2 Minor refactoring to more easily add in object stream support (Issue #101) 2026-01-18 19:29:17 -05:00
Michael R Sweet
a818dee123 Don't generate a mask image if unnecessary. 2026-01-18 18:18:25 -05:00
Michael R Sweet
ddb57bb754 Implement WebP image support (Issue #144) 2026-01-18 16:49:26 -05:00
Michael R Sweet
61d7e0c68d Update documentation to mention GIF files (Issue #145) 2026-01-18 11:43:56 -05:00
Michael R Sweet
c2f2cd6c37 Fix handling of partial image blocks (Issue #145)
Add a partial image block version of the animation test.

Rename test images to make it clear what GIF features are being used.

Finalize test page content/layout.
2026-01-18 11:26:50 -05:00
Michael R Sweet
b3aaf2e70f Initial GIF support (Issue #145) 2026-01-18 10:31:50 -05:00
Michael R Sweet
4e9ec397f1 Implement pdfioPageGetXxx functions (Issue #150) 2026-01-16 20:54:02 -05:00
Michael R Sweet
3c60d4a886 Fix 0 key length regression (Issue #149) 2026-01-16 20:19:34 -05:00
Michael R Sweet
aac04a2a96 Fix repaired xref stream offsets and support indirect Contents arrays for pages. 2026-01-16 16:54:37 -05:00
Michael R Sweet
65098b5509 Fix implementation of LZWDecode filter to account for the EarlyChange parameter
(somewhat buried, very frustrating...)

Add some debugging and update the test suite to find page metadata in any parent
page object.
2026-01-16 15:57:43 -05:00
Michael R Sweet
e6e0b84dfc Remove dead code detected by Coverity. 2026-01-16 11:47:08 -05:00
Michael R Sweet
3e6c38a436 Update Windows DLL exports. 2026-01-16 11:36:34 -05:00
Michael R Sweet
6daf9e5e64 Fix clang warnings. 2026-01-16 11:32:39 -05:00
Michael R Sweet
1044cc71a4 Fix VC++ project. 2026-01-16 11:32:37 -05:00
Michael R Sweet
387a30f6c5 Update TTF. 2026-01-16 11:32:18 -05:00
Michael R Sweet
09520d250f Add support for LZWDecode filter, needs more testing (Issue #11) 2026-01-16 09:53:51 -05:00
Michael R Sweet
bdcd963352 Implement ASCII85Decode filter (Issue #11) 2026-01-14 09:31:41 -05:00
Michael R Sweet
4565c52ff1 Merge pull request #147 from zYg-sys/master
examples: fix builds on windows
2026-01-14 08:08:28 -05:00
Yuguo Zhang
9cb19db1c5 examples: fix builds on windows 2026-01-14 12:35:26 +08:00
Michael R Sweet
5618c432cc Add an xref table offset array to better detect xref table loops (Issue #148) 2026-01-13 18:40:44 -05:00
Michael R Sweet
4143808398 Clarify security policy. 2026-01-13 13:44:36 -05:00
Michael R Sweet
c92546ed94 More test suite tweaking. 2026-01-13 10:34:38 -05:00
Michael R Sweet
07c6005fad Add some guards to make sure you don't accidentally overwrite existing PDF files with testpdfio... 2026-01-11 14:33:19 -05:00
Michael R Sweet
6fd1b781dc Update test script. 2026-01-11 13:46:04 -05:00
Michael R Sweet
6e8bd06937 Refactor PDF encryption handler to work with more files. 2026-01-11 13:36:56 -05:00
Michael R Sweet
0feace3eb5 Fix AES-256 key initialization. 2026-01-10 14:44:47 -05:00
Michael R Sweet
846b0c9c7f Add missing range checks to pdfioArrayCopy and pdfioDictCopy. 2026-01-08 15:05:12 -05:00
Michael R Sweet
fda0963220 Update Xcode project settings. 2026-01-08 12:39:08 -05:00
Yuguo Zhang
31fb66917e examples: fix builds on windows 2026-01-08 12:16:33 +08:00
Michael R Sweet
089288946e Increase PDFIO_MAX_STRING to 128k (Issue #146) 2026-01-06 11:21:58 -05:00
Michael R Sweet
68dda34448 Fix an error propagation bug in _pdfioValueCopy (Issue #146) 2026-01-06 11:18:52 -05:00
Michael R Sweet
ae97788728 Fix Coverity status badge link. 2026-01-01 10:06:41 -05:00
Michael R Sweet
6e049d4ed1 Update GitHub CI. 2026-01-01 09:59:16 -05:00
Michael R Sweet
d70a72fdda Update prerequisites. 2026-01-01 09:57:43 -05:00
Michael R Sweet
8ba48ba4ae Fix GitHub CI badge URL (again). 2026-01-01 09:54:19 -05:00
Michael R Sweet
031ad03a38 Fix GitHub CI badge URL. 2026-01-01 09:48:42 -05:00
Michael R Sweet
9b1047b2e3 Sync up with latest TTF changes for Windows. 2026-01-01 09:43:14 -05:00
Michael R Sweet
62dabe3580 Fix builds on Windows. 2025-12-31 11:58:24 -05:00
Michael R Sweet
16e2d6c91a Fix clang issues. 2025-12-31 11:44:28 -05:00
Michael R Sweet
d1cbeec9e3 Visual Studio needs additional include dirs. 2025-12-31 11:38:12 -05:00
Michael R Sweet
29c2d131da Update Xcode and Visual Studio projects. 2025-12-31 11:33:29 -05:00
Michael R Sweet
115119a6f8 Update TTF to v1.1.0. 2025-12-31 11:29:08 -05:00
Michael R Sweet
616f053835 No more testttf in the local directory. 2025-12-26 12:31:15 -05:00
Michael R Sweet
d1536eee0c Changelog. 2025-12-26 12:14:56 -05:00
Michael R Sweet
06d3e85627 Update GitHub CI rules. 2025-12-21 22:14:20 -05:00
Michael R Sweet
850b0fa0a0 Update makesrcdist to correctly embed a copy of the TTF library. 2025-12-21 20:37:50 -05:00
Michael R Sweet
7ac8669057 Change to using external TTF library, when available, otherwise local copy.
Fix pkgconfig file.
2025-12-21 20:28:26 -05:00
Michael R Sweet
f6f3191a8d Bump version. 2025-12-21 19:18:33 -05:00
Michael R Sweet
c2b25a1fa0 Support Encrypt dictionaries as well as indirect references (Issue #139) 2025-12-21 19:04:36 -05:00
Michael R Sweet
d5d089c560 Update autoconf cross-compilation files. 2025-12-19 13:40:17 -05:00
Michael R Sweet
23eb44e80b Changelog. 2025-12-02 15:02:25 -05:00
Michael R Sweet
7d412cf855 Add range check to ttfGetExtents. 2025-12-02 15:01:16 -05:00
Michael R Sweet
597c9f8cda Update support for UTF-16 string tokens (Issue #141) 2025-12-02 14:37:18 -05:00
Michael R Sweet
789b74af39 Update CI versions. 2025-12-01 10:37:03 -05:00
Michael R Sweet
5032ce926e Fix some clang warnings.
Update Xcode project to use installed PNG library.
2025-11-10 16:14:21 -05:00
Michael R Sweet
2977b82f35 Fix CI. 2025-11-10 15:41:25 -05:00
Michael R Sweet
b6008bae76 Add input checking to pdfioFileCreateFontObjFromBase. 2025-10-21 11:29:41 -04:00
Michael R Sweet
8459d97ff3 Update markdown 'library'. 2025-10-15 15:03:07 -04:00
Michael R Sweet
ed8be1f378 Changelog. 2025-10-06 08:42:49 -04:00
Michael R Sweet
5edb5ad006 Check for read error when reading horizontal metrics data. 2025-10-06 08:42:24 -04:00
Michael R Sweet
1b82f141e7 Clean up PDF/A test pages and fix image object names. 2025-10-05 18:28:58 -04:00
Michael R Sweet
5699a52127 Fix test suite failure caused by migration of metadata for PDF 2.0. 2025-10-05 18:14:23 -04:00
Michael R Sweet
3c9a52a824 Update docos. 2025-10-05 15:35:23 -04:00
Michael R Sweet
c272ddde3b Remove deprecated information key/value pairs for PDF 2.0 output. 2025-10-05 15:34:34 -04:00
Michael R Sweet
26a92febc6 Cleanup PR for PDF/A support and add documentation (Issue #122) 2025-10-05 15:19:15 -04:00
Michael R Sweet
d82da750d9 Remove example PDF file that is not needed. 2025-10-05 13:43:47 -04:00
Michael R Sweet
e6fea00d74 Merge pull request #137 from vididvidid/feature/pdfa-subset-support
Feature: Add method for creating PDF/A compliant files
2025-10-05 13:43:10 -04:00
vididvidid
815a45aec1 remove the return and used %c for the part 2025-10-03 02:42:19 +00:00
Yash kumar kasaudhan
fe3ab5d67f Merge branch 'michaelrsweet:master' into feature/pdfa-subset-support 2025-10-02 01:53:47 -04:00
vididvidid
abf2d6e7d8 remove the previously added .test-pdfa 2025-10-02 05:50:45 +00:00
vididvidid
2b68a0e0db used the cp startegy of string and pointer combination to get the part and comliance 2025-10-02 05:50:06 +00:00
vididvidid
c319f5a047 not printing whole pdf file name but only the pdfversion 2025-10-02 05:16:03 +00:00
vididvidid
6e98635d91 moved the do pdfa test after the closing of inpdf 2025-10-02 05:07:30 +00:00
vididvidid
04c52a74f6 used or assignment to clearly update the status 2025-10-02 04:57:16 +00:00
vididvidid
c603a4e2b0 renamed the filename to testpdfio-pdfa-rc4.pdf and updated message clearly encryption allowed on pdf/a files 2025-10-02 04:53:19 +00:00
vididvidid
66f0c84621 renamed the test pdfa files to testpdfio-pdfa- 2025-10-02 04:46:03 +00:00
vididvidid
98f6b90b53 removed text[256] which is now being unused because we removed the snprintf 2025-10-02 04:43:55 +00:00
vididvidid
c1ecf3a1ed used pdfioContentTextShow instead of snprintf 2025-10-02 04:42:29 +00:00
vididvidid
f417a25121 block usage of incompatible operators and functions, for example the pdfioFileCreateFontObjFromBase function, the use of transparency in pdfioFileCreateImageObjFrom..., and so forth. 2025-10-01 08:17:17 +00:00
vididvidid
1325c9cd61 removed the comment i made in the code 2025-10-01 07:40:14 +00:00
vididvidid
71790bb6c4 added the unit testcases for the pdfa feature in the testpdfio and removed the custom one 2025-10-01 07:38:54 +00:00
vididvidid
a08614911a added the comment at the end of line tyepdef enum _pdfio_pdfa_e 2025-10-01 05:06:49 +00:00
vididvidid
7bc3e84aed added string replacing the switch cases 2025-10-01 05:05:15 +00:00
vididvidid
0194feca6f moved the comment outside of nested if for standard header 2025-10-01 04:52:16 +00:00
vididvidid
219a288488 renamed actual_version to file_version 2025-10-01 04:43:51 +00:00
vididvidid
2b4b20efe4 removed the numering in comments 2025-10-01 04:40:16 +00:00
Michael R Sweet
4032eef826 Fix the unsupported filter error message (Issue #130) 2025-09-30 19:48:30 -04:00
Michael R Sweet
da58eec96d Update Windows DLL exports file. 2025-09-30 18:41:26 -04:00
Michael R Sweet
203a974682 Fix typo. 2025-09-30 18:40:28 -04:00
Michael R Sweet
7c69f13ba9 Document tagged PDF support (Issue #123) 2025-09-30 18:38:02 -04:00
vididvidid
6dcb2cdced deleted the test files result 2025-09-30 07:11:46 +00:00
vididvidid
b1550fdc10 corrected the issue regarding the headers in the pdfa files 2025-09-30 07:09:56 +00:00
vididvidid
ef3a6cb1c0 created a test file 2025-09-30 05:21:48 +00:00
vididvidid
0d1d5413ee pdf/a files must contains special xmp metadata that declares their conformance. so updated the write_metadata 2025-09-30 04:52:25 +00:00
vididvidid
f7db310942 added the enum for the conformance levels in the pdfio-private.h under _pdfio_file_s and updated the create_common() function which is used by pdfiofilecreate to recognize the new pdf/a-.. strings 2025-09-30 04:38:35 +00:00
Michael R Sweet
99d03ddf9c Fallthrough attribute is Apple-specific at the moment... 2025-09-25 17:47:59 -04:00
Michael R Sweet
fe122d557b Resolve clang warnings, update Xcode project. 2025-09-25 17:12:33 -04:00
Michael R Sweet
2582052377 More tagged PDF changes (Issue #123):
- Add MarkInfo dictionary to document catalog when generating marked content.
- Add basic structured tags to md2pdf example.
2025-09-18 12:08:55 -04:00
Michael R Sweet
af5c412c96 Add pdfioContentBegin/EndMarked functions (Issue #123) 2025-09-16 20:33:02 -04:00
Michael R Sweet
e13526226a Merge pull request #136 from uddhavphatak/master
Correct PDF trailer: output %%EOF instead of %EOF
2025-09-13 11:35:09 -04:00
Michael R Sweet
868794ab24 Changelog. 2025-09-13 11:34:48 -04:00
ThePhatak
604396c0e7 Correct PDF trailer: output %%EOF instead of %EOF 2025-09-13 19:25:39 +05:30
Michael R Sweet
156a184a45 Fix some Clang warnings. 2025-09-04 14:37:42 -04:00
Michael R Sweet
eb0c785d6d Current autoconf triggers the obsolete AC_HEADER_STDC checks, so force things before zlib. 2025-08-30 13:37:51 -04:00
Michael R Sweet
359b65298e Add underflow detection to TTF cmap code. 2025-08-27 11:19:35 -04:00
Michael R Sweet
ccf3a90c97 Document how warning messages work (Issue #118) 2025-08-26 15:18:36 -04:00
Michael R Sweet
c8017f50f6 PDFio 1.5.4 was released today. 2025-08-26 12:35:41 -04:00
Michael R Sweet
4a1c53e31b Fix typos. 2025-08-06 11:25:51 -04:00
Michael R Sweet
5e8c3eb0b2 Clarify token too large errors (Issue #131) 2025-07-28 19:11:02 +01:00
Michael R Sweet
07ea310cd1 Fix copy_jpeg with invalid/corrupt JPEG data (Issue #132) 2025-07-28 19:03:16 +01:00
Michael R Sweet
7ce8ec07ab Limit generation numbers to 0 to 65535. 2025-07-28 12:51:39 +01:00
Michael R Sweet
f9ff913bb9 Changelog. 2025-05-03 10:14:07 -04:00
Michael R Sweet
0c41a14919 Don't set extension pointer if object is NULL. 2025-04-25 12:40:54 -04:00
Michael R Sweet
57a01a7317 Fix object map to use unique file hash instead of pointer values (Issue #125) 2025-04-24 14:00:24 -04:00
Michael R Sweet
5b08046ece Add more range checking for streams. 2025-04-24 13:17:31 -04:00
Michael R Sweet
d1bf9a92dc Fix whitespace. 2025-04-24 12:29:20 -04:00
Michael R Sweet
749c0f845d Drop Xpdf and other checks from test-corpus.sh script. 2025-04-24 12:07:07 -04:00
Michael R Sweet
cad8f450ab Multiple fixes to allow PDFio to read more edge-case PDFs.
- Update _pdfioFileGets to allow for really long lines where it
  doesn't matter if we lose the end of the line.
- Update "startxref" detection at the end of the file.
- Refactor repair logic so that you just get a single WARNING about
  the repair (debug messages available for testing)
- Allow whitespace after the "obj" in the object header.
- Make sure to close xref stream on error.
- Update predictor code to support Colors <= 32 (some implementations
  set Colors to the number of bytes per record in the xref stream,
  which prevents the predictor from doing anything...)
- Allow CR CR in xref table.
- Clear old trailer/root/pages/etc. objects when repairing, update
  existing objects that were already found in load_xref.
- Don't set current object in pdfioObjectCreate/OpenStream if the
  stream can't be created/opened.
2025-04-24 11:09:54 -04:00
Michael R Sweet
278ddb7fa7 Clarify error callback API, and actually use the return value.
Improve repair implementation.
2025-04-23 14:43:14 -04:00
Michael R Sweet
1e6bb710e3 Changelog. 2025-04-23 10:13:35 -04:00
Michael R Sweet
4ca93bd34f Add support for EncryptMetadata key in encryption dictionary. 2025-04-23 10:07:44 -04:00
Michael R Sweet
404ca72882 Protect against invalid predictor buffer size. 2025-04-23 10:07:26 -04:00
Michael R Sweet
67704ce493 Update corpus test script to use Xpdf's pdfinfo to weed out known-bad PDFs for now. 2025-04-21 16:59:59 -04:00
Michael R Sweet
e983ba98d8 Fix encrypted PDF files with P values >2^31-1. 2025-04-20 17:33:56 -04:00
Michael R Sweet
c23c6dec9a Tweak script. 2025-04-18 20:00:14 -04:00
Michael R Sweet
5e9617a742 One more Windows build warning. 2025-04-18 18:10:03 -04:00
Michael R Sweet
09ee7d16c1 More test program cleanup to use test header. 2025-04-18 18:08:37 -04:00
Michael R Sweet
078985fc20 Try to eliminate more Windows build warnings. 2025-04-18 17:58:06 -04:00
Michael R Sweet
13fedfc424 Try a macro trick to get rid of some Windows build warnings. 2025-04-18 17:29:02 -04:00
Michael R Sweet
c07927dc2d Bump copyright on test header since it was updated. 2025-04-18 17:22:15 -04:00
Michael R Sweet
1393cd6c04 Hopefully fix Windows build issues. 2025-04-18 17:07:53 -04:00
Michael R Sweet
b278e33aac Update unit test program to use test header. 2025-04-18 11:27:12 -04:00
Michael R Sweet
1116e929f7 Add pdfioFileGet/SetLanguage functions (Issue #124) 2025-04-13 20:21:23 -04:00
Michael R Sweet
c75611e274 Update documentation. 2025-04-13 17:15:53 -04:00
Michael R Sweet
c507cabbda Update Windows DLL exports. 2025-04-13 16:56:50 -04:00
Michael R Sweet
81aeef46d2 Add XMP metadata to output (Issue #103) 2025-04-13 16:56:30 -04:00
Michael R Sweet
ba7371b2e1 Fix location of OutputIntents (catalog, not info dict) 2025-04-13 14:31:14 -04:00
Michael R Sweet
ec64af8b20 Add pdfioFileAddOutputIntent API (Issue #104) 2025-04-13 14:16:53 -04:00
Michael R Sweet
acd68df592 Start work on OutputIntent and better color support in PDFio:
- Add CGATS001-compatible "micro" ICC profile as a standard CMYK color space
  (this is the default used by several Adobe applications)
- Add `PDFIO_CS_CGATS001` color space enum.
- Extend `pdfioArrayCreateColorFromStandard` to support CMYK.
- Extend `pdfioFileCreateImageObjFromFile` to support CMYK JPEG files.
- Update `pdfioFileCreatePage` to add default grayscale, RGB, and CMYK color
  space resources as needed.
2025-04-13 13:31:19 -04:00
Michael R Sweet
3d9c22aa49 Remove old per-object crypto stuff we dont need. 2025-04-13 09:11:43 -04:00
Michael R Sweet
3ae95f1545 Fix decryption of UTF-16 strings (Issue #42) 2025-04-13 09:04:00 -04:00
Michael R Sweet
cfdd6f55d9 Fix decryption of RC4-40 files. 2025-04-13 08:36:58 -04:00
Michael R Sweet
06f38edcc7 Add pdfioFileCreateFontObjFromData function (Issue #120) 2025-04-12 16:25:34 -04:00
Michael R Sweet
76c1cc694f Bump version for start of 1.6.x development. 2025-04-12 15:52:28 -04:00
Michael R Sweet
4219b8fd77 Update release date. 2025-04-12 15:12:07 -04:00
Michael R Sweet
064e7fa473 Fix makesrcdist script. 2025-04-12 15:11:36 -04:00
76 changed files with 10579 additions and 5308 deletions

View File

@@ -13,7 +13,9 @@ jobs:
steps: steps:
- name: Checkout PDFio sources - name: Checkout PDFio sources
uses: actions/checkout@v4 uses: actions/checkout@v6
with:
submodules: recursive
- name: Update Build Environment - name: Update Build Environment
run: sudo apt-get update --fix-missing -y run: sudo apt-get update --fix-missing -y
- name: Install Prerequisites - name: Install Prerequisites
@@ -21,7 +23,7 @@ jobs:
- name: Configure PDFio - name: Configure PDFio
run: ./configure --enable-debug --enable-sanitizer --enable-maintainer run: ./configure --enable-debug --enable-sanitizer --enable-maintainer
- name: Build PDFio - name: Build PDFio
run: make "COMMONFLAGS=-g -fsanitize=address" run: make
- name: Test PDFio - name: Test PDFio
env: env:
ASAN_OPTIONS: leak_check_at_exit=false ASAN_OPTIONS: leak_check_at_exit=false
@@ -37,7 +39,9 @@ jobs:
steps: steps:
- name: Checkout PDFio sources - name: Checkout PDFio sources
uses: actions/checkout@v4 uses: actions/checkout@v6
with:
submodules: recursive
- name: Configure PDFio - name: Configure PDFio
run: ./configure --enable-debug --enable-sanitizer --enable-maintainer run: ./configure --enable-debug --enable-sanitizer --enable-maintainer
- name: Build PDFio - name: Build PDFio
@@ -53,9 +57,11 @@ jobs:
steps: steps:
- name: Checkout PDFio sources - name: Checkout PDFio sources
uses: actions/checkout@v4 uses: actions/checkout@v6
with:
submodules: recursive
- name: Setup MSBuild - name: Setup MSBuild
uses: microsoft/setup-msbuild@v1.0.2 uses: microsoft/setup-msbuild@v2
- name: Nuget Restore - name: Nuget Restore
run: nuget restore pdfio.sln run: nuget restore pdfio.sln
- name: Build PDFio - name: Build PDFio

View File

@@ -24,7 +24,7 @@ jobs:
steps: steps:
- name: Checkout PDFio sources - name: Checkout PDFio sources
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive

View File

@@ -8,7 +8,9 @@ jobs:
environment: Coverity environment: Coverity
steps: steps:
- name: Checkout PDFio sources - name: Checkout PDFio sources
uses: actions/checkout@v4 uses: actions/checkout@v6
with:
submodules: recursive
- name: Update Build Environment - name: Update Build Environment
run: sudo apt-get update --fix-missing -y run: sudo apt-get update --fix-missing -y
- name: Install Prerequisites - name: Install Prerequisites

2
.gitignore vendored
View File

@@ -25,5 +25,5 @@
/pdfio-*.zip* /pdfio-*.zip*
/testpdfio /testpdfio
/testpdfio-*.pdf /testpdfio-*.pdf
/testttf /testfiles/testpdfio-*.pdf
/x64 /x64

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "ttf"]
path = ttf
url = https://github.com/michaelrsweet/ttf.git

View File

@@ -2,7 +2,96 @@ Changes in PDFio
================ ================
v1.5.2 - YYYY-MM-DD v1.7.0 - YYYY-MM-DD
-------------------
- Now use TTF 1.1 or later for font support.
- Added support for basic compound stream filters for ASCII85Decode support
(Issue #11)
- Added support for LZWDecode filters (Issue #11)
- Added support for writing object streams (Issue #101)
- Added support for GIF files (Issue #145)
- Added support for WebP files (Issue #144)
- Added `pdfioPageGetXxx` functions to get values from the page dictionaries
(Issue #150)
- Fixed a buffer overflow in the (still not enabled) AES-256 code.
v1.6.2 - YYYY-MM-DD
-------------------
- Increased the maximum length of a single string to 128k (Issue #146)
- Added missing range checks to `pdfioArrayCopy` and `pdfioDictCopy`.
- Refactored PDF encryption code to fix unlocking with certain files.
- Improved xref table loop detection (Issue #148)
- Changed how duplicate objects are handled in PDF files (Issue #155)
- Fixed xref reconstruction for objects lacking a `Type` value.
- Fixed `pdfioPageOpenStream` for indirect `Contents` arrays.
- Fixed an error propagation bug when reading too-long values (Issue #146)
- Fixed a Clang warning.
v1.6.1 - 2025-12-26
-------------------
- Added missing input checking to `pdfioFileCreateFontObjFromBase` function.
- Updated support for UTF-16 strings (Issue #141)
- Updated Xcode project to use installed PNG library.
- Fixed decryption of PDF files using an Encrypt dictionary instead of an
indirect reference (Issue #139)
- Fixed character range checking in a TTF support function.
- Fixed some clang warnings.
- Fixed the generated pkg-config file.
v1.6.0 - 2025-10-06
-------------------
- Added generation of XMP metadata when writing PDFs (Issue #103)
- Added "standard" `PDFIO_CS_CGATS001` color space for non-device CMYK
(Issue #104)
- Added CMYK JPEG support with embedded ICC profiles or using the CGATS001
profile (Issue #104)
- Added `pdfioFileAddOutputIntent` function to adding output intent information
to a PDF file (Issue #104)
- Added explicit support for warning messages (Issue #118)
- Added `pdfioFileCreateFontObjFromData` function for embedding fonts in
memory (Issue #120)
- Added support for specifying PDF/A versions for created PDF files (Issue #122)
- Added `pdfioContentBeginMarked` and `pdfioContentEndMarked` functions for
creating tagged PDF content (Issue #123)
- Added `pdfioFileGetLanguage` and `pdfioFileSetLanguage` functions for getting
and setting the default/primary language of a PDF file (Issue #124)
- Now add default grayscale, RGB, and CMYK profile resources to pages as needed
(Issue #104)
- Fixed unsupported filter error (Issue #130)
- Fixed EOF comment written to the PDF (Issue #136)
- Fixed TTF cmap underflow error.
- Fixed some Clang warnings.
v1.5.4 - 2025-08-26
-------------------
- Updated indirect reference reading code to limit the range of generation
numbers.
- Updated error messages for too large tokens (Issue #131)
- Fixed a JPEG copy bug (Issue #132)
v1.5.3 - 2025-05-03
-------------------
- Fixed decryption of PDF files "protected" by 40-bit RC4 (Issue #42)
- Fixed decryption of UTF-16 strings (Issue #42)
- Fixed decryption of PDF files with large permission values.
- Fixed support for EncryptMetadata key in the encryption dictionary.
- Fixed `pdfioObjCopy` and `pdfioPageCopy` to properly identify the source PDF
file being used (Issue #125)
v1.5.2 - 2025-04-12
------------------- -------------------
- Updated maximum allowed PDF string size to 64k (Issue #117) - Updated maximum allowed PDF string size to 64k (Issue #117)

View File

@@ -110,10 +110,11 @@ same guidelines as allowed by the language.
### Source Files ### Source Files
All source files names must be 16 characters or less in length to ensure All source files names must be lowercase and should be 16 characters or less in
compatibility with older UNIX filesystems. Source files containing functions length to ensure compatibility with older UNIX filesystems. Source files
have an extension of ".c" for C source files. All "include" files have an containing functions have an extension of ".c" for C source files. All
extension of ".h". Tabs are set to 8 characters or columns. "include" files have an extension of ".h". Tabs are set every 8 characters,
columns, or spaces, with an indentation of 2 spaces in code.
The top of each source file contains a header giving the purpose or nature of The top of each source file contains a header giving the purpose or nature of
the source file and the copyright and licensing notice: the source file and the copyright and licensing notice:
@@ -141,8 +142,8 @@ private API header file will include the corresponding public API header file.
All source code utilizes block comments within functions to describe the All source code utilizes block comments within functions to describe the
operations being performed by a group of statements; avoid putting a comment operations being performed by a group of statements; avoid putting a comment
per line unless absolutely necessary, and then consider refactoring the code per line unless absolutely necessary, and then consider refactoring the code
so that it is not necessary. C source files use the C99 comment format so that it is not necessary. C source files typically use the C99 comment
("// comment"): format ("// comment"):
// Clear the state array before we begin... // Clear the state array before we begin...
for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++) for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
@@ -159,6 +160,17 @@ so that it is not necessary. C source files use the C99 comment format
sleep(1); sleep(1);
} while (i == (sizeof(array) / sizeof(array[0]))); } while (i == (sizeof(array) / sizeof(array[0])));
When passing literal argument values to functions, the argument name should be
supplied as an inline comment:
// This function call lacks any inline comments so it is hard to
// know what the numbers mean!
do_something(filename, 1, 42);
// This uses inline comments to specify what the numbers mean and
// makes the code easier to read and maintain:
do_something(filename, /*initial_value*/1, /*count*/42);
### Indentation ### Indentation
@@ -256,16 +268,16 @@ Return/output values are indicated using an "O" prefix, input values are
indicated using the "I" prefix, and values that are both input and output use indicated using the "I" prefix, and values that are both input and output use
the "IO" prefix for the corresponding in-line comment. the "IO" prefix for the corresponding in-line comment.
The [`codedoc` documentation generator][1] also understands the following The [`codedoc` documentation generator][CODEDOC] also understands the following
special text in the function description comment: special text in the function description comment:
@deprecated@ - Marks the function as deprecated (not recommended @deprecated@ - Marks the function as deprecated (not recommended
for new development and scheduled for removal) for new development and scheduled for removal)
@since version@ - Marks the function as new in the specified version. @since VERSION@ - Marks the function as new in the specified version.
@private@ - Marks the function as private (same as starting the @private@ - Marks the function as private (same as starting the
function name with an underscore) function name with an underscore)
[1]: https://www.msweet.org/codedoc [CODEDOC]: https://www.msweet.org/codedoc
### Variables ### Variables
@@ -373,11 +385,13 @@ 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, - `ARFLAGS`; options for the static library archiver,
- `BUILDROOT`: the destination root directory when installing - also picks up
the value of the `DESTDIR`, `DSTROOT`, and/or `RPM_BUILD_ROOT` environment
variables,
- `CC`; the C compiler command, - `CC`; the C compiler command,
- `CFLAGS`; options for the C compiler, - `CFLAGS`; options for the C compiler,
- `CODESIGN_IDENTITY`: the code signing identity, - `CODESIGN_IDENTITY`: the code signing identity,
- `CPPFLAGS`; options for the C preprocessor, - `CPPFLAGS`; options for the C preprocessor,
- `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,
- `LDFLAGS`; options for the linker, - `LDFLAGS`; options for the linker,
@@ -395,5 +409,6 @@ 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.
- `clean`; removes all target programs libraries, documentation files, and - `clean`; removes all target programs libraries, documentation files, and
object files, object files,
- `doc`; creates the library documentation files using [`codedoc`][CODEDOC],
- `install`; installs all distribution files in their corresponding locations. - `install`; installs all distribution files in their corresponding locations.
- `test`; runs the unit test program, building it as needed. - `test`; runs the unit test program, building it as needed.

View File

@@ -2,17 +2,12 @@ PDFio Examples
============== ==============
The "examples" subdirectory contains example code showing how to do different The "examples" subdirectory contains example code showing how to do different
things with PDFio. things with PDFio:
- `code128.c`: Shows how to embed and use a barcode font.
code128.c - `image2pdf.c`: Shows how to embed and use an image file.
--------- - `md2pdf.c`: Shows how to generate pages with multiple fonts, embedded images,
and headers and footers.
This example shows how to embed and use a barcode font. - `pdf2text.c`: Shows how to extract plain text from a PDF.
- `pdfioinfo.c`: Shows how to extract metadata from a PDF.
- `pdfiomerge.c`: Shows how to merge two or more PDFs.
md2pdf.c
--------
This example shows how to generate pages with multiple fonts, embedded images,
and headers and footers.

135
INSTALL.md Normal file
View File

@@ -0,0 +1,135 @@
Building, Testing, and Installing PDFio
=======================================
This file describes how to compile, test, and install the PDFio library from
source code. For more information on PDFio see the file called `README.md`.
Getting the Code
----------------
> Note: Do not use the ZIP file available via the Github "Code" button on the
> main project page, as that archive is missing the TTF submodule and will not
> compile.
The source code is available in release tarballs or via the Github repository.
For a release tarball, run the following commands:
tar xvzf pdfio-VERSION.tar.gz
cd pdfio-VERSION
Similarly, the release ZIP file can be extracted with the following commands:
unzip pdfio-VERSION.zip
cd pdfio-VERSION
From the Github sources, clone the repository with the `--recurse-submodules`
option *or* use the `git submodule` command:
git clone --recurse-submodules git@github.com:michaelrsweet/pdfio.git
cd pdfio
git clone git@github.com:michaelrsweet/pdfio.git
cd pdfio
git submodule update --init --recursive
To update an already-cloned repository:
git pull
git submodule update --init --recursive
Requirements
------------
PDFio requires the following to build the software:
- A C99 compiler such as Clang, GCC, or MS Visual C
- A POSIX-compliant `make` program
- A POSIX-compliant `sh` program
- libpng (<https://www.libpng.org/>) 1.6 or later for full PNG image support
(optional)
- libwebp (<https://developers.google.com/speed/webp>) 1.0 or later for WebP
image support (optional)
- ZLIB (<https://www.zlib.net/>) 1.1 or later for compression support
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
On a stock Ubuntu install, the following command will install the various
prerequisites:
sudo apt-get install build-essential libpng-dev libwebp-dev zlib1g-dev
Building and Installing on Linux/Unix
-------------------------------------
PDFio uses a configure script on Unix systems to generate a makefile:
./configure
If you want a shared library, run:
./configure --enable-shared
The default installation location is "/usr/local". Pass the `--prefix` option
to make to install it to another location:
./configure --prefix=/some/other/directory
Other configure options can be discovered by using the `--help` option:
./configure --help
Once configured, run the following command to build the library:
make all
Finally, run the following command to install the library, documentation, and
examples on the local system:
sudo make install
Use the `BUILDROOT` variable to install to an alternate root directory:
sudo make BUILDROOT=/some/other/root/directory install
> Note: PDFio also supports the GNU `DSTROOT`, Apple/Darwin `DESTDIR`, and
> Red Hat `RPM_BUILD_ROOT` variables to specify an alternate root directory.
### Running the Unit Tests
PDFio includes a unit test program that thoroughly tests the library. Type the
following to run the unit tests:
make test
Detailed test results are saved to the "test.log" file.
Building on Windows with Visual Studio
--------------------------------------
The Visual Studio solution (`pdfio.sln`) is provided for Windows developers and
generates the PDFIO1 DLL. You can also use NuGet to install the `pdfio_native`
package.
You can build and run the `testpdfio` unit tests target from within Visual
Studio to verify the functioning of the library.
Building on macOS with Xcode
----------------------------
The Xcode project (`pdfio.xcodeproj`) is provided for macOS developer which
generates a static library that will be installed under "/usr/local". Run the
following command to build and install PDFio on macOS:
sudo xcodebuild install
Alternately you can add the Xcode project to a workspace and reference the PDFio
library target as a dependency in your own project.
You can build and run the `testpdfio` unit tests target from within Xcode to
verify the functioning of the library.

View File

@@ -1,7 +1,7 @@
# #
# Makefile for PDFio. # Makefile for PDFio.
# #
# Copyright © 2021-2025 by Michael R Sweet. # Copyright © 2021-2026 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.
@@ -69,6 +69,8 @@ top_srcdir = @top_srcdir@
BUILDROOT = $(DSTROOT)$(RPM_BUILD_ROOT)$(DESTDIR) BUILDROOT = $(DSTROOT)$(RPM_BUILD_ROOT)$(DESTDIR)
TTFDIR = @TTFDIR@
# Build commands... # Build commands...
.SUFFIXES: .c .h .o .SUFFIXES: .c .h .o
@@ -89,6 +91,7 @@ PUBOBJS = \
pdfio-crypto.o \ pdfio-crypto.o \
pdfio-dict.o \ pdfio-dict.o \
pdfio-file.o \ pdfio-file.o \
pdfio-lzw.o \
pdfio-md5.o \ pdfio-md5.o \
pdfio-object.o \ pdfio-object.o \
pdfio-page.o \ pdfio-page.o \
@@ -99,17 +102,14 @@ PUBOBJS = \
pdfio-token.o \ pdfio-token.o \
pdfio-value.o pdfio-value.o
LIBOBJS = \ LIBOBJS = \
$(PUBOBJS) \ $(PUBOBJS)
ttf.o
OBJS = \ OBJS = \
$(LIBOBJS) \ $(LIBOBJS) \
testpdfio.o \ testpdfio.o
testttf.o
TARGETS = \ TARGETS = \
$(LIBPDFIO) \ $(LIBPDFIO) \
$(LIBPDFIO_STATIC) \ $(LIBPDFIO_STATIC) \
testpdfio \ testpdfio
testttf
DOCFILES = \ DOCFILES = \
doc/pdfio.html \ doc/pdfio.html \
doc/pdfio-512.png \ doc/pdfio-512.png \
@@ -135,16 +135,30 @@ EXAMPLES = \
# Make everything # Make everything
all: $(TARGETS) all:
if test "x$(TTFDIR)" != x; then \
echo Making all in $(TTFDIR)...; \
(cd $(TTFDIR); $(MAKE) $(MFLAGS) all) || exit 1; \
fi
$(MAKE) $(MFLAGS) $(TARGETS)
# Clean everything # Clean everything
clean: clean:
if test "x$(TTFDIR)" != x; then \
echo Cleaning in $(TTFDIR)...; \
(cd $(TTFDIR); $(MAKE) $(MFLAGS) clean) || exit 1; \
fi
echo Cleaning build files...
rm -f $(TARGETS) $(OBJS) rm -f $(TARGETS) $(OBJS)
# Install everything # Install everything
install: $(TARGETS) install: $(TARGETS)
if test "x$(TTFDIR)" != x; then \
echo Installing in $(TTFDIR)...; \
(cd $(TTFDIR); $(MAKE) $(MFLAGS) install) || exit 1; \
fi
echo Installing header files to $(BUILDROOT)$(includedir)... echo Installing header files to $(BUILDROOT)$(includedir)...
$(INSTALL) -d -m 755 $(BUILDROOT)$(includedir) $(INSTALL) -d -m 755 $(BUILDROOT)$(includedir)
for file in $(PUBHEADERS); do \ for file in $(PUBHEADERS); do \
@@ -186,9 +200,8 @@ install: $(TARGETS)
# Test everything # Test everything
test: testpdfio testttf test: testpdfio
./testttf 2>test.log ./testpdfio 2>test.log
./testpdfio 2>>test.log
LANG=fr_FR.UTF-8 ./testpdfio 2>>test.log LANG=fr_FR.UTF-8 ./testpdfio 2>>test.log
@@ -232,23 +245,16 @@ testpdfio: testpdfio.o libpdfio.a
$(CC) $(LDFLAGS) -o $@ testpdfio.o libpdfio.a $(LIBS) $(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 # Dependencies
$(OBJS): pdfio.h pdfio-private.h Makefile $(OBJS): pdfio.h pdfio-private.h Makefile
pdfio-content.o: pdfio-content.h ttf.h pdfio-content.o: pdfio-content.h
testttf.o: ttf.h testpdfio.o: test-internal.h
ttf.o: ttf.h
# Make documentation using Codedoc <https://www.msweet.org/codedoc> # Make documentation using Codedoc <https://www.msweet.org/codedoc>
DOCFLAGS = \ DOCFLAGS = \
--author "Michael R Sweet" \ --author "Michael R Sweet" \
--copyright "Copyright (c) 2021-2025 by Michael R Sweet" \ --copyright "Copyright (c) 2021-2026 by Michael R Sweet" \
--docversion $(PDFIO_VERSION) --docversion $(PDFIO_VERSION)
.PHONY: doc .PHONY: doc

2
NOTICE
View File

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

View File

@@ -1,10 +1,10 @@
pdfio - PDF Read/Write Library PDFio - PDF Read/Write Library
============================== ==============================
![Version](https://img.shields.io/github/v/release/michaelrsweet/pdfio?include_prereleases) ![Version](https://img.shields.io/github/v/release/michaelrsweet/pdfio?include_prereleases)
![Apache 2.0](https://img.shields.io/github/license/michaelrsweet/pdfio) ![Apache 2.0](https://img.shields.io/github/license/michaelrsweet/pdfio)
[![Build Status](https://img.shields.io/github/workflow/status/michaelrsweet/pdfio/Build)](https://github.com/michaelrsweet/pdfio/actions/workflows/build.yml) [![Build Status](https://img.shields.io/github/actions/workflow/status/michaelrsweet/pdfio/build.yml)](https://github.com/michaelrsweet/pdfio/actions/workflows/build.yml)
[![Coverity Scan Status](https://img.shields.io/coverity/scan/22385.svg)](https://scan.coverity.com/projects/michaelrsweet-pdfio) [![Coverity Scan Status](https://img.shields.io/coverity/scan/23194.svg)](https://scan.coverity.com/projects/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:
@@ -20,77 +20,29 @@ 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.
Copyright © 2021-2026 by Michael R Sweet. PDFio is licensed under the Apache
Requirements License Version 2.0 with an (optional) exception to allow linking against GNU
------------ GPL2-only software. See the files `LICENSE` and `NOTICE` for more information.
PDFio requires the following to build the software:
- A C99 compiler such as Clang, GCC, or MS Visual C
- A POSIX-compliant `make` program
- ZLIB (<https://www.zlib.net>) 1.1 or higher
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
Documentation Reading the Documentation
------------- -------------------------
See the man page (`pdfio.3`) and full HTML documentation (`pdfio.html`) for Initial documentation to get you started is provided in the root directory of
information on using PDFio. the PDFio sources:
- `CHANGES.md`: A list of changes for each release of PDFio.
- `CODE_OF_CONDUCT.md`: Code of conduct for the project.
- `CONTRIBUTING.md`: Guidelines for contributing to the project.
- `INSTALL.md`: Instructions for building, testing, and installing PDFio.
- `LICENSE`: The PDFio license agreement (Apache 2.0).
- `NOTICE`: Copyright notices and exceptions to the PDFio license agreement.
- `README.md`: This file.
- `SECURITY.md`: How (and when) to report security issues.
Installing PDFio You will find the PDFio documentation in HTML, EPUB, and man formats in the
---------------- `doc` directory.
PDFio uses a configure script on Unix systems to generate a makefile: Examples can be found in the `examples` directory.
./configure *Please read the documentation before asking questions.*
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
To test it, run:
make test
To install it, run:
sudo make install
Visual Studio Project
---------------------
The Visual Studio solution ("pdfio.sln") is provided for Windows developers and
generates the PDFIO1 DLL. You can also use NuGet to install the `pdfio_native`
package.
Xcode Project
-------------
There is also an Xcode project ("pdfio.xcodeproj") you can use on macOS which
generates a static library that will be installed under "/usr/local" with:
sudo xcodebuild install
Legal Stuff
-----------
PDFio is Copyright © 2021-2025 by Michael R Sweet.
This software is licensed under the Apache License Version 2.0 with an
(optional) exception to allow linking against GPL2/LGPL2 software. See the
files "LICENSE" and "NOTICE" for more information.

View File

@@ -5,12 +5,40 @@ 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.
What is a Security Bug?
-----------------------
Not every bug is a security bug.
Certain bugs that might be considered security bugs in a program, such as bugs
that lead to a Denial of Service, are *not* considered security bugs simply
because this project *does not provide a service*. Some might argue that, "my
server uses this library and the bug in this library causes a denial of service
for my server", however it is the responsibility of the *server* to protect
against DoS attacks, not a subordinate library, because only the server knows
what is an appropriate use of memory, CPU, time, and other resources.
Similarly, bugs caused by incorrect API usage such as passing `NULL` pointers
where such pointers are not allowed, passing the wrong kinds of pointers or
objects to an API, or using a private API are not security bugs because they
are not caused by an attacker but by the developer.
Finally, bugs that only exist in unreleased (non-production) or inactive code
are not security bugs because they do not affect ordinary users. See the
[Supported Versions](#supported-versions) section below for more information
about what versions of the project are covered by this security policy.
If the bug you've found falls into one of these three categories, please report
the bug as an the ordinary project issue at
<https://github.com/michaelrsweet/pdfio/issues>.
Reporting a Security Bug Reporting a Security Bug
------------------------ ------------------------
For the purposes of this project, a security bug is a software defect that 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 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 host computer or to causes the software to crash. Such defects should be
reported to the project security advisory page at reported to the project security advisory page at
<https://github.com/michaelrsweet/pdfio/security/advisories>. <https://github.com/michaelrsweet/pdfio/security/advisories>.
@@ -18,11 +46,6 @@ 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 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. 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
---------------------- ----------------------
@@ -68,6 +91,9 @@ example:
1.0b2 1.0b2
1.0rc1 1.0rc1
Pre-release code in a Git branch ("master", "v1.6.x", etc.) is similarly *not*
production release code.
PGP Public Key PGP Public Key
-------------- --------------

78
config.guess vendored
View File

@@ -1,10 +1,10 @@
#! /bin/sh #! /bin/sh
# Attempt to guess a canonical system name. # Attempt to guess a canonical system name.
# Copyright 1992-2022 Free Software Foundation, Inc. # Copyright 1992-2025 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale # shellcheck disable=SC2006,SC2268 # see below for rationale
timestamp='2022-09-17' timestamp='2025-07-10'
# This file is free software; you can redistribute it and/or modify it # This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by # under the terms of the GNU General Public License as published by
@@ -47,7 +47,7 @@ me=`echo "$0" | sed -e 's,.*/,,'`
usage="\ usage="\
Usage: $0 [OPTION] Usage: $0 [OPTION]
Output the configuration name of the system \`$me' is run on. Output the configuration name of the system '$me' is run on.
Options: Options:
-h, --help print this help, then exit -h, --help print this help, then exit
@@ -60,13 +60,13 @@ version="\
GNU config.guess ($timestamp) GNU config.guess ($timestamp)
Originally written by Per Bothner. Originally written by Per Bothner.
Copyright 1992-2022 Free Software Foundation, Inc. Copyright 1992-2025 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
help=" help="
Try \`$me --help' for more information." Try '$me --help' for more information."
# Parse command line # Parse command line
while test $# -gt 0 ; do while test $# -gt 0 ; do
@@ -102,8 +102,8 @@ GUESS=
# temporary files to be created and, as you can see below, it is a # temporary files to be created and, as you can see below, it is a
# headache to deal with in a portable fashion. # headache to deal with in a portable fashion.
# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still
# use `HOST_CC' if defined, but it is deprecated. # use 'HOST_CC' if defined, but it is deprecated.
# Portable tmp directory creation inspired by the Autoconf team. # Portable tmp directory creation inspired by the Autoconf team.
@@ -123,7 +123,7 @@ set_cc_for_build() {
dummy=$tmp/dummy dummy=$tmp/dummy
case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
,,) echo "int x;" > "$dummy.c" ,,) echo "int x;" > "$dummy.c"
for driver in cc gcc c89 c99 ; do for driver in cc gcc c17 c99 c89 ; do
if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
CC_FOR_BUILD=$driver CC_FOR_BUILD=$driver
break break
@@ -155,6 +155,9 @@ Linux|GNU|GNU/*)
set_cc_for_build set_cc_for_build
cat <<-EOF > "$dummy.c" cat <<-EOF > "$dummy.c"
#if defined(__ANDROID__)
LIBC=android
#else
#include <features.h> #include <features.h>
#if defined(__UCLIBC__) #if defined(__UCLIBC__)
LIBC=uclibc LIBC=uclibc
@@ -162,6 +165,8 @@ Linux|GNU|GNU/*)
LIBC=dietlibc LIBC=dietlibc
#elif defined(__GLIBC__) #elif defined(__GLIBC__)
LIBC=gnu LIBC=gnu
#elif defined(__LLVM_LIBC__)
LIBC=llvm
#else #else
#include <stdarg.h> #include <stdarg.h>
/* First heuristic to detect musl libc. */ /* First heuristic to detect musl libc. */
@@ -169,6 +174,7 @@ Linux|GNU|GNU/*)
LIBC=musl LIBC=musl
#endif #endif
#endif #endif
#endif
EOF EOF
cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
eval "$cc_set_libc" eval "$cc_set_libc"
@@ -459,7 +465,7 @@ case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
UNAME_RELEASE=`uname -v` UNAME_RELEASE=`uname -v`
;; ;;
esac esac
# Japanese Language versions have a version number like `4.1.3-JL'. # Japanese Language versions have a version number like '4.1.3-JL'.
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
GUESS=sparc-sun-sunos$SUN_REL GUESS=sparc-sun-sunos$SUN_REL
;; ;;
@@ -628,7 +634,8 @@ EOF
sed 's/^ //' << EOF > "$dummy.c" sed 's/^ //' << EOF > "$dummy.c"
#include <sys/systemcfg.h> #include <sys/systemcfg.h>
main() int
main ()
{ {
if (!__power_pc()) if (!__power_pc())
exit(1); exit(1);
@@ -712,7 +719,8 @@ EOF
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
int main () int
main ()
{ {
#if defined(_SC_KERNEL_BITS) #if defined(_SC_KERNEL_BITS)
long bits = sysconf(_SC_KERNEL_BITS); long bits = sysconf(_SC_KERNEL_BITS);
@@ -904,7 +912,7 @@ EOF
fi fi
;; ;;
*:FreeBSD:*:*) *:FreeBSD:*:*)
UNAME_PROCESSOR=`/usr/bin/uname -p` UNAME_PROCESSOR=`uname -p`
case $UNAME_PROCESSOR in case $UNAME_PROCESSOR in
amd64) amd64)
UNAME_PROCESSOR=x86_64 ;; UNAME_PROCESSOR=x86_64 ;;
@@ -976,7 +984,27 @@ EOF
GUESS=$UNAME_MACHINE-unknown-minix GUESS=$UNAME_MACHINE-unknown-minix
;; ;;
aarch64:Linux:*:*) aarch64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC set_cc_for_build
CPU=$UNAME_MACHINE
LIBCABI=$LIBC
if test "$CC_FOR_BUILD" != no_compiler_found; then
ABI=64
sed 's/^ //' << EOF > "$dummy.c"
#ifdef __ARM_EABI__
#ifdef __ARM_PCS_VFP
ABI=eabihf
#else
ABI=eabi
#endif
#endif
EOF
cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
eval "$cc_set_abi"
case $ABI in
eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;;
esac
fi
GUESS=$CPU-unknown-linux-$LIBCABI
;; ;;
aarch64_be:Linux:*:*) aarch64_be:Linux:*:*)
UNAME_MACHINE=aarch64_be UNAME_MACHINE=aarch64_be
@@ -1042,6 +1070,15 @@ EOF
k1om:Linux:*:*) k1om:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;; ;;
kvx:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
kvx:cos:*:*)
GUESS=$UNAME_MACHINE-unknown-cos
;;
kvx:mbr:*:*)
GUESS=$UNAME_MACHINE-unknown-mbr
;;
loongarch32:Linux:*:* | loongarch64:Linux:*:*) loongarch32:Linux:*:* | loongarch64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;; ;;
@@ -1197,7 +1234,7 @@ EOF
GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
;; ;;
i*86:OS/2:*:*) i*86:OS/2:*:*)
# If we were able to find `uname', then EMX Unix compatibility # If we were able to find 'uname', then EMX Unix compatibility
# is probably installed. # is probably installed.
GUESS=$UNAME_MACHINE-pc-os2-emx GUESS=$UNAME_MACHINE-pc-os2-emx
;; ;;
@@ -1338,7 +1375,7 @@ EOF
GUESS=ns32k-sni-sysv GUESS=ns32k-sni-sysv
fi fi
;; ;;
PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort
# says <Richard.M.Bartel@ccMail.Census.GOV> # says <Richard.M.Bartel@ccMail.Census.GOV>
GUESS=i586-unisys-sysv4 GUESS=i586-unisys-sysv4
;; ;;
@@ -1560,6 +1597,12 @@ EOF
*:Unleashed:*:*) *:Unleashed:*:*)
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
;; ;;
x86_64:[Ii]ronclad:*:*|i?86:[Ii]ronclad:*:*)
GUESS=$UNAME_MACHINE-pc-ironclad-mlibc
;;
*:[Ii]ronclad:*:*)
GUESS=$UNAME_MACHINE-unknown-ironclad-mlibc
;;
esac esac
# Do we have a guess based on uname results? # Do we have a guess based on uname results?
@@ -1583,6 +1626,7 @@ cat > "$dummy.c" <<EOF
#endif #endif
#endif #endif
#endif #endif
int
main () main ()
{ {
#if defined (sony) #if defined (sony)
@@ -1767,8 +1811,8 @@ fi
exit 1 exit 1
# Local variables: # Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp) # eval: (add-hook 'before-save-hook 'time-stamp nil t)
# time-stamp-start: "timestamp='" # time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d" # time-stamp-format: "%Y-%02m-%02d"
# time-stamp-end: "'" # time-stamp-end: "'"
# End: # End:

953
config.sub vendored

File diff suppressed because it is too large Load Diff

399
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for pdfio 1.5.2. # Generated by GNU Autoconf 2.71 for pdfio 1.7.0.
# #
# Report bugs to <https://github.com/michaelrsweet/pdfio/issues>. # Report bugs to <https://github.com/michaelrsweet/pdfio/issues>.
# #
@@ -610,8 +610,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='pdfio' PACKAGE_NAME='pdfio'
PACKAGE_TARNAME='pdfio' PACKAGE_TARNAME='pdfio'
PACKAGE_VERSION='1.5.2' PACKAGE_VERSION='1.7.0'
PACKAGE_STRING='pdfio 1.5.2' PACKAGE_STRING='pdfio 1.7.0'
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues' PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
PACKAGE_URL='https://www.msweet.org/pdfio' PACKAGE_URL='https://www.msweet.org/pdfio'
@@ -647,13 +647,16 @@ ac_includes_default="\
#endif" #endif"
ac_header_c_list= ac_header_c_list=
enable_option_checking=no
ac_subst_vars='LTLIBOBJS ac_subst_vars='LTLIBOBJS
LIBOBJS LIBOBJS
WARNINGS WARNINGS
CSFLAGS CSFLAGS
LIBPDFIO_STATIC LIBPDFIO_STATIC
LIBPDFIO LIBPDFIO
PKGCONFIG_LIBPNG TTFDIR
subdirs
PKGCONFIG_REQUIRES_PRIVATE
PKGCONFIG_REQUIRES PKGCONFIG_REQUIRES
PKGCONFIG_LIBS_PRIVATE PKGCONFIG_LIBS_PRIVATE
PKGCONFIG_LIBS PKGCONFIG_LIBS
@@ -731,6 +734,7 @@ ac_subst_files=''
ac_user_opts=' ac_user_opts='
enable_option_checking enable_option_checking
enable_libpng enable_libpng
enable_libwebp
enable_static enable_static
enable_shared enable_shared
enable_debug enable_debug
@@ -747,7 +751,7 @@ CFLAGS
LDFLAGS LDFLAGS
LIBS LIBS
CPPFLAGS' CPPFLAGS'
ac_subdirs_all='ttf'
# Initialize some variables set by options. # Initialize some variables set by options.
ac_init_help= ac_init_help=
@@ -1295,7 +1299,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures pdfio 1.5.2 to adapt to many kinds of systems. \`configure' configures pdfio 1.7.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1361,7 +1365,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of pdfio 1.5.2:";; short | recursive ) echo "Configuration of pdfio 1.7.0:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@@ -1369,7 +1373,9 @@ Optional Features:
--disable-option-checking ignore unrecognized --enable/--with options --disable-option-checking ignore unrecognized --enable/--with options
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--enable-libpng use libpng for pdfioFileCreateImageObjFromFile, --disable-libpng use libpng for pdfioFileCreateImageObjFromFile,
default=auto
--disable-libwebp use libwebp for pdfioFileCreateImageObjFromFile,
default=auto default=auto
--disable-static do not install static library --disable-static do not install static library
--enable-shared install shared library --enable-shared install shared library
@@ -1460,7 +1466,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
pdfio configure 1.5.2 pdfio configure 1.7.0
generated by GNU Autoconf 2.71 generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc. Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1513,6 +1519,39 @@ fi
} # ac_fn_c_try_compile } # ac_fn_c_try_compile
# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists and can be compiled using the include files in
# INCLUDES, setting the cache variable VAR accordingly.
ac_fn_c_check_header_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
eval "$3=yes"
else $as_nop
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
# ac_fn_c_try_link LINENO # ac_fn_c_try_link LINENO
# ----------------------- # -----------------------
# Try to link conftest.$ac_ext, and return whether this succeeded. # Try to link conftest.$ac_ext, and return whether this succeeded.
@@ -1621,39 +1660,6 @@ printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func } # ac_fn_c_check_func
# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists and can be compiled using the include files in
# INCLUDES, setting the cache variable VAR accordingly.
ac_fn_c_check_header_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
eval "$3=yes"
else $as_nop
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
ac_configure_args_raw= ac_configure_args_raw=
for ac_arg for ac_arg
do do
@@ -1678,7 +1684,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by pdfio $as_me 1.5.2, which was It was created by pdfio $as_me 1.7.0, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw $ $0$ac_configure_args_raw
@@ -2434,9 +2440,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
PDFIO_VERSION="1.5.2" PDFIO_VERSION="1.7.0"
PDFIO_VERSION_MAJOR="`echo 1.5.2 | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo 1.7.0 | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo 1.5.2 | awk -F. '{printf("%d\n",$2);}'`" PDFIO_VERSION_MINOR="`echo 1.7.0 | awk -F. '{printf("%d\n",$2);}'`"
@@ -3940,6 +3946,43 @@ printf "%s\n" "using $INSTALL" >&6; }
ac_header= ac_cache=
for ac_item in $ac_header_c_list
do
if test $ac_cache; then
ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
printf "%s\n" "#define $ac_item 1" >> confdefs.h
fi
ac_header= ac_cache=
elif test $ac_header; then
ac_cache=$ac_item
else
ac_header=$ac_item
fi
done
if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
then :
printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
fi
ac_fn_c_check_header_compile "$LINENO" "math.h" "ac_cv_header_math_h" "$ac_includes_default"
if test "x$ac_cv_header_math_h" = xyes
then :
fi
ac_fn_c_check_func "$LINENO" "timegm" "ac_cv_func_timegm" ac_fn_c_check_func "$LINENO" "timegm" "ac_cv_func_timegm"
if test "x$ac_cv_func_timegm" = xyes if test "x$ac_cv_func_timegm" = xyes
then : then :
@@ -4101,44 +4144,46 @@ fi
PKGCONFIG_CFLAGS="-I\${includedir}" PKGCONFIG_CFLAGS="-I\${includedir}"
PKGCONFIG_LIBS="-L\${libdir} -lpdfio" PKGCONFIG_LIBS="-L\${libdir} -lpdfio"
PKGCONFIG_LIBS_PRIVATE="-lm" PKGCONFIG_LIBS_PRIVATE="-lm"
PKGCONFIG_REQUIRES="zlib" PKGCONFIG_REQUIRES=""
PKGCONFIG_REQUIRES_PRIVATE="ttf"
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ttf library" >&5
printf %s "checking for ttf library... " >&6; }
if $PKGCONFIG --exists ttf
then :
# Use installed TTF library...
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
CPPFLAGS="$CPPFLAGS $($PKGCONFIG --cflags ttf)"
TTFDIR=""
LIBS="$($PKGCONFIG --libs ttf) $LIBS"
else $as_nop
# Use embedded TTF library...
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using embedded version" >&5
printf "%s\n" "no, using embedded version" >&6; }
CPPFLAGS="$CPPFLAGS -Ittf"
TTFDIR="ttf"
LIBS="-Lttf \`PKG_CONFIG_PATH=ttf $PKGCONFIG --libs ttf\` $LIBS"
subdirs="$subdirs ttf"
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib via pkg-config" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib via pkg-config" >&5
printf %s "checking for zlib via pkg-config... " >&6; } printf %s "checking for zlib via pkg-config... " >&6; }
ac_header= ac_cache=
for ac_item in $ac_header_c_list
do
if test $ac_cache; then
ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
printf "%s\n" "#define $ac_item 1" >> confdefs.h
fi
ac_header= ac_cache=
elif test $ac_header; then
ac_cache=$ac_item
else
ac_header=$ac_item
fi
done
if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
then :
printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
fi
if $PKGCONFIG --exists zlib if $PKGCONFIG --exists zlib
then : then :
@@ -4146,6 +4191,7 @@ then :
printf "%s\n" "yes" >&6; } printf "%s\n" "yes" >&6; }
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS" CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
LIBS="$($PKGCONFIG --libs zlib) $LIBS" LIBS="$($PKGCONFIG --libs zlib) $LIBS"
PKGCONFIG_REQUIRES_PRIVATE="$PKGCONFIG_REQUIRES_PRIVATE, zlib"
else $as_nop else $as_nop
@@ -4208,7 +4254,6 @@ then :
fi fi
PKGCONFIG_REQUIRES=""
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE" PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
fi fi
@@ -4221,9 +4266,6 @@ then :
fi fi
PKGCONFIG_LIBPNG=""
if test "x$PKGCONFIG" != x -a x$enable_libpng != xno if test "x$PKGCONFIG" != x -a x$enable_libpng != xno
then : then :
@@ -4239,8 +4281,7 @@ printf "%s\n" "#define HAVE_LIBPNG 1" >>confdefs.h
CPPFLAGS="$($PKGCONFIG --cflags libpng16) -DHAVE_LIBPNG=1 $CPPFLAGS" CPPFLAGS="$($PKGCONFIG --cflags libpng16) -DHAVE_LIBPNG=1 $CPPFLAGS"
LIBS="$($PKGCONFIG --libs libpng16) -lz $LIBS" LIBS="$($PKGCONFIG --libs libpng16) -lz $LIBS"
PKGCONFIG_LIBS_PRIVATE="$($PKGCONFIG --libs libpng16) $PKGCONFIG_LIBS_PRIVATE" PKGCONFIG_REQUIRES_PRIVATE="libpng >= 1.6, $PKGCONFIG_REQUIRES_PRIVATE"
PKGCONFIG_REQUIRES="libpng >= 1.6,$PKGCONFIG_REQUIRES"
else $as_nop else $as_nop
@@ -4263,6 +4304,51 @@ then :
fi fi
# Check whether --enable-libwebp was given.
if test ${enable_libwebp+y}
then :
enableval=$enable_libwebp;
fi
if test "x$PKGCONFIG" != x -a x$enable_libwebp != xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libwebp" >&5
printf %s "checking for libwebp... " >&6; }
if $PKGCONFIG --exists libwebp
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; };
printf "%s\n" "#define HAVE_LIBWEBP 1" >>confdefs.h
CPPFLAGS="$($PKGCONFIG --cflags libwebp) -DHAVE_LIBWEBP=1 $CPPFLAGS"
LIBS="$($PKGCONFIG --libs libwebp) -lz $LIBS"
PKGCONFIG_REQUIRES_PRIVATE="libwebp, $PKGCONFIG_REQUIRES_PRIVATE"
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; };
if test x$enable_libwebp = xyes
then :
as_fn_error $? "libwebp-dev required for --enable-libwebp." "$LINENO" 5
fi
fi
elif test x$enable_libwebp = xyes
then :
as_fn_error $? "libwebp-dev required for --enable-libwebp." "$LINENO" 5
fi
# Check whether --enable-static was given. # Check whether --enable-static was given.
if test ${enable_static+y} if test ${enable_static+y}
then : then :
@@ -4307,6 +4393,8 @@ else $as_nop
LIBPDFIO_STATIC="" LIBPDFIO_STATIC=""
PKGCONFIG_LIBS="$PKGCONFIG_LIBS $PKGCONFIG_LIBS_PRIVATE" PKGCONFIG_LIBS="$PKGCONFIG_LIBS $PKGCONFIG_LIBS_PRIVATE"
PKGCONFIG_LIBS_PRIVATE="" PKGCONFIG_LIBS_PRIVATE=""
PKGCONFIG_REQUIRES="$PKGCONFIG_REQUIRES_PRIVATE"
PKGCONFIG_REQUIRES_PRIVATE=""
fi fi
@@ -5099,7 +5187,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by pdfio $as_me 1.5.2, which was This file was extended by pdfio $as_me 1.7.0, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@@ -5155,7 +5243,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped' ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\ ac_cs_version="\\
pdfio config.status 1.5.2 pdfio config.status 1.7.0
configured by $0, generated by GNU Autoconf 2.71, configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"
@@ -5711,6 +5799,149 @@ if test "$no_create" != yes; then
# would make configure fail if this is the last instruction. # would make configure fail if this is the last instruction.
$ac_cs_success || as_fn_exit 1 $ac_cs_success || as_fn_exit 1
fi fi
#
# CONFIG_SUBDIRS section.
#
if test "$no_recursion" != yes; then
# Remove --cache-file, --srcdir, and --disable-option-checking arguments
# so they do not pile up.
ac_sub_configure_args=
ac_prev=
eval "set x $ac_configure_args"
shift
for ac_arg
do
if test -n "$ac_prev"; then
ac_prev=
continue
fi
case $ac_arg in
-cache-file | --cache-file | --cache-fil | --cache-fi \
| --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
ac_prev=cache_file ;;
-cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
| --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \
| --c=*)
;;
--config-cache | -C)
;;
-srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
ac_prev=srcdir ;;
-srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
;;
-prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
ac_prev=prefix ;;
-prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
;;
--disable-option-checking)
;;
*)
case $ac_arg in
*\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
esac
as_fn_append ac_sub_configure_args " '$ac_arg'" ;;
esac
done
# Always prepend --prefix to ensure using the same prefix
# in subdir configurations.
ac_arg="--prefix=$prefix"
case $ac_arg in
*\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
esac
ac_sub_configure_args="'$ac_arg' $ac_sub_configure_args"
# Pass --silent
if test "$silent" = yes; then
ac_sub_configure_args="--silent $ac_sub_configure_args"
fi
# Always prepend --disable-option-checking to silence warnings, since
# different subdirs can have different --enable and --with options.
ac_sub_configure_args="--disable-option-checking $ac_sub_configure_args"
ac_popdir=`pwd`
for ac_dir in : $subdirs; do test "x$ac_dir" = x: && continue
# Do not complain, so a configure script can configure whichever
# parts of a large source tree are present.
test -d "$srcdir/$ac_dir" || continue
ac_msg="=== configuring in $ac_dir (`pwd`/$ac_dir)"
printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_msg" >&5
printf "%s\n" "$ac_msg" >&6
as_dir="$ac_dir"; as_fn_mkdir_p
ac_builddir=.
case "$ac_dir" in
.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
*)
ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
# A ".." for each directory in $ac_dir_suffix.
ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
case $ac_top_builddir_sub in
"") ac_top_builddir_sub=. ac_top_build_prefix= ;;
*) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
esac ;;
esac
ac_abs_top_builddir=$ac_pwd
ac_abs_builddir=$ac_pwd$ac_dir_suffix
# for backward compatibility:
ac_top_builddir=$ac_top_build_prefix
case $srcdir in
.) # We are building in place.
ac_srcdir=.
ac_top_srcdir=$ac_top_builddir_sub
ac_abs_top_srcdir=$ac_pwd ;;
[\\/]* | ?:[\\/]* ) # Absolute name.
ac_srcdir=$srcdir$ac_dir_suffix;
ac_top_srcdir=$srcdir
ac_abs_top_srcdir=$srcdir ;;
*) # Relative name.
ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
ac_top_srcdir=$ac_top_build_prefix$srcdir
ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
esac
ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
cd "$ac_dir"
# Check for configure.gnu first; this name is used for a wrapper for
# Metaconfig's "Configure" on case-insensitive file systems.
if test -f "$ac_srcdir/configure.gnu"; then
ac_sub_configure=$ac_srcdir/configure.gnu
elif test -f "$ac_srcdir/configure"; then
ac_sub_configure=$ac_srcdir/configure
else
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: no configuration information is in $ac_dir" >&5
printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2;}
ac_sub_configure=
fi
# The recursion is here.
if test -n "$ac_sub_configure"; then
# Make the cache file name correct relative to the subdirectory.
case $cache_file in
[\\/]* | ?:[\\/]* ) ac_sub_cache_file=$cache_file ;;
*) # Relative name.
ac_sub_cache_file=$ac_top_build_prefix$cache_file ;;
esac
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: running $SHELL $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&5
printf "%s\n" "$as_me: running $SHELL $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&6;}
# The eval makes quoting arguments work.
eval "\$SHELL \"\$ac_sub_configure\" $ac_sub_configure_args \
--cache-file=\"\$ac_sub_cache_file\" --srcdir=\"\$ac_srcdir\"" ||
as_fn_error $? "$ac_sub_configure failed for $ac_dir" "$LINENO" 5
fi
cd "$ac_popdir"
done
fi
if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}

View File

@@ -1,7 +1,7 @@
dnl dnl
dnl Configuration script for PDFio dnl Configuration script for PDFio
dnl dnl
dnl Copyright © 2023-2025 by Michael R Sweet dnl Copyright © 2023-2026 by Michael R Sweet
dnl dnl
dnl Licensed under Apache License v2.0. See the file "LICENSE" for more dnl Licensed under Apache License v2.0. See the file "LICENSE" for more
dnl information. dnl information.
@@ -21,7 +21,7 @@ AC_PREREQ([2.70])
dnl Package name and version... dnl Package name and version...
AC_INIT([pdfio], [1.5.2], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio]) AC_INIT([pdfio], [1.7.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
PDFIO_VERSION="AC_PACKAGE_VERSION" PDFIO_VERSION="AC_PACKAGE_VERSION"
PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"
@@ -88,6 +88,10 @@ AC_SUBST([INSTALL])
AC_MSG_RESULT([using $INSTALL]) AC_MSG_RESULT([using $INSTALL])
dnl Standard C stuff...
AC_CHECK_HEADER([math.h])
dnl Check for date/time functionality... dnl Check for date/time functionality...
AC_CHECK_FUNC([timegm], [ AC_CHECK_FUNC([timegm], [
AC_DEFINE([HAVE_TIMEGM], [1], [Do we have the timegm function?]) AC_DEFINE([HAVE_TIMEGM], [1], [Do we have the timegm function?])
@@ -115,11 +119,32 @@ AC_PATH_TOOL([PKGCONFIG], [pkg-config])
PKGCONFIG_CFLAGS="-I\${includedir}" PKGCONFIG_CFLAGS="-I\${includedir}"
PKGCONFIG_LIBS="-L\${libdir} -lpdfio" PKGCONFIG_LIBS="-L\${libdir} -lpdfio"
PKGCONFIG_LIBS_PRIVATE="-lm" PKGCONFIG_LIBS_PRIVATE="-lm"
PKGCONFIG_REQUIRES="zlib" PKGCONFIG_REQUIRES=""
PKGCONFIG_REQUIRES_PRIVATE="ttf"
AC_SUBST([PKGCONFIG_CFLAGS]) AC_SUBST([PKGCONFIG_CFLAGS])
AC_SUBST([PKGCONFIG_LIBS]) AC_SUBST([PKGCONFIG_LIBS])
AC_SUBST([PKGCONFIG_LIBS_PRIVATE]) AC_SUBST([PKGCONFIG_LIBS_PRIVATE])
AC_SUBST([PKGCONFIG_REQUIRES]) AC_SUBST([PKGCONFIG_REQUIRES])
AC_SUBST([PKGCONFIG_REQUIRES_PRIVATE])
dnl TTF library for font support...
AC_MSG_CHECKING([for ttf library])
AS_IF([$PKGCONFIG --exists ttf], [
# Use installed TTF library...
AC_MSG_RESULT([yes])
CPPFLAGS="$CPPFLAGS $($PKGCONFIG --cflags ttf)"
TTFDIR=""
LIBS="$($PKGCONFIG --libs ttf) $LIBS"
], [
# Use embedded TTF library...
AC_MSG_RESULT([no, using embedded version])
CPPFLAGS="$CPPFLAGS -Ittf"
TTFDIR="ttf"
LIBS="-Lttf \`PKG_CONFIG_PATH=ttf $PKGCONFIG --libs ttf\` $LIBS"
AC_CONFIG_SUBDIRS([ttf])
])
AC_SUBST([TTFDIR])
dnl ZLIB dnl ZLIB
@@ -128,6 +153,7 @@ AS_IF([$PKGCONFIG --exists zlib], [
AC_MSG_RESULT([yes]) AC_MSG_RESULT([yes])
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS" CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
LIBS="$($PKGCONFIG --libs zlib) $LIBS" LIBS="$($PKGCONFIG --libs zlib) $LIBS"
PKGCONFIG_REQUIRES_PRIVATE="$PKGCONFIG_REQUIRES_PRIVATE, zlib"
],[ ],[
AC_MSG_RESULT([no]) AC_MSG_RESULT([no])
AC_CHECK_HEADER([zlib.h]) AC_CHECK_HEADER([zlib.h])
@@ -137,16 +163,12 @@ AS_IF([$PKGCONFIG --exists zlib], [
AC_MSG_ERROR([Sorry, this software requires zlib 1.1 or higher.]) AC_MSG_ERROR([Sorry, this software requires zlib 1.1 or higher.])
]) ])
PKGCONFIG_REQUIRES=""
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE" PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
]) ])
dnl libpng... dnl libpng...
AC_ARG_ENABLE([libpng], AS_HELP_STRING([--enable-libpng], [use libpng for pdfioFileCreateImageObjFromFile, default=auto])) AC_ARG_ENABLE([libpng], AS_HELP_STRING([--disable-libpng], [use libpng for pdfioFileCreateImageObjFromFile, default=auto]))
PKGCONFIG_LIBPNG=""
AC_SUBST([PKGCONFIG_LIBPNG])
AS_IF([test "x$PKGCONFIG" != x -a x$enable_libpng != xno], [ AS_IF([test "x$PKGCONFIG" != x -a x$enable_libpng != xno], [
AC_MSG_CHECKING([for libpng-1.6.x]) AC_MSG_CHECKING([for libpng-1.6.x])
@@ -155,8 +177,7 @@ AS_IF([test "x$PKGCONFIG" != x -a x$enable_libpng != xno], [
AC_DEFINE([HAVE_LIBPNG], 1, [Have PNG library?]) AC_DEFINE([HAVE_LIBPNG], 1, [Have PNG library?])
CPPFLAGS="$($PKGCONFIG --cflags libpng16) -DHAVE_LIBPNG=1 $CPPFLAGS" CPPFLAGS="$($PKGCONFIG --cflags libpng16) -DHAVE_LIBPNG=1 $CPPFLAGS"
LIBS="$($PKGCONFIG --libs libpng16) -lz $LIBS" LIBS="$($PKGCONFIG --libs libpng16) -lz $LIBS"
PKGCONFIG_LIBS_PRIVATE="$($PKGCONFIG --libs libpng16) $PKGCONFIG_LIBS_PRIVATE" PKGCONFIG_REQUIRES_PRIVATE="libpng >= 1.6, $PKGCONFIG_REQUIRES_PRIVATE"
PKGCONFIG_REQUIRES="libpng >= 1.6,$PKGCONFIG_REQUIRES"
], [ ], [
AC_MSG_RESULT([no]); AC_MSG_RESULT([no]);
AS_IF([test x$enable_libpng = xyes], [ AS_IF([test x$enable_libpng = xyes], [
@@ -168,6 +189,28 @@ AS_IF([test "x$PKGCONFIG" != x -a x$enable_libpng != xno], [
]) ])
dnl libwebp...
AC_ARG_ENABLE([libwebp], AS_HELP_STRING([--disable-libwebp], [use libwebp for pdfioFileCreateImageObjFromFile, default=auto]))
AS_IF([test "x$PKGCONFIG" != x -a x$enable_libwebp != xno], [
AC_MSG_CHECKING([for libwebp])
AS_IF([$PKGCONFIG --exists libwebp], [
AC_MSG_RESULT([yes]);
AC_DEFINE([HAVE_LIBWEBP], 1, [Have WebP library?])
CPPFLAGS="$($PKGCONFIG --cflags libwebp) -DHAVE_LIBWEBP=1 $CPPFLAGS"
LIBS="$($PKGCONFIG --libs libwebp) -lz $LIBS"
PKGCONFIG_REQUIRES_PRIVATE="libwebp, $PKGCONFIG_REQUIRES_PRIVATE"
], [
AC_MSG_RESULT([no]);
AS_IF([test x$enable_libwebp = xyes], [
AC_MSG_ERROR([libwebp-dev required for --enable-libwebp.])
])
])
], [test x$enable_libwebp = xyes], [
AC_MSG_ERROR([libwebp-dev required for --enable-libwebp.])
])
dnl Library target... dnl Library target...
AC_ARG_ENABLE([static], AS_HELP_STRING([--disable-static], [do not install static library])) AC_ARG_ENABLE([static], AS_HELP_STRING([--disable-static], [do not install static library]))
AC_ARG_ENABLE([shared], AS_HELP_STRING([--enable-shared], [install shared library])) AC_ARG_ENABLE([shared], AS_HELP_STRING([--enable-shared], [install shared library]))
@@ -189,6 +232,8 @@ AS_IF([test x$enable_shared = xyes], [
LIBPDFIO_STATIC="" LIBPDFIO_STATIC=""
PKGCONFIG_LIBS="$PKGCONFIG_LIBS $PKGCONFIG_LIBS_PRIVATE" PKGCONFIG_LIBS="$PKGCONFIG_LIBS $PKGCONFIG_LIBS_PRIVATE"
PKGCONFIG_LIBS_PRIVATE="" PKGCONFIG_LIBS_PRIVATE=""
PKGCONFIG_REQUIRES="$PKGCONFIG_REQUIRES_PRIVATE"
PKGCONFIG_REQUIRES_PRIVATE=""
]) ])
AC_SUBST([LIBPDFIO]) AC_SUBST([LIBPDFIO])

View File

@@ -1,4 +1,4 @@
.TH pdfio 3 "pdf read/write library" "2025-04-12" "pdf read/write library" .TH pdfio 3 "pdf read/write library" "2026-02-04" "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\-2025 by Michael R Sweet and is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information. Copyright \[co] 2021\-2026 by Michael R Sweet. PDFio is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GNU GPL2\-only software. See the files LICENSE and NOTICE for more information.
.SS Requirements .SS Requirements
.PP .PP
PDFio requires the following to build the software: PDFio requires the following to build the software:
@@ -52,14 +52,26 @@ A POSIX\-compliant sh program
.IP \(bu 5 .IP \(bu 5
.PP .PP
ZLIB (https://www.zlib.net/) 1.0 or higher libpng (https://www.libpng.org/) 1.6 or later for full PNG image support (optional)
.IP \(bu 5
.PP .PP
PDFio will also use libpng 1.6 or higher (https://www.libpng.org/) to provide enhanced PNG image support. libwebp (https://developers.google.com/speed/webp) 1.0 or later for WebP image support (optional)
.IP \(bu 5
.PP
ZLIB (https://www.zlib.net/) 1.1 or later for compression support
.PP .PP
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided. IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
.SS Installing PDFio .PP
On a stock Ubuntu install, the following command will install the various prerequisites:
.nf
sudo apt\-get install build\-essential libpng\-dev libwebp\-dev zlib1g\-dev
.fi
.SS Building and Installing PDFio on Linux/Unix
.PP .PP
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: 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
@@ -99,16 +111,22 @@ Other configure options can be found using the \-\-help option:
\./configure \-\-help \./configure \-\-help
.fi .fi
.SS Visual Studio Project .SS Building on Windows with Visual Studio
.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 the PDFIO1 DLL. You can also use NuGet to install the pdfio_native package.
.SS Xcode Project
.PP .PP
There is also an Xcode project ("pdfio.xcodeproj") you can use on macOS which generates a static library that will be installed under "/usr/local" with: You can build and run the testpdfio unit tests target from within Visual Studio to verify the functioning of the library.
.SS Building on macOS with Xcode
.PP
The Xcode project (pdfio.xcodeproj) is provided for macOS developer which generates a static library that will be installed under "/usr/local". Run the following command to build and install PDFio on macOS:
.nf .nf
sudo xcodebuild install sudo xcodebuild install
.fi .fi
.PP
Alternately you can add the Xcode project to a workspace and reference the PDFio library target as a dependency in your own project.
.PP
You can build and run the testpdfio unit tests target from within Xcode to verify the functioning of the library.
.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:
@@ -325,7 +343,7 @@ where the five arguments to the function are the filename ("myinputfile.pdf"), a
} }
.fi .fi
.PP .PP
The error callback is called for both errors and warnings and accepts the pdfio_file_t pointer, a message string, and the callback pointer value, for example: The error callback is called for both errors and warnings and accepts the pdfio_file_t pointer, a message string, and the callback pointer value. It returns true to continue processing the file or false to stop, for example:
.nf .nf
bool bool
@@ -335,12 +353,15 @@ The error callback is called for both errors and warnings and accepts the pdfio_
fprintf(stderr, "%s: %s\\n", pdfioFileGetName(pdf), message); fprintf(stderr, "%s: %s\\n", pdfioFileGetName(pdf), message);
// Return false to treat warnings as errors // Return true for warning messages (continue) and false for errors (stop)
return (false); return (!strncmp(message, "WARNING:", 8));
} }
.fi .fi
.PP .PP
The default error callback (NULL) does the equivalent of the above. The default error callback (NULL) does the equivalent of the above.
.PP
Note: Many errors are unrecoverable, so PDFio ignores the return value from the error callback and always stops processing the PDF file. Warning messages start with the prefix "WARNING:" while errors have no prefix.
.PP .PP
Each PDF file contains one or more pages. The pdfioFileGetNumPages function returns the number of pages in the file while the pdfioFileGetPage function gets the specified page in the PDF file: Each PDF file contains one or more pages. The pdfioFileGetNumPages function returns the number of pages in the file while the pdfioFileGetPage function gets the specified page in the PDF file:
.nf .nf
@@ -358,41 +379,28 @@ Each PDF file contains one or more pages. The pdfioFileGetNumPages function retu
} }
.fi .fi
.PP .PP
Each page is represented by a "page tree" object (what pdfioFileGetPage returns) that specifies information about the page and one or more "content" objects that contain the images, fonts, text, and graphics that appear on the page. Use the pdfioPageGetNumStreams and pdfioPageOpenStream functions to access the content streams for each page, and pdfioObjGetDict to get the associated page object dictionary. For example, if you want to display the media and crop boxes for a given page: Each page is represented by a "page tree" object (what pdfioFileGetPage returns) that specifies information about the page and one or more "content" objects that contain the images, fonts, text, and graphics that appear on the page. Use the pdfioPageGetNumStreams and pdfioPageOpenStream functions to access the content streams for each page, pdfioObjGetDict to get the associated page object dictionary, and pdfioPageGetArray, pdfioPageGetBinary, pdfioPageGetBoolean, pdfioPageGetDate, pdfioPageGetDict, pdfioPageGetName, pdfioPageGetObj, pdfioPageGetRect, and pdfioPageGetString to get a value from the page object dictionary or its parents. For example, if you want to display the media and crop boxes for a given page:
.nf .nf
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
size_t i; // Looping var size_t i; // Looping var
size_t count; // Number of pages size_t count; // Number of pages
pdfio_obj_t *page; // Current page pdfio_obj_t *page; // Current page
pdfio_dict_t *dict; // Current page dictionary pdfio_rect_t media_box; // MediaBox values
pdfio_array_t *media_box; // MediaBox array pdfio_rect_t crop_box; // CropBox values
double media_values[4]; // MediaBox values
pdfio_array_t *crop_box; // CropBox array
double crop_values[4]; // CropBox values
// Iterate the pages in the PDF file // Iterate the pages in the PDF file
for (i = 0, count = pdfioFileGetNumPages(pdf); i < count; i ++) for (i = 0, count = pdfioFileGetNumPages(pdf); i < count; i ++)
{ {
page = pdfioFileGetPage(pdf, i); page = pdfioFileGetPage(pdf, i);
dict = pdfioObjGetDict(page);
media_box = pdfioDictGetArray(dict, "MediaBox"); pdfioPageGetRect(page, "MediaBox", &media_box);
media_values[0] = pdfioArrayGetNumber(media_box, 0); pdfioPageGetRect(page, "CropBox", &crop_box);
media_values[1] = pdfioArrayGetNumber(media_box, 1);
media_values[2] = pdfioArrayGetNumber(media_box, 2);
media_values[3] = pdfioArrayGetNumber(media_box, 3);
crop_box = pdfioDictGetArray(dict, "CropBox");
crop_values[0] = pdfioArrayGetNumber(crop_box, 0);
crop_values[1] = pdfioArrayGetNumber(crop_box, 1);
crop_values[2] = pdfioArrayGetNumber(crop_box, 2);
crop_values[3] = pdfioArrayGetNumber(crop_box, 3);
printf("Page %u: MediaBox=[%g %g %g %g], CropBox=[%g %g %g %g]\\n", printf("Page %u: MediaBox=[%g %g %g %g], CropBox=[%g %g %g %g]\\n",
(unsigned)(i + 1), (unsigned)(i + 1),
media_values[0], media_values[1], media_values[2], media_values[3], media_box.x1, media_box.y1, media_box.x2, media_box.y2,
crop_values[0], crop_values[1], crop_values[2], crop_values[3]); crop_box.x1, crop_box.y1, crop_box.x2, crop_box.y2);
} }
.fi .fi
.PP .PP
@@ -781,16 +789,13 @@ will create an object for a 1024x1024 RGBA image in memory, using the default co
.PP .PP
The "interpolate" argument specifies whether the colors in the image should be smoothed/interpolated when scaling. This is most useful for photographs but should be false for screenshot and barcode images. The "interpolate" argument specifies whether the colors in the image should be smoothed/interpolated when scaling. This is most useful for photographs but should be false for screenshot and barcode images.
.PP .PP
If you have a JPEG or PNG file, use the pdfioFileCreateImageObjFromFile function to copy the image into a PDF image object, for example: If you have a GIF, JPEG, PNG, or WebP file, use the pdfioFileCreateImageObjFromFile function to copy the image into a PDF image object, for example:
.nf .nf
pdfio_file_t *pdf = pdfioFileCreate(...); pdfio_file_t *pdf = pdfioFileCreate(...);
pdfio_obj_t *img = pdfio_obj_t *img =
pdfioFileCreateImageObjFromFile(pdf, "myphoto.jpg", /*interpolate*/true); pdfioFileCreateImageObjFromFile(pdf, "myphoto.jpg", /*interpolate*/true);
.fi .fi
.PP
Note: Currently pdfioFileCreateImageObjFromFile does not support 12 bit JPEG files or PNG files with an alpha channel.
.PP .PP
Page Dictionary Functions Page Dictionary Functions
.PP .PP
@@ -1029,6 +1034,45 @@ pdfioContentTextShowf draws a formatted string in a text block
pdfioContentTextShowJustified draws an array of literal strings with offsets between them pdfioContentTextShowJustified draws an array of literal strings with offsets between them
.SS Tagged and Marked PDF Content
.PP
Content in a page stream can be tagged to help a PDF reader application know the kind and organization of that content. Content inserted using the PDFio Page Stream Functions can be tagged by surrounding it with the pdfioContentBeginMarked and pdfioContentEndMarked functions.
.PP
The pdfioContentBeginMarked function accepts a named tag and optional dictionary of attributes such as the marked content identifier ("MCID"). For example, the following code tags a paragraph of text:
.nf
pdfio_file_t *pdf; // PDF file
pdfio_stream_t *st; // Page stream
pdfioContentBeginMarked(st, "P", /*dict*/NULL);
pdfioContentTextShow(st, /*unicode*/false, "Mary had a little lamb\\n");
pdfioContentTextShow(st, /*unicode*/false, "whose fleece was white as snow.\\n");
pdfioContentTextShow(st, /*unicode*/false, "And everywhere that Mary went\\n");
pdfioContentTextShow(st, /*unicode*/false, "the lamb was sure to go,\\n");
pdfioContentEndMarked(st);
.fi
.PP
To mark the same paragraph with a content identifier you would first create a dictionary containing the "MCID" key/value pair and then mark the paragraph with that dictionary:
.nf
pdfio_file_t *pdf; // PDF file
pdfio_stream_t *st; // Page stream
pdfio_dict_t *dict; // Content dictionary
dict = pdfioDictCreate(pdf);
pdfioDictSetNumber(dict, "MCID", 42);
pdfioContentBeginMarked(st, "P", dict);
pdfioContentTextShow(st, /*unicode*/false, "Mary had a little lamb\\n");
pdfioContentTextShow(st, /*unicode*/false, "whose fleece was white as snow.\\n");
pdfioContentTextShow(st, /*unicode*/false, "And everywhere that Mary went\\n");
pdfioContentTextShow(st, /*unicode*/false, "the lamb was sure to go,\\n");
pdfioContentEndMarked(st);
.fi
.SH Examples .SH Examples
.PP .PP
PDFio includes several example programs that are typically installed to the /usr/share/doc/pdfio/examples or /usr/local/share/doc/pdfio/examples directories. A makefile is included to build them. PDFio includes several example programs that are typically installed to the /usr/share/doc/pdfio/examples or /usr/local/share/doc/pdfio/examples directories. A makefile is included to build them.
@@ -1134,16 +1178,10 @@ The pdfioinfo.c example program opens a PDF file and prints the title, author, c
for (cur = 0, prev = 0; cur < num_pages; cur ++) for (cur = 0, prev = 0; cur < num_pages; cur ++)
{ {
// Find the MediaBox for this page in the page tree... // Find the MediaBox for this page in the page tree...
for (page = pdfioFileGetPage(pdf, cur); page = pdfioFileGetPage(pdf, cur);
page != NULL;
page = pdfioDictGetObj(page_dict, "Parent"))
{
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
page_dict = pdfioObjGetDict(page);
if (pdfioDictGetRect(page_dict, "MediaBox", &cur_box)) cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
break; pdfioPageGetRect(page, "MediaBox", &cur_box);
}
// If this MediaBox is different from the previous one, show the range of // If this MediaBox is different from the previous one, show the range of
// pages that have that size... // pages that have that size...
@@ -1416,7 +1454,7 @@ Then we loop through the differences array, keeping track of the current index w
.fi .fi
.SS Create a PDF File With Text and an Image .SS Create a PDF File With Text and an Image
.PP .PP
The image2pdf.c example code creates a PDF file containing a JPEG or PNG image file and optional caption on a single page. The create_pdf_image_file function creates the PDF file, embeds a base font and the named JPEG or PNG image file, and then creates a page with the image centered on the page with any text centered below: The image2pdf.c example code creates a PDF file containing a GIF, JPEG, or PNG image file and optional caption on a single page. The create_pdf_image_file function creates the PDF file, embeds a base font and the named JPEG or PNG image file, and then creates a page with the image centered on the page with any text centered below:
.nf .nf
#include <pdfio.h> #include <pdfio.h>
@@ -1688,6 +1726,9 @@ The md2pdf program needs to maintain three sets of state \- one for the markdown
// State for the current page // State for the current page
pdfio_stream_t *st; // Current page stream pdfio_stream_t *st; // Current page stream
double y; // Current position on page double y; // Current position on page
const char *tag; // Current block tag
bool in_table, // Are we in a table?
in_row; // Are we in a table row?
docfont_t font; // Current font docfont_t font; // Current font
double fsize; // Current font size double fsize; // Current font size
doccolor_t color; // Current color doccolor_t color; // Current color
@@ -2164,6 +2205,9 @@ Code blocks consist of one or more lines of plain monospaced text. We draw a lig
pdfioContentFillAndStroke(dd\->st, false); pdfioContentFillAndStroke(dd\->st, false);
// Start a code text block... // Start a code text block...
dd\->tag = "P";
pdfioContentBeginMarked(dd\->st, dd\->tag, /*dict*/NULL);
set_font(dd, DOCFONT_MONOSPACE, SIZE_CODEBLOCK); set_font(dd, DOCFONT_MONOSPACE, SIZE_CODEBLOCK);
pdfioContentTextBegin(dd\->st); pdfioContentTextBegin(dd\->st);
pdfioContentTextMoveTo(dd\->st, left, dd\->y); pdfioContentTextMoveTo(dd\->st, left, dd\->y);
@@ -2200,6 +2244,9 @@ Code blocks consist of one or more lines of plain monospaced text. We draw a lig
pdfioContentTextEnd(dd\->st); pdfioContentTextEnd(dd\->st);
dd\->y += lineheight; dd\->y += lineheight;
pdfioContentEndMarked(dd\->st);
dd\->tag = NULL;
// Draw the bottom padding... // Draw the bottom padding...
set_color(dd, DOCCOLOR_LTGRAY); set_color(dd, DOCCOLOR_LTGRAY);
pdfioContentPathRect(dd\->st, left \- CODE_PADDING, pdfioContentPathRect(dd\->st, left \- CODE_PADDING,
@@ -2329,8 +2376,17 @@ Finally, we render each row in the table:
.nf .nf
// Render each table row... // Render each table row...
dd\->in_table = true;
if (dd\->st)
pdfioContentBeginMarked(dd\->st, "Table", /*dict*/NULL);
for (row = 0, rowptr = rows; row < num_rows; row ++, rowptr ++) for (row = 0, rowptr = rows; row < num_rows; row ++, rowptr ++)
render_row(dd, num_cols, cols, rowptr); render_row(dd, num_cols, cols, rowptr);
pdfioContentEndMarked(dd\->st);
dd\->in_table = false;
.fi .fi
.PP .PP
Rendering the Markdown Document Rendering the Markdown Document
@@ -2449,6 +2505,10 @@ PDFIO_CS_ADOBE
.br .br
AdobeRGB 1998 AdobeRGB 1998
.TP 5 .TP 5
PDFIO_CS_CGATS001
.br
CGATS001 (CMYK)
.TP 5
PDFIO_CS_P3_D65 PDFIO_CS_P3_D65
.br .br
Display P3 Display P3
@@ -2477,7 +2537,7 @@ PDFIO_ENCRYPTION_RC4_128
.TP 5 .TP 5
PDFIO_ENCRYPTION_RC4_40 PDFIO_ENCRYPTION_RC4_40
.br .br
40-bit RC4 encryption (PDF 1.3) 40-bit RC4 encryption (PDF 1.3, reading only)
.SS pdfio_filter_e .SS pdfio_filter_e
Compression/decompression filters for streams Compression/decompression filters for streams
.TP 5 .TP 5
@@ -2491,7 +2551,7 @@ ASCIIHexDecode filter (reading only)
.TP 5 .TP 5
PDFIO_FILTER_CCITTFAX PDFIO_FILTER_CCITTFAX
.br .br
CCITTFaxDecode filter CCITTFaxDecode filter (reading only)
.TP 5 .TP 5
PDFIO_FILTER_CRYPT PDFIO_FILTER_CRYPT
.br .br
@@ -2507,7 +2567,7 @@ FlateDecode filter
.TP 5 .TP 5
PDFIO_FILTER_JBIG2 PDFIO_FILTER_JBIG2
.br .br
JBIG2Decode filter JBIG2Decode filter (reading only)
.TP 5 .TP 5
PDFIO_FILTER_JPX PDFIO_FILTER_JPX
.br .br
@@ -2838,7 +2898,8 @@ pdfio_array_t * pdfioArrayCreateColorFromStandard (
.fi .fi
.PP .PP
This function creates a color array for a standard \fBPDFIO_CS_\fR enumerated color space. This function creates a color array for a standard \fBPDFIO_CS_\fR enumerated color space.
The "num_colors" argument must be \fB1\fR for grayscale and \fB3\fR for RGB color. The "num_colors" argument must be \fB1\fR for grayscale, \fB3\fR for RGB color, and
\fB4\fR for CMYK color.
.SS pdfioArrayGetArray .SS pdfioArrayGetArray
Get an array value from an array. Get an array value from an array.
.PP .PP
@@ -2947,6 +3008,32 @@ bool pdfioArrayRemove (
size_t n size_t n
); );
.fi .fi
.PP
.SS pdfioContentBeginMarked
Start marked content with an optional dictionary.
.PP
.nf
bool pdfioContentBeginMarked (
pdfio_stream_t *st,
const char *tag,
pdfio_dict_t *dict
);
.fi
.PP
This function starts an area of marked content with an optional dictionary.
It must be paired with a call to the \fIpdfioContentEndMarked\fR function.
.PP
The "tag" argument specifies the tag name string for the content such as "P"
for a paragraph, "H1" for a top-level heading, and so forth. The "dict"
argument specifies an optional dictionary of properties for the content such
as the marked content identifier ("MCID") number.
.PP
Calling this function sets the "Marked" key in the "MarkInfo" dictionary of
the document catalog. The caller is responsible for setting the
"StructTreeRoot" dictionary when creating marked content.
.SS pdfioContentClip .SS pdfioContentClip
Clip output to the current path. Clip output to the current path.
.PP .PP
@@ -2972,6 +3059,19 @@ bool pdfioContentDrawImage (
.PP .PP
The object name must be part of the page dictionary resources, typically The object name must be part of the page dictionary resources, typically
using the \fIpdfioPageDictAddImage\fR function. using the \fIpdfioPageDictAddImage\fR function.
.SS pdfioContentEndMarked
End marked content.
.PP
.nf
bool pdfioContentEndMarked (
pdfio_stream_t *st
);
.fi
.PP
This function ends an area of marked content that was started using the
\fIpdfioContentBeginMarked\fR function.
.SS pdfioContentFill .SS pdfioContentFill
Fill the current path. Fill the current path.
.PP .PP
@@ -3083,6 +3183,8 @@ bool pdfioContentPathEnd (
pdfio_stream_t *st pdfio_stream_t *st
); );
.fi .fi
.PP
.SS pdfioContentPathLineTo .SS pdfioContentPathLineTo
Add a straight line to the current path. Add a straight line to the current path.
.PP .PP
@@ -3423,6 +3525,8 @@ double pdfioContentTextMeasure (
This function measures the given text string "s" and returns its width based This function measures the given text string "s" and returns its width based
on "size". The text string must always use the UTF-8 (Unicode) encoding but on "size". The text string must always use the UTF-8 (Unicode) encoding but
any control characters (such as newlines) are ignored. any control characters (such as newlines) are ignored.
.SS pdfioContentTextMoveLine .SS pdfioContentTextMoveLine
Move to the next line and offset. Move to the next line and offset.
.PP .PP
@@ -3451,6 +3555,8 @@ bool pdfioContentTextNewLine (
pdfio_stream_t *st pdfio_stream_t *st
); );
.fi .fi
.PP
.SS pdfioContentTextNewLineShow .SS pdfioContentTextNewLineShow
Move to the next line and show text. Move to the next line and show text.
.PP .PP
@@ -3468,6 +3574,8 @@ This function moves to the next line and then shows some text with optional
word and character spacing in a PDF content stream. The "unicode" argument word and character spacing in a PDF content stream. The "unicode" argument
specifies that the current font maps to full Unicode. The "s" argument specifies that the current font maps to full Unicode. The "s" argument
specifies a UTF-8 encoded string. specifies a UTF-8 encoded string.
.SS pdfioContentTextNewLineShowf .SS pdfioContentTextNewLineShowf
Show formatted text. Show formatted text.
.PP .PP
@@ -3486,6 +3594,8 @@ This function moves to the next line and shows some formatted text with
optional word and character spacing in a PDF content stream. The "unicode" optional word and character spacing in a PDF content stream. The "unicode"
argument specifies that the current font maps to full Unicode. The "format" argument specifies that the current font maps to full Unicode. The "format"
argument specifies a UTF-8 encoded \fBprintf\fR-style format string. argument specifies a UTF-8 encoded \fBprintf\fR-style format string.
.SS pdfioContentTextShow .SS pdfioContentTextShow
Show text. Show text.
.PP .PP
@@ -3536,6 +3646,8 @@ bool pdfioDictClear (
const char *key const char *key
); );
.fi .fi
.PP
.SS pdfioDictCopy .SS pdfioDictCopy
Copy a dictionary to a PDF file. Copy a dictionary to a PDF file.
.PP .PP
@@ -3608,6 +3720,8 @@ const char * pdfioDictGetKey (
size_t n size_t n
); );
.fi .fi
.PP
.SS pdfioDictGetName .SS pdfioDictGetName
Get a key name value from a dictionary. Get a key name value from a dictionary.
.PP .PP
@@ -3625,6 +3739,8 @@ size_t pdfioDictGetNumPairs (
pdfio_dict_t *dict pdfio_dict_t *dict
); );
.fi .fi
.PP
.SS pdfioDictGetNumber .SS pdfioDictGetNumber
Get a key number value from a dictionary. Get a key number value from a dictionary.
.PP .PP
@@ -3697,6 +3813,8 @@ function "cb":
The iteration continues as long as the callback returns \fBtrue\fR or all keys The iteration continues as long as the callback returns \fBtrue\fR or all keys
have been iterated. have been iterated.
.SS pdfioDictSetArray .SS pdfioDictSetArray
Set a key array in a dictionary. Set a key array in a dictionary.
.PP .PP
@@ -3818,6 +3936,39 @@ bool pdfioDictSetStringf (
... ...
); );
.fi .fi
.SS pdfioFileAddOutputIntent
Add an OutputIntent to a file.
.PP
.nf
void pdfioFileAddOutputIntent (
pdfio_file_t *pdf,
const char *subtype,
const char *condition,
const char *cond_id,
const char *reg_name,
const char *info,
pdfio_obj_t *profile
);
.fi
.PP
This function adds an OutputIntent dictionary to the PDF file catalog.
The "subtype" argument specifies the intent subtype and is typically
"GTS_PDFX" for PDF/X, "GTS_PDFA1" for PDF/A, or "ISO_PDFE1" for PDF/E.
Passing \fBNULL\fR defaults the subtype to "GTS_PDFA1".
.PP
The "condition" argument specifies a short name for the output intent, while
the "info" argument specifies a longer description for the output intent.
Both can be \fBNULL\fR to omit this information.
.PP
The "cond_id" argument specifies a unique identifier such as a registration
("CGATS001") or color space name ("sRGB"). The "reg_name" argument provides
a URL for the identifier.
.PP
The "profile" argument specifies an ICC profile object for the output
condition. If \fBNULL\fR, the PDF consumer will attempt to look up the correct
profile using the "cond_id" value.
.SS pdfioFileClose .SS pdfioFileClose
Close a PDF file and free all memory used for it. Close a PDF file and free all memory used for it.
.PP .PP
@@ -3844,16 +3995,56 @@ This function creates a new PDF file. The "filename" argument specifies the
name of the PDF file to create. name of the PDF file to create.
.PP .PP
The "version" argument specifies the PDF version number for the file or The "version" argument specifies the PDF version number for the file or
\fBNULL\fR for the default ("2.0"). The value "PCLm-1.0" can be specified to \fBNULL\fR for the default ("2.0"). The following values are recognized:
produce the PCLm subset of PDF. .PP
.IP \(bu 5
"1.3", "1.4", "1.5", "1.6", "1.7", "2.0": Generic PDF files of the
specified versions.
.IP \(bu 5
"PCLm-1.0": The PCLm (raster) subset of PDF supported by some printers.
.IP \(bu 5
"PDF/A-1a": PDF/A-1a:2005
.IP \(bu 5
"PDF/A-1b": PDF/A-1b:2005
.IP \(bu 5
"PDF/A-2a": PDF/A-2a:2011
.IP \(bu 5
"PDF/A-2b": PDF/A-2b:2011
.IP \(bu 5
"PDF/A-2u": PDF/A-2u:2011
.IP \(bu 5
"PDF/A-3a": PDF/A-3a:2012
.IP \(bu 5
"PDF/A-3b": PDF/A-3b:2012
.IP \(bu 5
"PDF/A-3u": PDF/A-3u:2012
.IP \(bu 5
"PDF/A-4": PDF/A-4:2020
.PP .PP
The "media_box" and "crop_box" arguments specify the default MediaBox and The "media_box" and "crop_box" arguments specify the default MediaBox and
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
of 8.27x11in (the intersection of US Letter and ISO A4) is used. of 8.27x11in (the intersection of US Letter and ISO A4) is used.
.PP .PP
The "error_cb" and "error_cbdata" arguments specify an error handler callback The "error_cb" and "error_cbdata" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR the default error handler is used that and its data pointer - if \fBNULL\fR then the default error handler is used that
writes error messages to \fBstderr\fR. writes error and warning messages to \fBstderr\fR. The error handler callback
should return \fBtrue\fR to continue writing the PDF file or \fBfalse\fR to stop.
For example:
.PP
.nf
bool
my_error_cb(pdfio_file_t *pdf, const char *message, void *data)
{
(void)data; /* Not using data pointer in this callback */
fprintf(stderr, "%s: %sn", pdfioFileGetName(pdf), message);
/* Return true to continue on warning messages, false otherwise... */
return (!strncmp(message, "WARNING:", 8));
}
.fi
.SS pdfioFileCreateArrayObj .SS pdfioFileCreateArrayObj
Create a new object in a PDF file containing an array. Create a new object in a PDF file containing an array.
.PP .PP
@@ -3911,8 +4102,30 @@ specifies the font nane:
Aside from "Symbol" and "Zapf-Dingbats", Base fonts use the Windows CP1252 Aside from "Symbol" and "Zapf-Dingbats", Base fonts use the Windows CP1252
(ISO-8859-1 with additional characters such as the Euro symbol) subset of (ISO-8859-1 with additional characters such as the Euro symbol) subset of
Unicode. Unicode.
.PP
.IP 5
Note: This function cannot be used when producing PDF/A files.
.SS pdfioFileCreateFontObjFromData
Add a font in memory to a PDF file.
.PP
.nf
pdfio_obj_t * pdfioFileCreateFontObjFromData (
pdfio_file_t *pdf,
const void *data,
size_t datasize,
bool unicode
);
.fi
.PP
This function embeds TrueType/OpenType font data into a PDF file. The
"unicode" parameter controls whether the font is encoded for two-byte
characters (potentially full Unicode, but more typically a subset)
or to only support the Windows CP1252 (ISO-8859-1 with additional
characters such as the Euro symbol) subset of Unicode.
.SS pdfioFileCreateFontObjFromFile .SS pdfioFileCreateFontObjFromFile
Add a font object to a PDF file. Add a font file to a PDF file.
.PP .PP
.nf .nf
pdfio_obj_t * pdfioFileCreateFontObjFromFile ( pdfio_obj_t * pdfioFileCreateFontObjFromFile (
@@ -3922,7 +4135,7 @@ pdfio_obj_t * pdfioFileCreateFontObjFromFile (
); );
.fi .fi
.PP .PP
This function embeds a TrueType/OpenType font into a PDF file. The This function embeds a TrueType/OpenType font file into a PDF file. The
"unicode" parameter controls whether the font is encoded for two-byte "unicode" parameter controls whether the font is encoded for two-byte
characters (potentially full Unicode, but more typically a subset) characters (potentially full Unicode, but more typically a subset)
or to only support the Windows CP1252 (ISO-8859-1 with additional or to only support the Windows CP1252 (ISO-8859-1 with additional
@@ -3975,8 +4188,12 @@ the image is encoded in the corresponding device color space. The
"interpolate" parameter specifies whether to interpolate when scaling the "interpolate" parameter specifies whether to interpolate when scaling the
image on the page. image on the page.
.PP .PP
.IP 5
Note: When creating an image object with alpha, a second image object is Note: When creating an image object with alpha, a second image object is
created to hold the "soft mask" data for the primary image. .IP 5
created to hold the "soft mask" data for the primary image. PDF/A-1
.IP 5
files do not support alpha-based transparency.
.SS pdfioFileCreateImageObjFromFile .SS pdfioFileCreateImageObjFromFile
Add an image object to a PDF file from a file. Add an image object to a PDF file from a file.
.PP .PP
@@ -3988,17 +4205,21 @@ pdfio_obj_t * pdfioFileCreateImageObjFromFile (
); );
.fi .fi
.PP .PP
This function creates an image object in a PDF file from a JPEG or PNG file. This function creates an image object in a PDF file from a GIF, JPEG, PNG, or
The "filename" parameter specifies the name of the JPEG or PNG file, while WebP file. The "filename" parameter specifies the name of the GIF, JPEG,
the "interpolate" parameter specifies whether to interpolate when scaling the PNG, or WebP file, while the "interpolate" parameter specifies whether to
image on the page. interpolate when scaling the image on the page.
.PP .PP
.IP 5 .IP 5
Note: Currently PNG support is limited to grayscale, RGB, or indexed files Note: PNG files containing transparency cannot be used when producing
.IP 5 .IP 5
without interlacing or alpha. Transparency (masking) based on color/index PDF/A files. Files containing animation yield the final frame of the
.IP 5 .IP 5
is supported. animation. GIF and (optional) WebP support first appeared in PDFio v1.7.
.IP 5
WebP content is added as a Flate-compressed image with alpha mask as
.IP 5
needed and is usually significantly larger than the original image.
.SS pdfioFileCreateNameObj .SS pdfioFileCreateNameObj
Create a new object in a PDF file containing a name. Create a new object in a PDF file containing a name.
.PP .PP
@@ -4011,6 +4232,8 @@ pdfio_obj_t * pdfioFileCreateNameObj (
.PP .PP
This function creates a new object with a name value in a PDF file. This function creates a new object with a name value in a PDF file.
You must call \fIpdfioObjClose\fR to write the object to the file. You must call \fIpdfioObjClose\fR to write the object to the file.
.SS pdfioFileCreateNumberObj .SS pdfioFileCreateNumberObj
Create a new object in a PDF file containing a number. Create a new object in a PDF file containing a number.
.PP .PP
@@ -4023,6 +4246,8 @@ pdfio_obj_t * pdfioFileCreateNumberObj (
.PP .PP
This function creates a new object with a number value in a PDF file. This function creates a new object with a number value in a PDF file.
You must call \fIpdfioObjClose\fR to write the object to the file. You must call \fIpdfioObjClose\fR to write the object to the file.
.SS pdfioFileCreateObj .SS pdfioFileCreateObj
Create a new object in a PDF file. Create a new object in a PDF file.
.PP .PP
@@ -4062,18 +4287,57 @@ 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"). Unlike \fIpdfioFileCreate\fR and \fBNULL\fR for the default ("2.0"). The following values are recognized:
\fIpdfioFileCreateTemporary\fR, it is generally not safe to pass the .PP
"PCLm-1.0" version string. .IP \(bu 5
"1.3", "1.4", "1.5", "1.6", "1.7", "2.0": Generic PDF files of the
specified versions.
.IP \(bu 5
"PDF/A-1a": PDF/A-1a:2005
.IP \(bu 5
"PDF/A-1b": PDF/A-1b:2005
.IP \(bu 5
"PDF/A-2a": PDF/A-2a:2011
.IP \(bu 5
"PDF/A-2b": PDF/A-2b:2011
.IP \(bu 5
"PDF/A-2u": PDF/A-2u:2011
.IP \(bu 5
"PDF/A-3a": PDF/A-3a:2012
.IP \(bu 5
"PDF/A-3b": PDF/A-3b:2012
.IP \(bu 5
"PDF/A-3u": PDF/A-3u:2012
.IP \(bu 5
"PDF/A-4": PDF/A-4:2020
.PP
Unlike \fIpdfioFileCreate\fR and \fIpdfioFileCreateTemporary\fR, it is
generally not safe to pass the "PCLm-1.0" version string.
.PP .PP
The "media_box" and "crop_box" arguments specify the default MediaBox and The "media_box" and "crop_box" arguments specify the default MediaBox and
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
of 8.27x11in (the intersection of US Letter and ISO A4) is used. of 8.27x11in (the intersection of US Letter and ISO A4) is used.
.PP .PP
The "error_cb" and "error_cbdata" arguments specify an error handler callback The "error_cb" and "error_cbdata" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR the default error handler is used that and its data pointer - if \fBNULL\fR then the default error handler is used that
writes error messages to \fBstderr\fR. writes error and warning messages to \fBstderr\fR. The error handler callback
should return \fBtrue\fR to continue writing the PDF file or \fBfalse\fR to stop.
For example:
.PP .PP
.nf
bool
my_error_cb(pdfio_file_t *pdf, const char *message, void *data)
{
(void)data; /* Not using data pointer in this callback */
fprintf(stderr, "%s: %sn", pdfioFileGetName(pdf), message);
/* Return true to continue on warning messages, false otherwise... */
return (!strncmp(message, "WARNING:", 8));
}
.fi
.IP 5 .IP 5
\fINote\fR: Files created using this API are slightly larger than those \fINote\fR: Files created using this API are slightly larger than those
.IP 5 .IP 5
@@ -4101,6 +4365,8 @@ pdfio_obj_t * pdfioFileCreateStringObj (
.PP .PP
This function creates a new object with a string value in a PDF file. This function creates a new object with a string value in a PDF file.
You must call \fIpdfioObjClose\fR to write the object to the file. You must call \fIpdfioObjClose\fR to write the object to the file.
.SS pdfioFileCreateTemporary .SS pdfioFileCreateTemporary
.PP .PP
@@ -4177,6 +4443,21 @@ const char * pdfioFileGetKeywords (
pdfio_file_t *pdf pdfio_file_t *pdf
); );
.fi .fi
.SS pdfioFileGetLanguage
Get the language metadata for a PDF file.
.PP
.nf
const char * pdfioFileGetLanguage (
pdfio_file_t *pdf
);
.fi
.PP
This function gets the (primary/default) language metadata, if any, for a PDF
file. The returned string is an IETF BCP 47 language tag of the form
"lang-REGION". For example, the string "en-CA" specifies Canadian English
and the string "fr-CA" specifies Canadian French.
.SS pdfioFileGetModificationDate .SS pdfioFileGetModificationDate
Get the most recent modification date for a PDF file. Get the most recent modification date for a PDF file.
.PP .PP
@@ -4294,8 +4575,33 @@ cancel the open. If \fBNULL\fR is specified for the callback function and the
PDF file requires a password, the open will always fail. PDF file requires a password, the open will always fail.
.PP .PP
The "error_cb" and "error_cbdata" arguments specify an error handler callback The "error_cb" and "error_cbdata" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR the default error handler is used that and its data pointer - if \fBNULL\fR then the default error handler is used that
writes error messages to \fBstderr\fR. writes error and warning messages to \fBstderr\fR. The error handler callback
should return \fBtrue\fR to continue reading the PDF file or \fBfalse\fR to stop.
For example:
.PP
.nf
bool
my_error_cb(pdfio_file_t *pdf, const char *message, void *data)
{
(void)data; /* Not using data pointer in this callback */
fprintf(stderr, "%s: %sn", pdfioFileGetName(pdf), message);
/* Return true to continue on warning messages, false otherwise... */
return (!strncmp(message, "WARNING:", 8));
}
.fi
.IP 5
Note: Error messages starting with "WARNING:" are actually warning
.IP 5
messages - the callback should normally return \fBtrue\fR to allow PDFio to
.IP 5
try to resolve the issue. In addition, some errors are unrecoverable and
.IP 5
ignore the return value of the error callback.
.SS pdfioFileSetAuthor .SS pdfioFileSetAuthor
Set the author for a PDF file. Set the author for a PDF file.
.PP .PP
@@ -4332,6 +4638,22 @@ void pdfioFileSetKeywords (
const char *value const char *value
); );
.fi .fi
.SS pdfioFileSetLanguage
Set the language metadata for a PDF file.
.PP
.nf
void pdfioFileSetLanguage (
pdfio_file_t *pdf,
const char *value
);
.fi
.PP
This function sets the (primary/default) language metadata for a PDF file.
The "value" argument is an IETF BCP 47 language tag string of the form
"lang-REGION". For example, the string "en-CA" specifies Canadian English
and the string "fr-CA" specifies Canadian French.
.SS pdfioFileSetModificationDate .SS pdfioFileSetModificationDate
Set the modification date for a PDF file. Set the modification date for a PDF file.
.PP .PP
@@ -4474,6 +4796,8 @@ const char * pdfioObjGetName (
pdfio_obj_t *obj pdfio_obj_t *obj
); );
.fi .fi
.PP
.SS pdfioObjGetNumber .SS pdfioObjGetNumber
Get the object's number. Get the object's number.
.PP .PP
@@ -4602,6 +4926,91 @@ bool pdfioPageDictAddImage (
pdfio_obj_t *obj pdfio_obj_t *obj
); );
.fi .fi
.SS pdfioPageGetArray
Get an array value from the page dictionary.
.PP
.nf
pdfio_array_t * pdfioPageGetArray (
pdfio_obj_t *page,
const char *key
);
.fi
.PP
This function looks up an array value in the page dictionary, either in the
specified object or one of its parents.
.SS pdfioPageGetBinary
Get a binary value from the page dictionary.
.PP
.nf
unsigned char * pdfioPageGetBinary (
pdfio_obj_t *page,
const char *key,
size_t *length
);
.fi
.PP
This function looks up a binary value in the page dictionary, either in the
specified object or one of its parents.
.SS pdfioPageGetBoolean
Get a boolean value from the page dictionary.
.PP
.nf
bool pdfioPageGetBoolean (
pdfio_obj_t *page,
const char *key
);
.fi
.PP
This function looks up a boolean value in the page dictionary, either in the
specified object or one of its parents.
.SS pdfioPageGetDate
Get a date value from the page dictionary.
.PP
.nf
time_t pdfioPageGetDate (
pdfio_obj_t *page,
const char *key
);
.fi
.PP
This function looks up a date value in the page dictionary, either in the
specified object or one of its parents.
.SS pdfioPageGetDict
Get a dictionary value from the page dictionary.
.PP
.nf
pdfio_dict_t * pdfioPageGetDict (
pdfio_obj_t *page,
const char *key
);
.fi
.PP
This function looks up a dictionary value in the page dictionary, either in
the specified object or one of its parents.
.SS pdfioPageGetName
Get a name value from the page dictionary.
.PP
.nf
const char * pdfioPageGetName (
pdfio_obj_t *page,
const char *key
);
.fi
.PP
This function looks up a name value in the page dictionary, either in the
specified object or one of its parents.
.SS pdfioPageGetNumStreams .SS pdfioPageGetNumStreams
Get the number of content streams for a page object. Get the number of content streams for a page object.
.PP .PP
@@ -4610,6 +5019,63 @@ size_t pdfioPageGetNumStreams (
pdfio_obj_t *page pdfio_obj_t *page
); );
.fi .fi
.SS pdfioPageGetNumber
Get a number value from the page dictionary.
.PP
.nf
double pdfioPageGetNumber (
pdfio_obj_t *page,
const char *key
);
.fi
.PP
This function looks up a number value in the page dictionary, either in the
specified object or one of its parents.
.SS pdfioPageGetObj
Get an indirect object value from the page dictionary.
.PP
.nf
pdfio_obj_t * pdfioPageGetObj (
pdfio_obj_t *page,
const char *key
);
.fi
.PP
This function looks up an indirect object value in the page dictionary,
either in the specified object or one of its parents.
.SS pdfioPageGetRect
Get a rectangle value from the page dictionary.
.PP
.nf
pdfio_rect_t * pdfioPageGetRect (
pdfio_obj_t *page,
const char *key,
pdfio_rect_t *rect
);
.fi
.PP
This function looks up a rectangle value in the page dictionary, either in
the specified object or one of its parents.
.SS pdfioPageGetString
Get a string value from the page dictionary.
.PP
.nf
const char * pdfioPageGetString (
pdfio_obj_t *page,
const char *key
);
.fi
.PP
This function looks up a string value in the page dictionary, either in the
specified object or one of its parents.
.SS pdfioPageOpenStream .SS pdfioPageOpenStream
Open a content stream for a page. Open a content stream for a page.
.PP .PP
@@ -4677,8 +5143,9 @@ bool pdfioStreamPrintf (
.fi .fi
.PP .PP
This function writes a formatted string to a stream. In addition to the This function writes a formatted string to a stream. In addition to the
standard \fBprintf\fR format characters, you can use "%N" to format a PDF name standard \fBprintf\fR format characters, you can use "%H" to format a HTML/XML
value ("/Name") and "%S" to format a PDF string ("(String)") value. string value, "%N" to format a PDF name value ("/Name"), and "%S" to format
a PDF string ("(String)") value.
.SS pdfioStreamPutChar .SS pdfioStreamPutChar
Write a single character to a stream. Write a single character to a stream.
.PP .PP
@@ -4886,4 +5353,4 @@ typedef enum pdfio_valtype_e pdfio_valtype_t;
Michael R Sweet Michael R Sweet
.SH COPYRIGHT .SH COPYRIGHT
.PP .PP
Copyright (c) 2021-2025 by Michael R Sweet Copyright (c) 2021-2026 by Michael R Sweet

File diff suppressed because it is too large Load Diff

View File

@@ -15,9 +15,9 @@ 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-2025 by Michael R Sweet and is licensed under the Copyright © 2021-2026 by Michael R Sweet. PDFio is licensed under the Apache
Apache License Version 2.0 with an (optional) exception to allow linking against License Version 2.0 with an (optional) exception to allow linking against GNU
GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information. GPL2-only software. See the files `LICENSE` and `NOTICE` for more information.
Requirements Requirements
@@ -28,16 +28,22 @@ PDFio requires the following to build the software:
- A C99 compiler such as Clang, GCC, or MS Visual C - A C99 compiler such as Clang, GCC, or MS Visual C
- A POSIX-compliant `make` program - A POSIX-compliant `make` program
- A POSIX-compliant `sh` program - A POSIX-compliant `sh` program
- ZLIB (<https://www.zlib.net/>) 1.0 or higher - libpng (<https://www.libpng.org/>) 1.6 or later for full PNG image support
(optional)
PDFio will also use libpng 1.6 or higher (<https://www.libpng.org/>) to provide - libwebp (<https://developers.google.com/speed/webp>) 1.0 or later for WebP
enhanced PNG image support. image support (optional)
- ZLIB (<https://www.zlib.net/>) 1.1 or later for compression support
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided. IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
On a stock Ubuntu install, the following command will install the various
prerequisites:
Installing PDFio sudo apt-get install build-essential libpng-dev libwebp-dev zlib1g-dev
----------------
Building and Installing PDFio on Linux/Unix
-------------------------------------------
PDFio comes with a configure script that creates a portable makefile that will 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: work on any POSIX-compliant system with ZLIB installed. To make it, run:
@@ -69,21 +75,32 @@ Other configure options can be found using the `--help` option:
./configure --help ./configure --help
Visual Studio Project Building on Windows with Visual Studio
--------------------- --------------------------------------
The Visual Studio solution ("pdfio.sln") is provided for Windows developers and The Visual Studio solution ("pdfio.sln") is provided for Windows developers and
generates both a static library and DLL. generates the PDFIO1 DLL. You can also use NuGet to install the `pdfio_native`
package.
You can build and run the `testpdfio` unit tests target from within Visual
Studio to verify the functioning of the library.
Xcode Project Building on macOS with Xcode
------------- ----------------------------
There is also an Xcode project ("pdfio.xcodeproj") you can use on macOS which The Xcode project (`pdfio.xcodeproj`) is provided for macOS developer which
generates a static library that will be installed under "/usr/local" with: generates a static library that will be installed under "/usr/local". Run the
following command to build and install PDFio on macOS:
sudo xcodebuild install sudo xcodebuild install
Alternately you can add the Xcode project to a workspace and reference the PDFio
library target as a dependency in your own project.
You can build and run the `testpdfio` unit tests target from within Xcode to
verify the functioning of the library.
Detecting PDFio Detecting PDFio
--------------- ---------------
@@ -343,8 +360,8 @@ password_cb(void *data, const char *filename)
``` ```
The error callback is called for both errors and warnings and accepts the The error callback is called for both errors and warnings and accepts the
`pdfio_file_t` pointer, a message string, and the callback pointer value, for `pdfio_file_t` pointer, a message string, and the callback pointer value. It
example: returns `true` to continue processing the file or `false` to stop, for example:
```c ```c
bool bool
@@ -354,13 +371,17 @@ error_cb(pdfio_file_t *pdf, const char *message, void *data)
fprintf(stderr, "%s: %s\n", pdfioFileGetName(pdf), message); fprintf(stderr, "%s: %s\n", pdfioFileGetName(pdf), message);
// Return false to treat warnings as errors // Return true for warning messages (continue) and false for errors (stop)
return (false); return (!strncmp(message, "WARNING:", 8));
} }
``` ```
The default error callback (`NULL`) does the equivalent of the above. The default error callback (`NULL`) does the equivalent of the above.
> Note: Many errors are unrecoverable, so PDFio ignores the return value from
> the error callback and always stops processing the PDF file. Warning messages
> start with the prefix "WARNING:" while errors have no prefix.
Each PDF file contains one or more pages. The [`pdfioFileGetNumPages`](@@) Each PDF file contains one or more pages. The [`pdfioFileGetNumPages`](@@)
function returns the number of pages in the file while the function returns the number of pages in the file while the
[`pdfioFileGetPage`](@@) function gets the specified page in the PDF file: [`pdfioFileGetPage`](@@) function gets the specified page in the PDF file:
@@ -383,43 +404,35 @@ Each page is represented by a "page tree" object (what [`pdfioFileGetPage`](@@)
returns) that specifies information about the page and one or more "content" returns) that specifies information about the page and one or more "content"
objects that contain the images, fonts, text, and graphics that appear on the objects that contain the images, fonts, text, and graphics that appear on the
page. Use the [`pdfioPageGetNumStreams`](@@) and [`pdfioPageOpenStream`](@@) page. Use the [`pdfioPageGetNumStreams`](@@) and [`pdfioPageOpenStream`](@@)
functions to access the content streams for each page, and functions to access the content streams for each page, [`pdfioObjGetDict`](@@)
[`pdfioObjGetDict`](@@) to get the associated page object dictionary. For to get the associated page object dictionary, and [`pdfioPageGetArray`](@@),
example, if you want to display the media and crop boxes for a given page: [`pdfioPageGetBinary`](@@), [`pdfioPageGetBoolean`](@@),
[`pdfioPageGetDate`](@@), [`pdfioPageGetDict`](@@), [`pdfioPageGetName`](@@),
[`pdfioPageGetObj`](@@), [`pdfioPageGetRect`](@@), and
[`pdfioPageGetString`](@@) to get a value from the page object dictionary or its
parents. For example, if you want to display the media and crop boxes for a
given page:
```c ```c
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
size_t i; // Looping var size_t i; // Looping var
size_t count; // Number of pages size_t count; // Number of pages
pdfio_obj_t *page; // Current page pdfio_obj_t *page; // Current page
pdfio_dict_t *dict; // Current page dictionary pdfio_rect_t media_box; // MediaBox values
pdfio_array_t *media_box; // MediaBox array pdfio_rect_t crop_box; // CropBox values
double media_values[4]; // MediaBox values
pdfio_array_t *crop_box; // CropBox array
double crop_values[4]; // CropBox values
// Iterate the pages in the PDF file // Iterate the pages in the PDF file
for (i = 0, count = pdfioFileGetNumPages(pdf); i < count; i ++) for (i = 0, count = pdfioFileGetNumPages(pdf); i < count; i ++)
{ {
page = pdfioFileGetPage(pdf, i); page = pdfioFileGetPage(pdf, i);
dict = pdfioObjGetDict(page);
media_box = pdfioDictGetArray(dict, "MediaBox"); pdfioPageGetRect(page, "MediaBox", &media_box);
media_values[0] = pdfioArrayGetNumber(media_box, 0); pdfioPageGetRect(page, "CropBox", &crop_box);
media_values[1] = pdfioArrayGetNumber(media_box, 1);
media_values[2] = pdfioArrayGetNumber(media_box, 2);
media_values[3] = pdfioArrayGetNumber(media_box, 3);
crop_box = pdfioDictGetArray(dict, "CropBox");
crop_values[0] = pdfioArrayGetNumber(crop_box, 0);
crop_values[1] = pdfioArrayGetNumber(crop_box, 1);
crop_values[2] = pdfioArrayGetNumber(crop_box, 2);
crop_values[3] = pdfioArrayGetNumber(crop_box, 3);
printf("Page %u: MediaBox=[%g %g %g %g], CropBox=[%g %g %g %g]\n", printf("Page %u: MediaBox=[%g %g %g %g], CropBox=[%g %g %g %g]\n",
(unsigned)(i + 1), (unsigned)(i + 1),
media_values[0], media_values[1], media_values[2], media_values[3], media_box.x1, media_box.y1, media_box.x2, media_box.y2,
crop_values[0], crop_values[1], crop_values[2], crop_values[3]); crop_box.x1, crop_box.y1, crop_box.x2, crop_box.y2);
} }
``` ```
@@ -756,8 +769,9 @@ The "interpolate" argument specifies whether the colors in the image should be
smoothed/interpolated when scaling. This is most useful for photographs but smoothed/interpolated when scaling. This is most useful for photographs but
should be `false` for screenshot and barcode images. should be `false` for screenshot and barcode images.
If you have a JPEG or PNG file, use the [`pdfioFileCreateImageObjFromFile`](@@) If you have a GIF, JPEG, PNG, or WebP file, use the
function to copy the image into a PDF image object, for example: [`pdfioFileCreateImageObjFromFile`](@@) function to copy the image into a PDF
image object, for example:
```c ```c
pdfio_file_t *pdf = pdfioFileCreate(...); pdfio_file_t *pdf = pdfioFileCreate(...);
@@ -765,9 +779,6 @@ pdfio_obj_t *img =
pdfioFileCreateImageObjFromFile(pdf, "myphoto.jpg", /*interpolate*/true); pdfioFileCreateImageObjFromFile(pdf, "myphoto.jpg", /*interpolate*/true);
``` ```
> Note: Currently `pdfioFileCreateImageObjFromFile` does not support 12 bit JPEG
> files or PNG files with an alpha channel.
### Page Dictionary Functions ### Page Dictionary Functions
@@ -864,6 +875,55 @@ escaping, as needed:
offsets between them offsets between them
Tagged and Marked PDF Content
-----------------------------
Content in a page stream can be tagged to help a PDF reader application know the
kind and organization of that content. Content inserted using the PDFio
[Page Stream Functions](@) can be tagged by surrounding it with the
[`pdfioContentBeginMarked`](@@) and [`pdfioContentEndMarked`](@@) functions.
The `pdfioContentBeginMarked` function accepts a named tag and optional
dictionary of attributes such as the marked content identifier ("MCID"). For
example, the following code tags a paragraph of text:
```c
pdfio_file_t *pdf; // PDF file
pdfio_stream_t *st; // Page stream
pdfioContentBeginMarked(st, "P", /*dict*/NULL);
pdfioContentTextShow(st, /*unicode*/false, "Mary had a little lamb\n");
pdfioContentTextShow(st, /*unicode*/false, "whose fleece was white as snow.\n");
pdfioContentTextShow(st, /*unicode*/false, "And everywhere that Mary went\n");
pdfioContentTextShow(st, /*unicode*/false, "the lamb was sure to go,\n");
pdfioContentEndMarked(st);
```
To mark the same paragraph with a content identifier you would first create a
dictionary containing the "MCID" key/value pair and then mark the paragraph with
that dictionary:
```c
pdfio_file_t *pdf; // PDF file
pdfio_stream_t *st; // Page stream
pdfio_dict_t *dict; // Content dictionary
dict = pdfioDictCreate(pdf);
pdfioDictSetNumber(dict, "MCID", 42);
pdfioContentBeginMarked(st, "P", dict);
pdfioContentTextShow(st, /*unicode*/false, "Mary had a little lamb\n");
pdfioContentTextShow(st, /*unicode*/false, "whose fleece was white as snow.\n");
pdfioContentTextShow(st, /*unicode*/false, "And everywhere that Mary went\n");
pdfioContentTextShow(st, /*unicode*/false, "the lamb was sure to go,\n");
pdfioContentEndMarked(st);
```
Examples Examples
======== ========
@@ -976,16 +1036,10 @@ main(int argc, // I - Number of command-line arguments
for (cur = 0, prev = 0; cur < num_pages; cur ++) for (cur = 0, prev = 0; cur < num_pages; cur ++)
{ {
// Find the MediaBox for this page in the page tree... // Find the MediaBox for this page in the page tree...
for (page = pdfioFileGetPage(pdf, cur); page = pdfioFileGetPage(pdf, cur);
page != NULL;
page = pdfioDictGetObj(page_dict, "Parent"))
{
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
page_dict = pdfioObjGetDict(page);
if (pdfioDictGetRect(page_dict, "MediaBox", &cur_box)) cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
break; pdfioPageGetRect(page, "MediaBox", &cur_box);
}
// If this MediaBox is different from the previous one, show the range of // If this MediaBox is different from the previous one, show the range of
// pages that have that size... // pages that have that size...
@@ -1289,7 +1343,7 @@ Unicode glyph for the current index:
Create a PDF File With Text and an Image Create a PDF File With Text and an Image
---------------------------------------- ----------------------------------------
The `image2pdf.c` example code creates a PDF file containing a JPEG or PNG The `image2pdf.c` example code creates a PDF file containing a GIF, JPEG, or PNG
image file and optional caption on a single page. The `create_pdf_image_file` image file and optional caption on a single page. The `create_pdf_image_file`
function creates the PDF file, embeds a base font and the named JPEG or PNG function creates the PDF file, embeds a base font and the named JPEG or PNG
image file, and then creates a page with the image centered on the page with any image file, and then creates a page with the image centered on the page with any
@@ -1593,6 +1647,9 @@ typedef struct docdata_s // Document formatting data
// State for the current page // State for the current page
pdfio_stream_t *st; // Current page stream pdfio_stream_t *st; // Current page stream
double y; // Current position on page double y; // Current position on page
const char *tag; // Current block tag
bool in_table, // Are we in a table?
in_row; // Are we in a table row?
docfont_t font; // Current font docfont_t font; // Current font
double fsize; // Current font size double fsize; // Current font size
doccolor_t color; // Current color doccolor_t color; // Current color
@@ -2096,6 +2153,9 @@ pdfioContentPathRect(dd->st, left - CODE_PADDING, dd->y + SIZE_CODEBLOCK,
pdfioContentFillAndStroke(dd->st, false); pdfioContentFillAndStroke(dd->st, false);
// Start a code text block... // Start a code text block...
dd->tag = "P";
pdfioContentBeginMarked(dd->st, dd->tag, /*dict*/NULL);
set_font(dd, DOCFONT_MONOSPACE, SIZE_CODEBLOCK); set_font(dd, DOCFONT_MONOSPACE, SIZE_CODEBLOCK);
pdfioContentTextBegin(dd->st); pdfioContentTextBegin(dd->st);
pdfioContentTextMoveTo(dd->st, left, dd->y); pdfioContentTextMoveTo(dd->st, left, dd->y);
@@ -2132,6 +2192,9 @@ for (code = mmdGetFirstChild(block); code; code = mmdGetNextSibling(code))
pdfioContentTextEnd(dd->st); pdfioContentTextEnd(dd->st);
dd->y += lineheight; dd->y += lineheight;
pdfioContentEndMarked(dd->st);
dd->tag = NULL;
// Draw the bottom padding... // Draw the bottom padding...
set_color(dd, DOCCOLOR_LTGRAY); set_color(dd, DOCCOLOR_LTGRAY);
pdfioContentPathRect(dd->st, left - CODE_PADDING, pdfioContentPathRect(dd->st, left - CODE_PADDING,
@@ -2272,8 +2335,17 @@ Finally, we render each row in the table:
```c ```c
// Render each table row... // Render each table row...
dd->in_table = true;
if (dd->st)
pdfioContentBeginMarked(dd->st, "Table", /*dict*/NULL);
for (row = 0, rowptr = rows; row < num_rows; row ++, rowptr ++) for (row = 0, rowptr = rows; row < num_rows; row ++, rowptr ++)
render_row(dd, num_cols, cols, rowptr); render_row(dd, num_cols, cols, rowptr);
pdfioContentEndMarked(dd->st);
dd->in_table = false;
``` ```

View File

@@ -13,6 +13,10 @@
#include <pdfio.h> #include <pdfio.h>
#include <pdfio-content.h> #include <pdfio-content.h>
#ifdef _WIN32
# include <io.h>
# include <fcntl.h>
#endif // _WIN32
// //

View File

@@ -1,7 +1,7 @@
// //
// Simple markdown to PDF converter example for PDFio. // Simple markdown to PDF converter example for PDFio.
// //
// Copyright © 2024 by Michael R Sweet. // Copyright © 2024-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@@ -25,6 +25,7 @@
#include <math.h> #include <math.h>
#ifdef _WIN32 #ifdef _WIN32
# include <io.h> # include <io.h>
# include <fcntl.h>
#else #else
# include <unistd.h> # include <unistd.h>
#endif // _WIN32 #endif // _WIN32
@@ -122,6 +123,9 @@ typedef struct docdata_s // Document formatting data
// State for the current page // State for the current page
pdfio_stream_t *st; // Current page stream pdfio_stream_t *st; // Current page stream
double y; // Current position on page double y; // Current position on page
const char *tag; // Current block tag
bool in_table, // Are we in a table?
in_row; // Are we in a table row?
docfont_t font; // Current font docfont_t font; // Current font
double fsize; // Current font size double fsize; // Current font size
doccolor_t color; // Current color doccolor_t color; // Current color
@@ -289,8 +293,8 @@ main(int argc, // I - Number of command-line arguments
// Get the markdown file from the command-line... // Get the markdown file from the command-line...
if (argc < 2 || argc > 3) if (argc < 2 || argc > 3)
{ {
fputs("Usage: md2pdf FILENANE.md [FILENAME.pdf]\n", stderr); fputs("Usage: md2pdf FILENAME.md [FILENAME.pdf]\n", stderr);
fputs(" md2pdf FILENANE.md >FILENAME.pdf\n", stderr); fputs(" md2pdf FILENAME.md >FILENAME.pdf\n", stderr);
return (1); return (1);
} }
@@ -472,6 +476,7 @@ add_links(docdata_t *dd) // I - Document data
// Create the annotation object pointing to the action... // Create the annotation object pointing to the action...
dict = pdfioDictCreate(dd->pdf); dict = pdfioDictCreate(dd->pdf);
pdfioDictSetName(dict, "Subtype", "Link"); pdfioDictSetName(dict, "Subtype", "Link");
pdfioDictSetNumber(dict, "F", 4); // Print flag
pdfioDictSetRect(dict, "Rect", &l->box); pdfioDictSetRect(dict, "Rect", &l->box);
border = pdfioArrayCreate(dd->pdf); border = pdfioArrayCreate(dd->pdf);
pdfioArrayAppendNumber(border, 0.0); pdfioArrayAppendNumber(border, 0.0);
@@ -618,6 +623,50 @@ format_block(docdata_t *dd, // I - Document data
frag = frags + num_frags; frag = frags + num_frags;
switch (mmdGetType(block))
{
case MMD_TYPE_HEADING_1 :
dd->tag = "H1";
break;
case MMD_TYPE_HEADING_2 :
dd->tag = "H2";
break;
case MMD_TYPE_HEADING_3 :
dd->tag = "H3";
break;
case MMD_TYPE_HEADING_4 :
dd->tag = "H4";
break;
case MMD_TYPE_HEADING_5 :
dd->tag = "H5";
break;
case MMD_TYPE_HEADING_6 :
dd->tag = "H6";
break;
case MMD_TYPE_TABLE_HEADER_CELL :
dd->tag = "TH";
break;
case MMD_TYPE_TABLE_BODY_CELL_LEFT :
case MMD_TYPE_TABLE_BODY_CELL_CENTER :
case MMD_TYPE_TABLE_BODY_CELL_RIGHT :
dd->tag = "TD";
break;
default :
dd->tag = "P";
break;
}
if (dd->st)
pdfioContentBeginMarked(dd->st, dd->tag, /*dict*/NULL);
// Loop through the block and render lines... // Loop through the block and render lines...
for (current = mmdGetFirstChild(block), x = left; current; current = next) for (current = mmdGetFirstChild(block), x = left; current; current = next)
{ {
@@ -796,6 +845,9 @@ format_block(docdata_t *dd, // I - Document data
pdfioContentRestore(dd->st); pdfioContentRestore(dd->st);
} }
} }
pdfioContentEndMarked(dd->st);
dd->tag = NULL;
} }
@@ -835,6 +887,9 @@ format_code(docdata_t *dd, // I - Document data
pdfioContentFillAndStroke(dd->st, false); pdfioContentFillAndStroke(dd->st, false);
// Start a code text block... // Start a code text block...
dd->tag = "P";
pdfioContentBeginMarked(dd->st, dd->tag, /*dict*/NULL);
set_font(dd, DOCFONT_MONOSPACE, SIZE_CODEBLOCK); set_font(dd, DOCFONT_MONOSPACE, SIZE_CODEBLOCK);
pdfioContentTextBegin(dd->st); pdfioContentTextBegin(dd->st);
pdfioContentTextMoveTo(dd->st, left, dd->y); pdfioContentTextMoveTo(dd->st, left, dd->y);
@@ -869,6 +924,9 @@ format_code(docdata_t *dd, // I - Document data
pdfioContentTextEnd(dd->st); pdfioContentTextEnd(dd->st);
dd->y += lineheight; dd->y += lineheight;
pdfioContentEndMarked(dd->st);
dd->tag = NULL;
// Draw the bottom padding... // Draw the bottom padding...
set_color(dd, DOCCOLOR_LTGRAY); set_color(dd, DOCCOLOR_LTGRAY);
pdfioContentPathRect(dd->st, left - CODE_PADDING, dd->y - CODE_PADDING - (LINE_HEIGHT - 1.0) * SIZE_CODEBLOCK, right - left + 2.0 * CODE_PADDING, CODE_PADDING); pdfioContentPathRect(dd->st, left - CODE_PADDING, dd->y - CODE_PADDING - (LINE_HEIGHT - 1.0) * SIZE_CODEBLOCK, right - left + 2.0 * CODE_PADDING, CODE_PADDING);
@@ -1144,8 +1202,17 @@ format_table(docdata_t *dd, // I - Document data
} }
// Render each table row... // Render each table row...
dd->in_table = true;
if (dd->st)
pdfioContentBeginMarked(dd->st, "Table", /*dict*/NULL);
for (row = 0, rowptr = rows; row < num_rows; row ++, rowptr ++) for (row = 0, rowptr = rows; row < num_rows; row ++, rowptr ++)
render_row(dd, num_cols, cols, rowptr); render_row(dd, num_cols, cols, rowptr);
pdfioContentEndMarked(dd->st);
dd->in_table = false;
} }
@@ -1359,6 +1426,16 @@ new_page(docdata_t *dd) // I - Document data
// Close the current page... // Close the current page...
if (dd->st) if (dd->st)
{ {
if (dd->tag)
{
// Close current tag and any row or table...
pdfioContentEndMarked(dd->st);
if (dd->in_row)
pdfioContentEndMarked(dd->st);
if (dd->in_table)
pdfioContentEndMarked(dd->st);
}
pdfioStreamClose(dd->st); pdfioStreamClose(dd->st);
add_links(dd); add_links(dd);
} }
@@ -1387,6 +1464,7 @@ new_page(docdata_t *dd) // I - Document data
dd->y = dd->art_box.y2; dd->y = dd->art_box.y2;
// Add header/footer text // Add header/footer text
pdfioContentBeginMarked(dd->st, "Artifact", /*dict*/NULL);
set_color(dd, DOCCOLOR_GRAY); set_color(dd, DOCCOLOR_GRAY);
set_font(dd, DOCFONT_REGULAR, SIZE_HEADFOOT); set_font(dd, DOCFONT_REGULAR, SIZE_HEADFOOT);
@@ -1445,6 +1523,17 @@ new_page(docdata_t *dd) // I - Document data
pdfioContentTextShow(dd->st, UNICODE_VALUE, dd->heading); pdfioContentTextShow(dd->st, UNICODE_VALUE, dd->heading);
pdfioContentTextEnd(dd->st); pdfioContentTextEnd(dd->st);
} }
pdfioContentEndMarked(dd->st);
if (dd->in_table)
pdfioContentBeginMarked(dd->st, "Table", /*dict*/NULL);
if (dd->in_row)
pdfioContentBeginMarked(dd->st, "TR", /*dict*/NULL);
if (dd->tag)
pdfioContentBeginMarked(dd->st, dd->tag, /*dict*/NULL);
} }
@@ -1617,10 +1706,12 @@ render_row(docdata_t *dd, // I - Document data
// Start a new page as needed... // Start a new page as needed...
if (!dd->st) if (!dd->st)
new_page(dd); new_page(dd);
else if ((dd->y - row->height) < dd->art_box.y1)
if ((dd->y - row->height) < dd->art_box.y1)
new_page(dd); new_page(dd);
dd->in_row = true;
pdfioContentBeginMarked(dd->st, "TR", /*dict*/NULL);
if (mmdGetType(row->cells[0]) == MMD_TYPE_TABLE_HEADER_CELL) if (mmdGetType(row->cells[0]) == MMD_TYPE_TABLE_HEADER_CELL)
{ {
// Header row, no border... // Header row, no border...
@@ -1651,6 +1742,9 @@ render_row(docdata_t *dd, // I - Document data
} }
dd->y = row_y - row->height; dd->y = row_y - row->height;
pdfioContentEndMarked(dd->st);
dd->in_row = false;
} }

View File

@@ -3,7 +3,7 @@
// //
// https://www.msweet.org/mmd // https://www.msweet.org/mmd
// //
// Copyright © 2017-2024 by Michael R Sweet. // Copyright © 2017-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@@ -166,7 +166,7 @@ mmdCopyAllText(mmd_t *node) // I - Parent node
char *all = NULL, // String buffer char *all = NULL, // String buffer
*allptr = NULL, // Pointer into string buffer *allptr = NULL, // Pointer into string buffer
*temp; // Temporary pointer *temp; // Temporary pointer
size_t allsize = 1, // Size of "all" buffer size_t allsize = 0, // Size of "all" buffer
textlen; // Length of "text" string textlen; // Length of "text" string
mmd_t *current, // Current node mmd_t *current, // Current node
*next; // Next node *next; // Next node
@@ -179,6 +179,8 @@ mmdCopyAllText(mmd_t *node) // I - Parent node
if (current->text) if (current->text)
{ {
// Append this node's text to the string... // Append this node's text to the string...
long alloff = allptr - all; // Offset within current buffer
textlen = strlen(current->text); textlen = strlen(current->text);
allsize += textlen + (size_t)current->whitespace; allsize += textlen + (size_t)current->whitespace;
temp = realloc(all, allsize); temp = realloc(all, allsize);
@@ -189,8 +191,8 @@ mmdCopyAllText(mmd_t *node) // I - Parent node
return (NULL); return (NULL);
} }
allptr = temp + (allptr - all);
all = temp; all = temp;
allptr = all + alloff;
if (current->whitespace) if (current->whitespace)
*allptr++ = ' '; *allptr++ = ' ';
@@ -1059,6 +1061,8 @@ mmdLoadIO(mmd_t *root, // I - Root node for document or `NULL` for a new d
break; break;
else if (line[0] == '>' && *ptr == '>') else if (line[0] == '>' && *ptr == '>')
memmove(ptr, ptr + 1, strlen(ptr)); memmove(ptr, ptr + 1, strlen(ptr));
DEBUG2_printf(" line=\"%s\"\n", line);
} }
mmd_parse_inline(&doc, block, lineptr); mmd_parse_inline(&doc, block, lineptr);
@@ -1493,7 +1497,7 @@ mmd_parse_inline(_mmd_doc_t *doc, // I - Document
for (text = NULL, type = MMD_TYPE_NORMAL_TEXT; *lineptr; lineptr ++) for (text = NULL, type = MMD_TYPE_NORMAL_TEXT; *lineptr; lineptr ++)
{ {
DEBUG2_printf("mmd_parse_inline: lineptr=%p(\"%32.32s...\"), type=%d, text=%p, whitespace=%d\n", lineptr, lineptr, type, text, whitespace); DEBUG2_printf("mmd_parse_inline: lineptr=%p(\"%s\"), type=%d, text=%p, whitespace=%d\n", lineptr, lineptr, type, text, whitespace);
if (isspace(*lineptr & 255) && type != MMD_TYPE_CODE_TEXT) if (isspace(*lineptr & 255) && type != MMD_TYPE_CODE_TEXT)
{ {
@@ -2090,6 +2094,8 @@ mmd_read_buffer(_mmd_filebuf_t *file) // I - File buffer
if (file->bufptr && file->bufptr > file->buffer) if (file->bufptr && file->bufptr > file->buffer)
{ {
// Discard previous characters in the buffer. // Discard previous characters in the buffer.
DEBUG2_printf("mmd_read_buffer: before buffer=\"%s\"\n", file->bufptr);
memmove(file->buffer, file->bufptr, file->bufend - file->bufptr); memmove(file->buffer, file->bufptr, file->bufend - file->bufptr);
file->bufend -= (file->bufptr - file->buffer); file->bufend -= (file->bufptr - file->buffer);
} }
@@ -2099,11 +2105,13 @@ mmd_read_buffer(_mmd_filebuf_t *file) // I - File buffer
file->bufend = file->buffer; file->bufend = file->buffer;
} }
if ((bytes = (file->cb)(file->cbdata, file->bufend, sizeof(file->buffer) - (size_t)(file->bufend - file->buffer - 1))) > 0) if ((bytes = (file->cb)(file->cbdata, file->bufend, sizeof(file->buffer) - (size_t)(file->bufend - file->buffer) - 1)) > 0)
file->bufend += bytes; file->bufend += bytes;
*(file->bufend) = '\0'; *(file->bufend) = '\0';
file->bufptr = file->buffer; file->bufptr = file->buffer;
DEBUG2_printf("mmd_read_buffer: after buffer=\"%s\"\n", file->buffer);
} }
@@ -2161,6 +2169,8 @@ mmd_read_line(_mmd_filebuf_t *file, // I - File buffer
else if (!strchr(file->bufptr, '\n')) else if (!strchr(file->bufptr, '\n'))
mmd_read_buffer(file); mmd_read_buffer(file);
DEBUG2_printf("mmd_read_line: Returning \"%s\"\n", line);
return (line); return (line);
} }

View File

@@ -1,7 +1,7 @@
// //
// PDF metadata example for PDFio. // PDF metadata example for PDFio.
// //
// Copyright © 2023-2025 by Michael R Sweet. // Copyright © 2023-2026 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.
@@ -113,16 +113,10 @@ main(int argc, // I - Number of command-line arguments
for (cur = 0, prev = 0; cur < num_pages; cur ++) for (cur = 0, prev = 0; cur < num_pages; cur ++)
{ {
// Find the MediaBox for this page in the page tree... // Find the MediaBox for this page in the page tree...
for (page = pdfioFileGetPage(pdf, cur); page = pdfioFileGetPage(pdf, cur);
page != NULL;
page = pdfioDictGetObj(page_dict, "Parent"))
{
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
page_dict = pdfioObjGetDict(page);
if (pdfioDictGetRect(page_dict, "MediaBox", &cur_box)) cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
break; pdfioPageGetRect(page, "MediaBox", &cur_box);
}
// If this MediaBox is different from the previous one, show the range of // If this MediaBox is different from the previous one, show the range of
// pages that have that size... // pages that have that size...

View File

@@ -8,8 +8,8 @@
// //
// Usage: // Usage:
// //
// ./pdfmerge [-o OUTPUT.pdf] INPUT.pdf [... INPUT.pdf] // ./pdfiomerge [-o OUTPUT.pdf] INPUT.pdf [... INPUT.pdf]
// ./pdfmerge INPUT.pdf [... INPUT.pdf] >OUTPUT.pdf // ./pdfiomerge INPUT.pdf [... INPUT.pdf] >OUTPUT.pdf
// //
#include <pdfio.h> #include <pdfio.h>
@@ -47,7 +47,7 @@ main(int argc, // I - Number of command-line arguments
} }
else if (!strncmp(argv[i], "--", 2)) else if (!strncmp(argv[i], "--", 2))
{ {
fprintf(stderr, "pdfmerge: Unknown option '%s'.\n", argv[i]); fprintf(stderr, "pdfiomerge: Unknown option '%s'.\n", argv[i]);
return (usage(stderr)); return (usage(stderr));
} }
else if (argv[i][0] == '-') else if (argv[i][0] == '-')
@@ -59,14 +59,14 @@ main(int argc, // I - Number of command-line arguments
case 'o' : // -o OUTPUT.pdf case 'o' : // -o OUTPUT.pdf
if (outpdf) if (outpdf)
{ {
fputs("pdfmerge: Only one output file can be specified.\n", stderr); fputs("pdfiomerge: Only one output file can be specified.\n", stderr);
return (usage(stderr)); return (usage(stderr));
} }
i ++; i ++;
if (i >= argc) if (i >= argc)
{ {
fputs("pdfmerge: Missing output filename after '-o'.\n", stderr); fputs("pdfiomerge: Missing output filename after '-o'.\n", stderr);
return (usage(stderr)); return (usage(stderr));
} }
@@ -75,7 +75,7 @@ main(int argc, // I - Number of command-line arguments
break; break;
default : default :
fprintf(stderr, "pdfmerge: Unknown option '-%c'.\n", *opt); fprintf(stderr, "pdfiomerge: Unknown option '-%c'.\n", *opt);
return (usage(stderr)); return (usage(stderr));
} }
} }

View File

@@ -7,6 +7,10 @@
# ./makesrcdist [--snapshot] VERSION # ./makesrcdist [--snapshot] VERSION
# #
# Save the current directory...
basedir="$(pwd)"
# Support "--snapshot" option... # Support "--snapshot" option...
if test "$1" == "--snapshot"; then if test "$1" == "--snapshot"; then
shift shift
@@ -15,28 +19,31 @@ else
snapshot=0 snapshot=0
fi fi
# Get version...
# Get the release version...
if test $# != 1; then if test $# != 1; then
echo "Usage: ./makesrcdist [--snapshot] VERSION" echo "Usage: ./makesrcdist [--snapshot] VERSION"
exit 1 exit 1
fi fi
status=0
version=$1 version=$1
version_major=$(echo $1 | awk -F. '{print $1}') version_major=$(echo $1 | awk -F. '{print $1}')
version_minor=$(echo $1 | awk -F. '{print $2}') version_minor=$(echo $1 | awk -F. '{print $2}')
# Check that version number has been updated everywhere... # Check that version number has been updated everywhere...
status=0
if test $(grep AC_INIT configure.ac | awk '{print $2}') != "[$version],"; then if test $(grep AC_INIT configure.ac | awk '{print $2}') != "[$version],"; then
echo "Still need to update AC_INIT version in 'configure.ac'." echo "Still need to update AC_INIT version in 'configure.ac'."
status=1 status=1
fi fi
if test $(head -4 CHANGES.md | tail -1 | awk '{print $1}') != "v$version"; then if test $(head -5 CHANGES.md | tail -1 | awk '{print $1}') != "v$version"; then
echo "Still need to update CHANGES.md version number." echo "Still need to update CHANGES.md version number."
status=1 status=1
fi fi
if test $(head -4 CHANGES.md | tail -1 | awk '{print $3}') = "YYYY-MM-DD"; then if test $(head -5 CHANGES.md | tail -1 | awk '{print $3}') = "YYYY-MM-DD"; then
echo "Still need to update CHANGES.md release date." echo "Still need to update CHANGES.md release date."
status=1 status=1
fi fi
@@ -78,18 +85,32 @@ if test $status = 1; then
exit 1 exit 1
fi fi
# Tag release... # Tag release...
if test $snapshot = 0; then if test $snapshot = 0; then
echo Creating tag for release... echo "Creating tag v$version for release..."
git tag -m "Tag $version" v$version git tag -m "Tag $version" v$version
git push origin v$version git push origin v$version
fi fi
# Make source archives...
echo Creating pdfio-$version.tar.gz...
git archive --format tar --prefix=pdfio-$version/ HEAD | gzip -v9 >pdfio-$version.tar.gz
gpg --detach-sign pdfio-$version.tar.gz
echo Creating pdfio-$version.zip... # Make and sign source archives...
git archive --format zip --prefix=pdfio-$version/ HEAD >pdfio-$version.zip echo "Exporting $version..."
gpg --detach-sign pdfio-$version.zip rm -rf $TMPDIR/pdfio-$version
mkdir $TMPDIR/pdfio-$version
git archive --format tar HEAD | (cd $TMPDIR/pdfio-$version; tar xf -)
(cd ttf; git archive --prefix=ttf/ HEAD) | (cd $TMPDIR/pdfio-$version; tar xf -)
cd $TMPDIR
echo "Creating pdfio-$version.tar.gz..."
tar cf - pdfio-$version | gzip -v9 >"$basedir/pdfio-$version.tar.gz"
gpg --detach-sign "$basedir/pdfio-$version.tar.gz"
echo "Creating pdfio-$version.zip..."
zip -r "$basedir/pdfio-$version.zip" pdfio-$version
gpg --detach-sign "$basedir/pdfio-$version.zip"
# Clean up...
echo "Removing temporary files..."
rm -rf pdfio-$version

View File

@@ -1,7 +1,7 @@
// //
// AES functions for PDFio. // AES functions for PDFio.
// //
// Copyright © 2021-2025 by Michael R Sweet. // Copyright © 2021-2026 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.
@@ -117,7 +117,7 @@ _pdfioCryptoAESInit(
memcpy(ctx->round_key, key, keylen); memcpy(ctx->round_key, key, keylen);
// All other round keys are found from the previous round keys. // All other round keys are found from the previous round keys.
for (rkptr0 = ctx->round_key, rkptr = rkptr0 + keylen, rkend = rkptr + 16 * ctx->round_size, i = nwords; rkptr < rkend; i ++) for (rkptr0 = ctx->round_key, rkptr = rkptr0 + keylen, rkend = rkptr0 + 16 * ctx->round_size + 16, i = nwords; rkptr < rkend; i ++)
{ {
if ((i % nwords) == 0) if ((i % nwords) == 0)
{ {

View File

@@ -1,7 +1,7 @@
// //
// PDF array functions for PDFio. // PDF array functions for PDFio.
// //
// Copyright © 2021-2024 by Michael R Sweet. // Copyright © 2021-2026 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.
@@ -262,7 +262,11 @@ pdfioArrayCopy(pdfio_file_t *pdf, // I - PDF file
vdst; // Current destination value vdst; // Current destination value
PDFIO_DEBUG("pdfioArrayCopy(pdf=%p, a=%p(%p))\n", pdf, a, a ? a->pdf : NULL); PDFIO_DEBUG("pdfioArrayCopy(pdf=%p, a=%p(%p))\n", (void *)pdf, (void *)a, a ? (void *)a->pdf : NULL);
// Range check input...
if (!pdf || !a)
return (NULL);
// Create the new array... // Create the new array...
if ((na = pdfioArrayCreate(pdf)) == NULL) if ((na = pdfioArrayCreate(pdf)) == NULL)
@@ -606,7 +610,7 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
_pdfio_value_t value; // Value _pdfio_value_t value; // Value
PDFIO_DEBUG("_pdfioArrayRead(pdf=%p, tb=%p)\n", pdf, tb); PDFIO_DEBUG("_pdfioArrayRead(pdf=%p, tb=%p)\n", (void *)pdf, (void *)tb);
// Create an array... // Create an array...
if ((array = pdfioArrayCreate(pdf)) == NULL) if ((array = pdfioArrayCreate(pdf)) == NULL)
@@ -640,6 +644,8 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
// //
// 'pdfioArrayRemove()' - Remove an array entry. // 'pdfioArrayRemove()' - Remove an array entry.
// //
// @since PDFio v1.4@
//
bool // O - `true` on success, `false` otherwise bool // O - `true` on success, `false` otherwise
pdfioArrayRemove(pdfio_array_t *a, // I - Array pdfioArrayRemove(pdfio_array_t *a, // I - Array
@@ -664,27 +670,29 @@ pdfioArrayRemove(pdfio_array_t *a, // I - Array
// //
bool // O - `true` on success, `false` otherwise bool // O - `true` on success, `false` otherwise
_pdfioArrayWrite(pdfio_array_t *a, // I - Array _pdfioArrayWrite(
pdfio_obj_t *obj) // I - Object, if any _pdfio_printf_t cb, // I - Printf callback function
void *cbdata, // I - Printf callback data
pdfio_obj_t *obj, // I - Object, if any
pdfio_array_t *a) // I - Array
{ {
pdfio_file_t *pdf = a->pdf; // PDF file
size_t i; // Looping var size_t i; // Looping var
_pdfio_value_t *v; // Current value _pdfio_value_t *v; // Current value
// Arrays are surrounded by square brackets ([ ... ]) // Arrays are surrounded by square brackets ([ ... ])
if (!_pdfioFilePuts(pdf, "[")) if (!(cb)(cbdata, "["))
return (false); return (false);
// Write each value... // Write each value...
for (i = a->num_values, v = a->values; i > 0; i --, v ++) for (i = a->num_values, v = a->values; i > 0; i --, v ++)
{ {
if (!_pdfioValueWrite(pdf, obj, v, NULL)) if (!_pdfioValueWrite(cb, cbdata, obj, v, NULL))
return (false); return (false);
} }
// Closing bracket... // Closing bracket...
return (_pdfioFilePuts(pdf, "]")); return ((cb)(cbdata, "]"));
} }

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

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

View File

@@ -27,7 +27,7 @@ bool // O - `true` on sucess, `false` on EOF
_pdfioFileConsume(pdfio_file_t *pdf, // I - PDF file _pdfioFileConsume(pdfio_file_t *pdf, // I - PDF file
size_t bytes) // I - Bytes to consume size_t bytes) // I - Bytes to consume
{ {
PDFIO_DEBUG("_pdfioFileConsume(pdf=%p, bytes=%u)\n", pdf, (unsigned)bytes); PDFIO_DEBUG("_pdfioFileConsume(pdf=%p, bytes=%u)\n", (void *)pdf, (unsigned)bytes);
if ((size_t)(pdf->bufend - pdf->bufptr) > bytes) if ((size_t)(pdf->bufend - pdf->bufptr) > bytes)
pdf->bufptr += bytes; pdf->bufptr += bytes;
@@ -47,7 +47,7 @@ _pdfioFileConsume(pdfio_file_t *pdf, // I - PDF file
// `false` to halt. // `false` to halt.
// //
bool // O - `false` to stop bool // O - `false` to stop, `true` to continue
_pdfioFileDefaultError( _pdfioFileDefaultError(
pdfio_file_t *pdf, // I - PDF file pdfio_file_t *pdf, // I - PDF file
const char *message, // I - Error message const char *message, // I - Error message
@@ -57,7 +57,7 @@ _pdfioFileDefaultError(
fprintf(stderr, "%s: %s\n", pdf->filename, message); fprintf(stderr, "%s: %s\n", pdf->filename, message);
return (false); return (!strncmp(message, "WARNING:", 8));
} }
@@ -91,7 +91,7 @@ _pdfioFileError(pdfio_file_t *pdf, // I - PDF file
bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
_pdfioFileFlush(pdfio_file_t *pdf) // I - PDF file _pdfioFileFlush(pdfio_file_t *pdf) // I - PDF file
{ {
PDFIO_DEBUG("_pdfioFileFlush(pdf=%p)\n", pdf); PDFIO_DEBUG("_pdfioFileFlush(pdf=%p)\n", (void *)pdf);
if (pdf->bufptr > pdf->buffer) if (pdf->bufptr > pdf->buffer)
{ {
@@ -134,19 +134,20 @@ _pdfioFileGetChar(pdfio_file_t *pdf) // I - PDF file
bool // O - `true` on success, `false` on error bool // O - `true` on success, `false` on error
_pdfioFileGets(pdfio_file_t *pdf, // I - PDF file _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
char *buffer, // I - Line buffer char *buffer, // I - Line buffer
size_t bufsize) // I - Size of line buffer size_t bufsize, // I - Size of line buffer
bool discard) // I - OK to discard excess line chars?
{ {
bool eol = false; // End of line? bool eol = false; // End of line?
char *bufptr = buffer, // Pointer into buffer char *bufptr = buffer, // Pointer into buffer
*bufend = buffer + bufsize - 1; // Pointer to end of buffer *bufend = buffer + bufsize - 1; // Pointer to end of buffer
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p, offset=%lu\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend, (unsigned long)(pdf->bufpos + (pdf->bufptr - pdf->buffer))); PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu, discard=%s) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p, offset=%lu\n", (void *)pdf, (void *)buffer, (unsigned long)bufsize, discard ? "true" : "false", (long)pdf->bufpos, (void *)pdf->buffer, (void *)pdf->bufptr, (void *)pdf->bufend, (unsigned long)(pdf->bufpos + (pdf->bufptr - pdf->buffer)));
while (!eol) while (!eol)
{ {
// If there are characters ready in the buffer, use them... // If there are characters ready in the buffer, use them...
while (!eol && pdf->bufptr < pdf->bufend && bufptr < bufend) while (!eol && pdf->bufptr < pdf->bufend)
{ {
char ch = *(pdf->bufptr++); // Next character in buffer char ch = *(pdf->bufptr++); // Next character in buffer
@@ -168,8 +169,10 @@ _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
pdf->bufptr ++; pdf->bufptr ++;
} }
} }
else else if (bufptr < bufend)
*bufptr++ = ch; *bufptr++ = ch;
else if (!discard)
break;
} }
// Fill the read buffer as needed... // Fill the read buffer as needed...
@@ -356,7 +359,7 @@ _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) pdf->bufpos=%lu\n", pdf, (long)offset, whence, (unsigned long)(pdf ? pdf->bufpos : 0)); PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d) pdf->bufpos=%lu\n", (void *)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)
@@ -373,7 +376,7 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
// 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(<%02X%02X...>), bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufptr[0] & 255, pdf->bufptr[1] & 255, pdf->bufend); PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p(<%02X%02X...>), bufend=%p\n", (void *)pdf->buffer, (void *)pdf->bufptr, pdf->bufptr[0] & 255, pdf->bufptr[1] & 255, (void *)pdf->bufend);
return (offset); return (offset);
} }
@@ -408,7 +411,7 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
} }
PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld, offset=%lu.\n", (long)pdf->bufpos, (unsigned long)offset); 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", (void *)pdf->buffer, (void *)pdf->bufptr, (void *)pdf->bufend);
pdf->bufpos = offset; pdf->bufpos = offset;
@@ -424,7 +427,7 @@ off_t // O - Offset from beginning of file
_pdfioFileTell(pdfio_file_t *pdf) // I - PDF file _pdfioFileTell(pdfio_file_t *pdf) // I - PDF file
{ {
if (pdf->bufptr) if (pdf->bufptr)
return (pdf->bufpos + (pdf->bufptr - pdf->buffer)); return (pdf->bufpos + (off_t)(pdf->bufptr - pdf->buffer));
else else
return (pdf->bufpos); return (pdf->bufpos);
} }
@@ -452,7 +455,7 @@ _pdfioFileWrite(pdfio_file_t *pdf, // I - PDF file
if (!write_buffer(pdf, buffer, bytes)) if (!write_buffer(pdf, buffer, bytes))
return (false); return (false);
pdf->bufpos += bytes; pdf->bufpos += (off_t)bytes;
return (true); return (true);
} }
@@ -478,7 +481,7 @@ fill_buffer(pdfio_file_t *pdf) // I - PDF file
// Advance current position in file as needed... // Advance current position in file as needed...
if (pdf->bufend) if (pdf->bufend)
pdf->bufpos += pdf->bufend - pdf->buffer; pdf->bufpos += (off_t)(pdf->bufend - pdf->buffer);
// Try reading from the file... // Try reading from the file...
if ((bytes = read_buffer(pdf, pdf->buffer, sizeof(pdf->buffer))) <= 0) if ((bytes = read_buffer(pdf, pdf->buffer, sizeof(pdf->buffer))) <= 0)

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
// //
// Public content header file for PDFio. // Public content header file for PDFio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@@ -23,7 +23,8 @@ typedef enum pdfio_cs_e // Standard color spaces
{ {
PDFIO_CS_ADOBE, // AdobeRGB 1998 PDFIO_CS_ADOBE, // AdobeRGB 1998
PDFIO_CS_P3_D65, // Display P3 PDFIO_CS_P3_D65, // Display P3
PDFIO_CS_SRGB // sRGB PDFIO_CS_SRGB, // sRGB
PDFIO_CS_CGATS001 // CGATS001 (CMYK)
} pdfio_cs_t; } pdfio_cs_t;
typedef enum pdfio_linecap_e // Line capping modes typedef enum pdfio_linecap_e // Line capping modes
@@ -68,8 +69,10 @@ extern pdfio_array_t *pdfioArrayCreateColorFromPrimaries(pdfio_file_t *pdf, size
extern pdfio_array_t *pdfioArrayCreateColorFromStandard(pdfio_file_t *pdf, size_t num_colors, pdfio_cs_t cs); extern pdfio_array_t *pdfioArrayCreateColorFromStandard(pdfio_file_t *pdf, size_t num_colors, pdfio_cs_t cs);
// PDF content drawing functions... // PDF content drawing functions...
extern bool pdfioContentBeginMarked(pdfio_stream_t *st, const char *tag, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern bool pdfioContentClip(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC; extern bool pdfioContentClip(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC;
extern bool pdfioContentDrawImage(pdfio_stream_t *st, const char *name, double x, double y, double w, double h) _PDFIO_PUBLIC; extern bool pdfioContentDrawImage(pdfio_stream_t *st, const char *name, double x, double y, double w, double h) _PDFIO_PUBLIC;
extern bool pdfioContentEndMarked(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentFill(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC; extern bool pdfioContentFill(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC;
extern bool pdfioContentFillAndStroke(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC; extern bool pdfioContentFillAndStroke(pdfio_stream_t *st, bool even_odd) _PDFIO_PUBLIC;
extern bool pdfioContentMatrixConcat(pdfio_stream_t *st, pdfio_matrix_t m) _PDFIO_PUBLIC; extern bool pdfioContentMatrixConcat(pdfio_stream_t *st, pdfio_matrix_t m) _PDFIO_PUBLIC;
@@ -127,7 +130,9 @@ extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char
extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, size_t num_fragments, const double *offsets, const char * const *fragments) _PDFIO_PUBLIC; extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, size_t num_fragments, const double *offsets, const char * const *fragments) _PDFIO_PUBLIC;
// Resource helpers... // Resource helpers...
extern void pdfioFileAddOutputIntent(pdfio_file_t *pdf, const char *subtype, const char *condition, const char *cond_id, const char *reg_name, const char *info, pdfio_obj_t *profile) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateFontObjFromData(pdfio_file_t *pdf, const void *data, size_t datasize, bool unicode) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateICCObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t datalen, size_t num_colors) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateICCObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t datalen, size_t num_colors) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateICCObjFromFile(pdfio_file_t *pdf, const char *filename, size_t num_colors) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateICCObjFromFile(pdfio_file_t *pdf, const char *filename, size_t num_colors) _PDFIO_PUBLIC;

View File

@@ -1,7 +1,7 @@
// //
// Cryptographic support functions for PDFio. // Cryptographic support functions for PDFio.
// //
// Copyright © 2021-2025 by Michael R Sweet. // Copyright © 2021-2026 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.
@@ -96,12 +96,14 @@ static uint8_t pdf_passpad[32] = // Padding for passwords
// Local functions... // Local functions...
// //
static void decrypt_user_key(pdfio_encryption_t encryption, const uint8_t *file_key, uint8_t user_key[32]); static void decrypt_ou_key(pdfio_encryption_t encryption, const uint8_t *file_key, size_t file_keylen, uint8_t ou_key[32]);
static void encrypt_user_key(pdfio_encryption_t encryption, const uint8_t *file_key, uint8_t user_key[32]); static void encrypt_ou_key(pdfio_encryption_t encryption, const uint8_t *file_key, size_t file_keylen, uint8_t ou_key[32]);
static void make_file_key(pdfio_encryption_t encryption, pdfio_permission_t permissions, const unsigned char *file_id, size_t file_idlen, const uint8_t *user_pad, const uint8_t *owner_key, uint8_t file_key[16]); static void make_file_key(pdfio_encryption_t encryption, size_t file_keylen, pdfio_permission_t permissions, const unsigned char *file_id, size_t file_idlen, const uint8_t *user_pad, const uint8_t *owner_key, bool encrypt_metadata, uint8_t file_key[16]);
static void make_owner_key(pdfio_encryption_t encryption, const uint8_t *owner_pad, const uint8_t *user_pad, uint8_t owner_key[32]); static void make_owner_key(pdfio_encryption_t encryption, size_t file_keylen, const uint8_t *owner_pad, const uint8_t *user_pad, uint8_t owner_key[32]);
static void make_password_key(pdfio_encryption_t encryption, size_t file_keylen, const uint8_t *pad, uint8_t *key);
static void make_user_key(const unsigned char *file_id, size_t file_idlen, uint8_t user_key[32]); static void make_user_key(const unsigned char *file_id, size_t file_idlen, uint8_t user_key[32]);
static void pad_password(const char *password, uint8_t pad[32]); static void pad_password(const char *password, uint8_t pad[32]);
static bool test_password(pdfio_encryption_t encryption, size_t file_keylen, pdfio_permission_t permisions,const unsigned char *file_id, size_t file_idlen, const uint8_t *user_pad, const uint8_t *user_key, const uint8_t *owner_key, bool encrypt_metadata, uint8_t file_key[16]);
// //
@@ -139,6 +141,7 @@ _pdfioCryptoLock(
case PDFIO_ENCRYPTION_AES_128 : case PDFIO_ENCRYPTION_AES_128 :
// Create the 128-bit encryption keys... // Create the 128-bit encryption keys...
pad_password(user_password, user_pad); pad_password(user_password, user_pad);
pdf->file_keylen = 16;
if (!owner_password && user_password && *user_password) if (!owner_password && user_password && *user_password)
{ {
@@ -152,18 +155,17 @@ _pdfioCryptoLock(
} }
// Compute the owner key... // Compute the owner key...
make_owner_key(encryption, owner_pad, user_pad, pdf->owner_key); make_owner_key(encryption, pdf->file_keylen, owner_pad, user_pad, pdf->owner_key);
pdf->owner_keylen = 32; pdf->owner_keylen = 32;
// Generate the encryption key // Generate the encryption key
file_id = pdfioArrayGetBinary(pdf->id_array, 0, &file_idlen); file_id = pdfioArrayGetBinary(pdf->id_array, 0, &file_idlen);
make_file_key(encryption, permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->file_key); make_file_key(encryption, pdf->file_keylen, permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->encrypt_metadata, pdf->file_key);
pdf->file_keylen = 16;
// Generate the user key... // Generate the user key...
make_user_key(file_id, file_idlen, pdf->user_key); make_user_key(file_id, file_idlen, pdf->user_key);
encrypt_user_key(encryption, pdf->file_key, pdf->user_key); encrypt_ou_key(encryption, pdf->file_key, pdf->file_keylen, pdf->user_key);
pdf->user_keylen = 32; pdf->user_keylen = 32;
// Save everything in the dictionary... // Save everything in the dictionary...
@@ -214,8 +216,9 @@ _pdfioCryptoLock(
pdfioObjClose(pdf->encrypt_obj); pdfioObjClose(pdf->encrypt_obj);
pdf->encryption = encryption; pdf->encrypt_dict = dict;
pdf->permissions = permissions; pdf->encryption = encryption;
pdf->permissions = permissions;
return (true); return (true);
} }
@@ -409,16 +412,9 @@ _pdfioCryptoMakeReader(
uint8_t data[21]; // Key data uint8_t data[21]; // Key data
_pdfio_md5_t md5; // MD5 state _pdfio_md5_t md5; // MD5 state
uint8_t digest[16]; // MD5 digest value uint8_t digest[16]; // MD5 digest value
#if PDFIO_OBJ_CRYPT
pdfio_array_t *id_array; // Object ID array
unsigned char *id_value; // Object ID value
size_t id_len; // Length of object ID
uint8_t temp_key[16]; // File key for object
#endif // PDFIO_OBJ_CRYPT
uint8_t *file_key; // Computed file key to use
PDFIO_DEBUG("_pdfioCryptoMakeReader(pdf=%p, obj=%p(%d), ctx=%p, iv=%p, ivlen=%p(%d))\n", pdf, obj, (int)obj->number, ctx, iv, ivlen, (int)*ivlen); PDFIO_DEBUG("_pdfioCryptoMakeReader(pdf=%p, obj=%p(%d), ctx=%p, iv=%p, ivlen=%p(%d))\n", (void *)pdf, (void *)obj, (int)obj->number, (void *)ctx, (void *)iv, (void *)ivlen, (int)*ivlen);
// Range check input... // Range check input...
if (!pdf) if (!pdf)
@@ -427,60 +423,6 @@ _pdfioCryptoMakeReader(
return (NULL); return (NULL);
} }
#if PDFIO_OBJ_CRYPT
if ((id_array = pdfioDictGetArray(pdfioObjGetDict(obj), "ID")) != NULL)
{
// Object has its own ID that will get used for encryption...
_pdfio_md5_t md5; // MD5 context
uint8_t file_digest[16]; // MD5 digest of file ID and pad
uint8_t user_pad[32], // Padded user password
own_user_key[32], // Calculated user key
pdf_user_key[32]; // Decrypted user key
PDFIO_DEBUG("_pdfioCryptoMakeReader: Per-object file ID.\n");
if ((id_value = pdfioArrayGetBinary(id_array, 0, &id_len)) == NULL)
{
*ivlen = 0;
return (NULL);
}
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, pdf_passpad, 32);
_pdfioCryptoMD5Append(&md5, id_value, id_len);
_pdfioCryptoMD5Finish(&md5, file_digest);
make_owner_key(pdf->encryption, pdf->password, pdf->owner_key, user_pad);
make_file_key(pdf->encryption, pdf->permissions, id_value, id_len, user_pad, pdf->owner_key, temp_key);
make_user_key(id_value, id_len, own_user_key);
if (memcmp(own_user_key, pdf->user_key, sizeof(own_user_key)))
{
PDFIO_DEBUG("_pdfioCryptoMakeReader: Not user password, trying owner password.\n");
make_file_key(pdf->encryption, pdf->permissions, id_value, id_len, pdf->password, pdf->owner_key, temp_key);
make_user_key(id_value, id_len, own_user_key);
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key));
decrypt_user_key(pdf->encryption, temp_key, pdf_user_key);
if (memcmp(pdf->password, pdf_user_key, 32) && memcmp(own_user_key, pdf_user_key, 16))
{
_pdfioFileError(pdf, "Unable to unlock file.");
*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 :
@@ -490,20 +432,20 @@ _pdfioCryptoMakeReader(
case PDFIO_ENCRYPTION_RC4_40 : case PDFIO_ENCRYPTION_RC4_40 :
// Copy the key data for the MD5 hash. // Copy the key data for the MD5 hash.
memcpy(data, file_key, 16); memcpy(data, pdf->file_key, 5);
data[16] = (uint8_t)obj->number; data[5] = (uint8_t)obj->number;
data[17] = (uint8_t)(obj->number >> 8); data[6] = (uint8_t)(obj->number >> 8);
data[18] = (uint8_t)(obj->number >> 16); data[7] = (uint8_t)(obj->number >> 16);
data[19] = (uint8_t)obj->generation; data[8] = (uint8_t)obj->generation;
data[20] = (uint8_t)(obj->generation >> 8); data[9] = (uint8_t)(obj->generation >> 8);
// Hash it... // Hash it...
_pdfioCryptoMD5Init(&md5); _pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, data, sizeof(data)); _pdfioCryptoMD5Append(&md5, data, 10);
_pdfioCryptoMD5Finish(&md5, digest); _pdfioCryptoMD5Finish(&md5, digest);
// Initialize the RC4 context using 40 bits of the digest... // Initialize the RC4 context using 80 bits of the digest...
_pdfioCryptoRC4Init(&ctx->rc4, digest, 5); _pdfioCryptoRC4Init(&ctx->rc4, digest, 10);
*ivlen = 0; *ivlen = 0;
return ((_pdfio_crypto_cb_t)_pdfioCryptoRC4Crypt); return ((_pdfio_crypto_cb_t)_pdfioCryptoRC4Crypt);
@@ -515,9 +457,13 @@ _pdfioCryptoMakeReader(
return (NULL); return (NULL);
} }
#ifdef __APPLE__
__attribute__((fallthrough));
#endif // __APPLE__
case PDFIO_ENCRYPTION_RC4_128 : case PDFIO_ENCRYPTION_RC4_128 :
// Copy the key data for the MD5 hash. // Copy the key data for the MD5 hash.
memcpy(data, file_key, 16); memcpy(data, pdf->file_key, 16);
data[16] = (uint8_t)obj->number; data[16] = (uint8_t)obj->number;
data[17] = (uint8_t)(obj->number >> 8); data[17] = (uint8_t)(obj->number >> 8);
data[18] = (uint8_t)(obj->number >> 16); data[18] = (uint8_t)(obj->number >> 16);
@@ -565,7 +511,7 @@ _pdfioCryptoMakeWriter(
uint8_t digest[16]; /* MD5 digest value */ uint8_t digest[16]; /* MD5 digest value */
PDFIO_DEBUG("_pdfioCryptoMakeWriter(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("_pdfioCryptoMakeWriter(pdf=%p, obj=%p(%d), ctx=%p, iv=%p, ivlen=%p(%d))\n", (void *)pdf, (void *)obj, (int)obj->number, (void *)ctx, (void *)iv, (void *)ivlen, (int)*ivlen);
// Range check input... // Range check input...
if (!pdf) if (!pdf)
@@ -627,7 +573,6 @@ _pdfioCryptoUnlock(
{ {
int tries; // Number of tries int tries; // Number of tries
const char *password = NULL; // Password to try const char *password = NULL; // Password to try
pdfio_dict_t *encrypt_dict; // Encrypt objection dictionary
int version, // Version value int version, // Version value
revision, // Revision value revision, // Revision value
length; // Key length value length; // Key length value
@@ -643,22 +588,23 @@ _pdfioCryptoUnlock(
file_idlen; // Length of file ID file_idlen; // Length of file ID
_pdfio_md5_t md5; // MD5 context _pdfio_md5_t md5; // MD5 context
uint8_t file_digest[16]; // MD5 digest of file ID and pad uint8_t file_digest[16]; // MD5 digest of file ID and pad
double p; // Permissions value as a double
_pdfio_value_t *value; // Encrypt dictionary value, if any
// See if we support the type of encryption specified by the Encrypt object // See if we support the type of encryption specified by the Encrypt
// dictionary... // dictionary...
if ((encrypt_dict = pdfioObjGetDict(pdf->encrypt_obj)) == NULL) handler = pdfioDictGetName(pdf->encrypt_dict, "Filter");
{ version = (int)pdfioDictGetNumber(pdf->encrypt_dict, "V");
_pdfioFileError(pdf, "Unable to get encryption dictionary."); revision = (int)pdfioDictGetNumber(pdf->encrypt_dict, "R");
return (false); length = (int)pdfioDictGetNumber(pdf->encrypt_dict, "Length");
}
handler = pdfioDictGetName(encrypt_dict, "Filter"); if ((value = _pdfioDictGetValue(pdf->encrypt_dict, "EncryptMetadata")) != NULL && value->type == PDFIO_VALTYPE_BOOLEAN)
version = (int)pdfioDictGetNumber(encrypt_dict, "V"); pdf->encrypt_metadata = value->value.boolean;
revision = (int)pdfioDictGetNumber(encrypt_dict, "R"); else
length = (int)pdfioDictGetNumber(encrypt_dict, "Length"); pdf->encrypt_metadata = true;
PDFIO_DEBUG("_pdfioCryptoUnlock: handler=%p(%s), version=%d, revision=%d, length=%d\n", (void *)handler, handler ? handler : "(null)", version, revision, length); PDFIO_DEBUG("_pdfioCryptoUnlock: handler=%p(%s), version=%d, revision=%d, length=%d, encrypt_metadata=%s\n", (void *)handler, handler ? handler : "(null)", version, revision, length, pdf->encrypt_metadata ? "true" : "false");
if (!handler || strcmp(handler, "Standard")) if (!handler || strcmp(handler, "Standard"))
{ {
@@ -672,9 +618,9 @@ _pdfioCryptoUnlock(
pdfio_dict_t *filter; // Crypt Filter pdfio_dict_t *filter; // Crypt Filter
const char *cfm; // Crypt filter method const char *cfm; // Crypt filter method
stream_filter = pdfioDictGetName(encrypt_dict, "StmF"); stream_filter = pdfioDictGetName(pdf->encrypt_dict, "StmF");
string_filter = pdfioDictGetName(encrypt_dict, "StrF"); string_filter = pdfioDictGetName(pdf->encrypt_dict, "StrF");
cf_dict = pdfioDictGetDict(encrypt_dict, "CF"); cf_dict = pdfioDictGetDict(pdf->encrypt_dict, "CF");
if (!cf_dict) if (!cf_dict)
{ {
@@ -747,15 +693,25 @@ _pdfioCryptoUnlock(
_pdfioFileError(pdf, "Unsupported encryption V%d R%d.", version, revision); _pdfioFileError(pdf, "Unsupported encryption V%d R%d.", version, revision);
return (false); return (false);
} }
else if (length < 40 || length > 128)
{
_pdfioFileError(pdf, "Unsupported key length %d.", length);
return (false);
}
// Grab the remaining values we need to unlock the PDF... // Grab the remaining values we need to unlock the PDF...
pdf->file_keylen = (size_t)(length / 8); pdf->file_keylen = (size_t)(length / 8);
pdf->permissions = (pdfio_permission_t)pdfioDictGetNumber(encrypt_dict, "P");
p = pdfioDictGetNumber(pdf->encrypt_dict, "P");
PDFIO_DEBUG("_pdfioCryptoUnlock: P=%.0f\n", p);
if (p < 0x7fffffff) // Handle integers > 2^31-1
pdf->permissions = (pdfio_permission_t)p;
else
pdf->permissions = (pdfio_permission_t)(p - 4294967296.0);
PDFIO_DEBUG("_pdfioCryptoUnlock: permissions=%d\n", pdf->permissions); PDFIO_DEBUG("_pdfioCryptoUnlock: permissions=%d\n", pdf->permissions);
owner_key = pdfioDictGetBinary(encrypt_dict, "O", &owner_keylen); owner_key = pdfioDictGetBinary(pdf->encrypt_dict, "O", &owner_keylen);
user_key = pdfioDictGetBinary(encrypt_dict, "U", &user_keylen); user_key = pdfioDictGetBinary(pdf->encrypt_dict, "U", &user_keylen);
if (!owner_key) if (!owner_key)
{ {
@@ -784,7 +740,7 @@ _pdfioCryptoUnlock(
return (false); return (false);
} }
PDFIO_DEBUG("_pdfioCryptoUnlock: user_key[%d]=%02X%02X%02X%02X...%02X%02X%02X%02X\n", (int)user_keylen, 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: user_key[%d]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", (int)user_keylen, user_key[0], user_key[1], user_key[2], user_key[3], user_key[28], user_key[29], user_key[30], user_key[31]);
memcpy(pdf->user_key, user_key, user_keylen); memcpy(pdf->user_key, user_key, user_keylen);
pdf->user_keylen = user_keylen; pdf->user_keylen = user_keylen;
@@ -795,6 +751,9 @@ _pdfioCryptoUnlock(
return (false); return (false);
} }
PDFIO_DEBUG("_pdfioCryptoUnlock: P=%d\n", pdf->permissions);
PDFIO_DEBUG("_pdfioCryptoUnlock: file_id(%d)=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", (int)file_idlen, file_id[0], file_id[1], file_id[2], file_id[3], file_id[12], file_id[13], file_id[14], file_id[15]);
// Generate a base hash from known values... // Generate a base hash from known values...
_pdfioCryptoMD5Init(&md5); _pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, pdf_passpad, 32); _pdfioCryptoMD5Append(&md5, pdf_passpad, 32);
@@ -806,56 +765,34 @@ _pdfioCryptoUnlock(
{ {
if (pdf->encryption <= PDFIO_ENCRYPTION_AES_128) if (pdf->encryption <= PDFIO_ENCRYPTION_AES_128)
{ {
// RC4 and AES-128 encryption...
uint8_t pad[32], // Padded password uint8_t pad[32], // Padded password
file_key[16], // File key padkey[16], // Password key
user_pad[32], // Padded user password file_key[16]; // File key
own_user_key[32], // Calculated user key
pdf_user_key[32]; // Decrypted user key
// Pad the supplied password, if any... // Pad the supplied password, if any...
pad_password(password, pad); pad_password(password, pad);
// Generate keys to see if things match... // Test the user password...
PDFIO_DEBUG("_pdfioCryptoUnlock: Trying %02X%02X%02X%02X...%02X%02X%02X%02X\n", pad[0], pad[1], pad[2], pad[3], pad[28], pad[29], pad[30], pad[31]); PDFIO_DEBUG("_pdfioCryptoUnlock: Trying <%02X%02X%02X%02X...%02X%02X%02X%02X>\n", pad[0], pad[1], pad[2], pad[3], pad[28], pad[29], pad[30], pad[31]);
PDFIO_DEBUG("_pdfioCryptoUnlock: P=%d\n", pdf->permissions);
PDFIO_DEBUG("_pdfioCryptoUnlock: Fid(%d)=%02X%02X%02X%02X...%02X%02X%02X%02X\n", (int)file_idlen, file_id[0], file_id[1], file_id[2], file_id[3], file_id[12], file_id[13], file_id[14], file_id[15]);
make_owner_key(pdf->encryption, pad, pdf->owner_key, user_pad); if (test_password(pdf->encryption, pdf->file_keylen, pdf->permissions, file_id, file_idlen, pad, pdf->user_key, pdf->owner_key, pdf->encrypt_metadata, file_key))
PDFIO_DEBUG("_pdfioCryptoUnlock: Upad=%02X%02X%02X%02X...%02X%02X%02X%02X\n", user_pad[0], user_pad[1], user_pad[2], user_pad[3], user_pad[28], user_pad[29], user_pad[30], user_pad[31]);
make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, user_pad, pdf->owner_key, file_key);
PDFIO_DEBUG("_pdfioCryptoUnlock: Fown=%02X%02X%02X%02X...%02X%02X%02X%02X\n", file_key[0], file_key[1], file_key[2], file_key[3], file_key[12], file_key[13], file_key[14], file_key[15]);
make_user_key(file_id, file_idlen, own_user_key);
PDFIO_DEBUG("_pdfioCryptoUnlock: U=%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: Uown=%02X%02X%02X%02X...%02X%02X%02X%02X\n", own_user_key[0], own_user_key[1], own_user_key[2], own_user_key[3], own_user_key[28], own_user_key[29], own_user_key[30], own_user_key[31]);
if (!memcmp(own_user_key, pdf->user_key, sizeof(own_user_key)))
{ {
// Matches! memcpy(pdf->file_key, file_key, pdf->file_keylen);
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
memcpy(pdf->password, pad, sizeof(pdf->password)); memcpy(pdf->password, pad, sizeof(pdf->password));
return (true); return (true);
} }
// Not the owner password, try the user password... // Test the owner password...
make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, pad, pdf->owner_key, file_key); make_password_key(pdf->encryption, pdf->file_keylen, pad, padkey);
PDFIO_DEBUG("_pdfioCryptoUnlock: Fuse=%02X%02X%02X%02X...%02X%02X%02X%02X\n", file_key[0], file_key[1], file_key[2], file_key[3], file_key[12], file_key[13], file_key[14], file_key[15]);
make_user_key(file_id, file_idlen, own_user_key); memcpy(pad, pdf->owner_key, sizeof(pad));
decrypt_ou_key(pdf->encryption, padkey, pdf->file_keylen, pad);
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key)); if (test_password(pdf->encryption, pdf->file_keylen, pdf->permissions, file_id, file_idlen, pad, pdf->user_key, pdf->owner_key, pdf->encrypt_metadata, file_key))
decrypt_user_key(pdf->encryption, file_key, pdf_user_key);
PDFIO_DEBUG("_pdfioCryptoUnlock: Uuse=%02X%02X%02X%02X...%02X%02X%02X%02X\n", user_key[0], user_key[1], user_key[2], user_key[3], user_key[28], user_key[29], user_key[30], user_key[31]);
PDFIO_DEBUG("_pdfioCryptoUnlock: Updf=%02X%02X%02X%02X...%02X%02X%02X%02X\n", pdf_user_key[0], pdf_user_key[1], pdf_user_key[2], pdf_user_key[3], pdf_user_key[28], pdf_user_key[29], pdf_user_key[30], pdf_user_key[31]);
if (!memcmp(pad, pdf_user_key, 32) || !memcmp(own_user_key, pdf_user_key, 16))
{ {
// Matches! memcpy(pdf->file_key, file_key, pdf->file_keylen);
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
memcpy(pdf->password, pad, sizeof(pdf->password)); memcpy(pdf->password, pad, sizeof(pdf->password));
return (true); return (true);
@@ -883,14 +820,15 @@ _pdfioCryptoUnlock(
// //
// 'decrypt_user_key()' - Decrypt the user key. // 'decrypt_ou_key()' - Decrypt the user key.
// //
static void static void
decrypt_user_key( decrypt_ou_key(
pdfio_encryption_t encryption, // I - Type of encryption pdfio_encryption_t encryption, // I - Type of encryption
const uint8_t *file_key, // I - File encryption key const uint8_t *file_key, // I - File encryption key
uint8_t user_key[32]) // IO - User key size_t file_keylen, // I - Length of file encryption key
uint8_t ou_key[32]) // IO - Owner/User key
{ {
size_t i, j; // Looping vars size_t i, j; // Looping vars
_pdfio_rc4_t rc4; // RC4 encryption context _pdfio_rc4_t rc4; // RC4 encryption context
@@ -900,38 +838,38 @@ decrypt_user_key(
{ {
// Encrypt the result once... // Encrypt the result once...
_pdfioCryptoRC4Init(&rc4, file_key, 5); _pdfioCryptoRC4Init(&rc4, file_key, 5);
_pdfioCryptoRC4Crypt(&rc4, user_key, user_key, 32); _pdfioCryptoRC4Crypt(&rc4, ou_key, ou_key, 32);
} }
else else
{ {
// Encrypt the result 20 times... // Encrypt the result 20 times...
uint8_t key[16]; // Current encryption key uint8_t key[16]; // Current encryption key
for (i = 19; i > 0; i --) for (i = 20; i > 0;)
{ {
// XOR each byte in the key with the loop counter... // XOR each byte in the key with the loop counter...
for (j = 0; j < 16; j ++) i --;
for (j = 0; j < file_keylen; j ++)
key[j] = (uint8_t)(file_key[j] ^ i); key[j] = (uint8_t)(file_key[j] ^ i);
_pdfioCryptoRC4Init(&rc4, key, 16); _pdfioCryptoRC4Init(&rc4, key, file_keylen);
_pdfioCryptoRC4Crypt(&rc4, user_key, user_key, 32); _pdfioCryptoRC4Crypt(&rc4, ou_key, ou_key, 32);
} }
_pdfioCryptoRC4Init(&rc4, file_key, 16);
_pdfioCryptoRC4Crypt(&rc4, user_key, user_key, 32);
} }
} }
// //
// 'encrypt_user_key()' - Encrypt the user key. // 'encrypt_ou_key()' - Encrypt the owner/user key.
// //
static void static void
encrypt_user_key( encrypt_ou_key(
pdfio_encryption_t encryption, // I - Type of encryption pdfio_encryption_t encryption, // I - Type of encryption
const uint8_t *file_key, // I - File encryption key const uint8_t *file_key, // I - File encryption key
uint8_t user_key[32]) // IO - User key size_t file_keylen, // I - Length of file encryption key
uint8_t ou_key[32]) // IO - Owner/User key
{ {
size_t i, j; // Looping vars size_t i, j; // Looping vars
_pdfio_rc4_t rc4; // RC4 encryption context _pdfio_rc4_t rc4; // RC4 encryption context
@@ -941,7 +879,7 @@ encrypt_user_key(
{ {
// Encrypt the result once... // Encrypt the result once...
_pdfioCryptoRC4Init(&rc4, file_key, 5); _pdfioCryptoRC4Init(&rc4, file_key, 5);
_pdfioCryptoRC4Crypt(&rc4, user_key, user_key, 32); _pdfioCryptoRC4Crypt(&rc4, ou_key, ou_key, 32);
} }
else else
{ {
@@ -951,11 +889,11 @@ encrypt_user_key(
for (i = 0; i < 20; i ++) for (i = 0; i < 20; i ++)
{ {
// XOR each byte in the key with the loop counter... // XOR each byte in the key with the loop counter...
for (j = 0; j < 16; j ++) for (j = 0; j < file_keylen; j ++)
key[j] = (uint8_t)(file_key[j] ^ i); key[j] = (uint8_t)(file_key[j] ^ i);
_pdfioCryptoRC4Init(&rc4, key, 16); _pdfioCryptoRC4Init(&rc4, key, file_keylen);
_pdfioCryptoRC4Crypt(&rc4, user_key, user_key, 32); _pdfioCryptoRC4Crypt(&rc4, ou_key, ou_key, 32);
} }
} }
} }
@@ -968,11 +906,14 @@ encrypt_user_key(
static void static void
make_file_key( make_file_key(
pdfio_encryption_t encryption, // I - Type of encryption pdfio_encryption_t encryption, // I - Type of encryption
size_t file_keylen, // I - Encryption key length
pdfio_permission_t permissions, // I - File permissions pdfio_permission_t permissions, // I - File permissions
const unsigned char *file_id, // I - File ID value const unsigned char *file_id, // I - File ID value
size_t file_idlen, // I - Length of file ID size_t file_idlen, // I - Length of file ID
const uint8_t *user_pad, // I - Padded user password const uint8_t *user_pad, // I - Padded user password
const uint8_t *owner_key, // I - Owner key const uint8_t *owner_key, // I - Owner key
bool encrypt_metadata,
// I - Encrypt metadata?
uint8_t file_key[16]) // O - Encryption key uint8_t file_key[16]) // O - Encryption key
{ {
size_t i; // Looping var size_t i; // Looping var
@@ -986,25 +927,39 @@ make_file_key(
perm_bytes[2] = (uint8_t)(permissions >> 16); perm_bytes[2] = (uint8_t)(permissions >> 16);
perm_bytes[3] = (uint8_t)(permissions >> 24); perm_bytes[3] = (uint8_t)(permissions >> 24);
PDFIO_DEBUG("make_file_key: user_pad[32]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", user_pad[0], user_pad[1], user_pad[2], user_pad[3], user_pad[28], user_pad[29], user_pad[30], user_pad[31]);
PDFIO_DEBUG("make_file_key: owner_key[32]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", owner_key[0], owner_key[1], owner_key[2], owner_key[3], owner_key[28], owner_key[29], owner_key[30], owner_key[31]);
PDFIO_DEBUG("make_file_key: permissions(%d)=<%02X%02X%02X%02X>\n", permissions, perm_bytes[0], perm_bytes[1], perm_bytes[2], perm_bytes[3]);
_pdfioCryptoMD5Init(&md5); _pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, user_pad, 32); _pdfioCryptoMD5Append(&md5, user_pad, 32);
_pdfioCryptoMD5Append(&md5, owner_key, 32); _pdfioCryptoMD5Append(&md5, owner_key, 32);
_pdfioCryptoMD5Append(&md5, perm_bytes, 4); _pdfioCryptoMD5Append(&md5, perm_bytes, 4);
_pdfioCryptoMD5Append(&md5, file_id, file_idlen); _pdfioCryptoMD5Append(&md5, file_id, file_idlen);
if (!encrypt_metadata)
{
uint8_t meta_bytes[4] = { 0xff, 0xff, 0xff, 0xff };
// Metadata bytes
_pdfioCryptoMD5Append(&md5, meta_bytes, 4);
}
_pdfioCryptoMD5Finish(&md5, digest); _pdfioCryptoMD5Finish(&md5, digest);
PDFIO_DEBUG("make_file_key: first md5=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", digest[0], digest[1], digest[2], digest[3], digest[12], digest[13], digest[14], digest[15]);
if (encryption != PDFIO_ENCRYPTION_RC4_40) if (encryption != PDFIO_ENCRYPTION_RC4_40)
{ {
// MD5 the result 50 times.. // MD5 the result 50 times..
for (i = 0; i < 50; i ++) for (i = 0; i < 50; i ++)
{ {
_pdfioCryptoMD5Init(&md5); _pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, digest, 16); _pdfioCryptoMD5Append(&md5, digest, file_keylen);
_pdfioCryptoMD5Finish(&md5, digest); _pdfioCryptoMD5Finish(&md5, digest);
} }
} }
memcpy(file_key, digest, 16); PDFIO_DEBUG("make_file_key: file_key[16]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", digest[0], digest[1], digest[2], digest[3], digest[12], digest[13], digest[14], digest[15]);
memcpy(file_key, digest, file_keylen);
} }
@@ -1015,55 +970,57 @@ make_file_key(
static void static void
make_owner_key( make_owner_key(
pdfio_encryption_t encryption, // I - Type of encryption pdfio_encryption_t encryption, // I - Type of encryption
size_t file_keylen, // I - Length of encryption key
const uint8_t *owner_pad, // I - Padded owner password const uint8_t *owner_pad, // I - Padded owner password
const uint8_t *user_pad, // I - Padded user password const uint8_t *user_pad, // I - Padded user password
uint8_t owner_key[32]) // O - Owner key value uint8_t owner_key[32]) // O - Owner key value
{ {
size_t i, j; // Looping vars uint8_t key[16]; // Base encryption key
_pdfio_md5_t md5; // MD5 context
uint8_t digest[16]; // 128-bit MD5 digest
_pdfio_rc4_t rc4; // RC4 encryption context
// Hash the owner password... // Hash the owner password...
make_password_key(encryption, file_keylen, owner_pad, key);
// Copy and encrypt the padded user password...
memcpy(owner_key, user_pad, 32);
decrypt_ou_key(encryption, key, file_keylen, owner_key);
}
//
// 'make_password_key()' - Make a password key.
//
static void
make_password_key(
pdfio_encryption_t encryption, // I - Type of encryption
size_t file_keylen, // I - Length of encryption key
const uint8_t *pad, // I - Padded password
uint8_t *key) // O - Key data
{
size_t i; // Looping var
_pdfio_md5_t md5; // MD5 context
uint8_t digest[16]; // 128-bit MD5 digest
// Hash the padded password...
_pdfioCryptoMD5Init(&md5); _pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, owner_pad, 32); _pdfioCryptoMD5Append(&md5, pad, 32);
_pdfioCryptoMD5Finish(&md5, digest); _pdfioCryptoMD5Finish(&md5, digest);
if (encryption != PDFIO_ENCRYPTION_RC4_40) if (encryption != PDFIO_ENCRYPTION_RC4_40)
{ {
// Hash the hash another 50 times...
for (i = 0; i < 50; i ++) for (i = 0; i < 50; i ++)
{ {
_pdfioCryptoMD5Init(&md5); _pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, digest, 16); _pdfioCryptoMD5Append(&md5, digest, file_keylen);
_pdfioCryptoMD5Finish(&md5, digest); _pdfioCryptoMD5Finish(&md5, digest);
} }
} }
// Copy and encrypt the padded user password... // Copy the key portion of the hashed password to the key...
memcpy(owner_key, user_pad, 32); memcpy(key, digest, file_keylen);
if (encryption == PDFIO_ENCRYPTION_RC4_40)
{
// Encrypt once...
_pdfioCryptoRC4Init(&rc4, digest, 5);
_pdfioCryptoRC4Crypt(&rc4, owner_key, owner_key, 32);
}
else
{
// Encrypt 20 times...
uint8_t encrypt_key[16]; // RC4 encryption key
for (i = 0; i < 20; i ++)
{
// XOR each byte in the digest with the loop counter to make a key...
for (j = 0; j < sizeof(encrypt_key); j ++)
encrypt_key[j] = (uint8_t)(digest[j] ^ i);
_pdfioCryptoRC4Init(&rc4, encrypt_key, sizeof(encrypt_key));
_pdfioCryptoRC4Crypt(&rc4, owner_key, owner_key, 32);
}
}
} }
@@ -1118,3 +1075,52 @@ pad_password(const char *password, // I - Password string or `NULL`
if (len < 32) if (len < 32)
memcpy(pad + len, pdf_passpad, 32 - len); memcpy(pad + len, pdf_passpad, 32 - len);
} }
//
// 'test_password()' - Test a user password...
//
static bool // O - `true` if password is correct, `false` otherwise
test_password(
pdfio_encryption_t encryption, // I - Type of encryption
size_t file_keylen, // I - Length of encryption key
pdfio_permission_t permissions, // I - File permissions
const unsigned char *file_id, // I - File ID value
size_t file_idlen, // I - Length of file ID
const uint8_t *user_pad, // I - Padded user password
const uint8_t *user_key, // I - User key
const uint8_t *owner_key, // I - Owner key
bool encrypt_metadata,
// I - Encrypt metadata?
uint8_t file_key[16]) // O - Encryption key
{
uint8_t pdf_user_key[32]; // Decrypted user key
// Make the file key...
make_file_key(encryption, file_keylen, permissions, file_id, file_idlen, user_pad, owner_key, encrypt_metadata, file_key);
// Decrypt the user key using the file key...
memcpy(pdf_user_key, user_key, sizeof(pdf_user_key));
decrypt_ou_key(encryption, file_key, file_keylen, pdf_user_key);
PDFIO_DEBUG("test_password: pdf_user_key[32]=<%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 (encryption == PDFIO_ENCRYPTION_RC4_40 && !memcmp(pdf_user_key, pdf_passpad, 32))
{
// 40-bit encryption just compares the eecrypted user key matches...
return (true);
}
else
{
// 128-bit encryption needs to match the calculated user key...
uint8_t own_user_key[32]; // Calculated user key
// Calculate what the user key should be...
make_user_key(file_id, file_idlen, own_user_key);
PDFIO_DEBUG("test_password: own_user_key[32]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", own_user_key[0], own_user_key[1], own_user_key[2], own_user_key[3], own_user_key[28], own_user_key[29], own_user_key[30], own_user_key[31]);
// Return whether they match...
return (!memcmp(pdf_user_key, own_user_key, 16));
}
}

View File

@@ -1,7 +1,7 @@
// //
// PDF dictionary functions for PDFio. // PDF dictionary functions for PDFio.
// //
// Copyright © 2021-2025 by Michael R Sweet. // Copyright © 2021-2026 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@@ -20,6 +20,8 @@ static int compare_pairs(_pdfio_pair_t *a, _pdfio_pair_t *b);
// //
// 'pdfioDictClear()' - Remove a key/value pair from a dictionary. // 'pdfioDictClear()' - Remove a key/value pair from a dictionary.
// //
// @since PDFio v1.4@
//
bool // O - `true` if cleared, `false` otherwise bool // O - `true` if cleared, `false` otherwise
pdfioDictClear(pdfio_dict_t *dict, // I - Dictionary pdfioDictClear(pdfio_dict_t *dict, // I - Dictionary
@@ -30,7 +32,7 @@ pdfioDictClear(pdfio_dict_t *dict, // I - Dictionary
pkey; // Search key pkey; // Search key
PDFIO_DEBUG("pdfioDictClear(dict=%p, key=\"%s\")\n", dict, key); PDFIO_DEBUG("pdfioDictClear(dict=%p, key=\"%s\")\n", (void *)dict, key);
if (!dict || !key) if (!dict || !key)
return (false); return (false);
@@ -75,7 +77,11 @@ pdfioDictCopy(pdfio_file_t *pdf, // I - PDF file
_pdfio_value_t v; // Current destination value _pdfio_value_t v; // Current destination value
PDFIO_DEBUG("pdfioDictCopy(pdf=%p, dict=%p(%p))\n", pdf, dict, dict ? dict->pdf : NULL); PDFIO_DEBUG("pdfioDictCopy(pdf=%p, dict=%p(%p))\n", (void *)pdf, (void *)dict, dict ? (void *)dict->pdf : NULL);
// Range check input...
if (!pdf || !dict)
return (NULL);
// Create the new dictionary... // Create the new dictionary...
if ((ndict = pdfioDictCreate(pdf)) == NULL) if ((ndict = pdfioDictCreate(pdf)) == NULL)
@@ -90,6 +96,8 @@ pdfioDictCopy(pdfio_file_t *pdf, // I - PDF file
// Copy and add each of the source dictionary's key/value pairs... // Copy and add each of the source dictionary's key/value pairs...
for (i = dict->num_pairs, p = dict->pairs; i > 0; i --, p ++) for (i = dict->num_pairs, p = dict->pairs; i > 0; i --, p ++)
{ {
PDFIO_DEBUG("pdfioDictCopy: key=\"%s\", value.type=%d\n", p->key, p->value.type);
if (!strcmp(p->key, "Length") && p->value.type == PDFIO_VALTYPE_INDIRECT && dict->pdf != pdf) if (!strcmp(p->key, "Length") && p->value.type == PDFIO_VALTYPE_INDIRECT && dict->pdf != pdf)
{ {
// Don't use indirect stream lengths for copied objects... // Don't use indirect stream lengths for copied objects...
@@ -100,15 +108,28 @@ pdfioDictCopy(pdfio_file_t *pdf, // I - PDF file
if (lenobj) if (lenobj)
{ {
if (lenobj->value.type == PDFIO_VALTYPE_NONE) if (lenobj->value.type == PDFIO_VALTYPE_NONE)
_pdfioObjLoad(lenobj); {
if (!_pdfioObjLoad(lenobj))
{
PDFIO_DEBUG("pdfioDictCopy: Unable to copy value of \"%s\", returning NULL.\n", p->key);
return (NULL);
}
}
v.value.number = lenobj->value.value.number; v.value.number = lenobj->value.value.number;
} }
else else
{
v.value.number = 0.0; v.value.number = 0.0;
}
PDFIO_DEBUG("pdfioDictCopy: Length is %.0f.\n", v.value.number);
} }
else if (!_pdfioValueCopy(pdf, &v, dict->pdf, &p->value)) else if (!_pdfioValueCopy(pdf, &v, dict->pdf, &p->value))
{
PDFIO_DEBUG("pdfioDictCopy: Unable to copy value of \"%s\", returning NULL.\n", p->key);
return (NULL); // Let pdfioFileClose do the cleanup... return (NULL); // Let pdfioFileClose do the cleanup...
}
if (_pdfioStringIsAllocated(dict->pdf, p->key)) if (_pdfioStringIsAllocated(dict->pdf, p->key))
key = pdfioStringCreate(pdf, p->key); key = pdfioStringCreate(pdf, p->key);
@@ -345,6 +366,8 @@ pdfioDictGetDict(pdfio_dict_t *dict, // I - Dictionary
// //
// 'pdfioDictGetKey()' - Get the key for the specified pair. // 'pdfioDictGetKey()' - Get the key for the specified pair.
// //
// @since PDFio v1.4@
//
const char * // O - Key for specified pair const char * // O - Key for specified pair
pdfioDictGetKey(pdfio_dict_t *dict, // I - Dictionary pdfioDictGetKey(pdfio_dict_t *dict, // I - Dictionary
@@ -375,6 +398,8 @@ pdfioDictGetName(pdfio_dict_t *dict, // I - Dictionary
// //
// 'pdfioDictGetNumPairs()' - Get the number of key/value pairs in a dictionary. // 'pdfioDictGetNumPairs()' - Get the number of key/value pairs in a dictionary.
// //
// @since PDFio v1.4@
//
size_t // O - Number of pairs size_t // O - Number of pairs
pdfioDictGetNumPairs(pdfio_dict_t *dict)// I - Dictionary pdfioDictGetNumPairs(pdfio_dict_t *dict)// I - Dictionary
@@ -458,134 +483,25 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
_pdfio_value_t *value = _pdfioDictGetValue(dict, key); _pdfio_value_t *value = _pdfioDictGetValue(dict, key);
PDFIO_DEBUG("pdfioDictGetString(dict=%p, key=\"%s\")\n", (void *)dict, key);
PDFIO_DEBUG("pdfioDictGetString: value=%p(type=%d)\n", (void *)value, value ? value->type : 0);
if (value && value->type == PDFIO_VALTYPE_STRING) if (value && value->type == PDFIO_VALTYPE_STRING)
{ {
PDFIO_DEBUG("pdfioDictGetString: Returning \"%s\".\n", value->value.string);
return (value->value.string); return (value->value.string);
} }
else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096) else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096)
{ {
// Convert binary string to regular string... // Convert binary string to regular string...
char temp[4096], // Temporary string char temp[4096]; // Temporary UTF-8 string
*tempptr; // Pointer into temporary string
unsigned char *dataptr; // Pointer into the data string
if (!(value->value.binary.datalen & 1) && !memcmp(value->value.binary.data, "\376\377", 2)) if (!(value->value.binary.datalen & 1) && (!memcmp(value->value.binary.data, "\376\377", 2) || !memcmp(value->value.binary.data, "\377\376", 2)))
{ {
// Copy UTF-16 BE // Copy UTF-16...
int ch; // Unicode character PDFIO_DEBUG("pdfioDictGetString: Converting UTF-16 to UTF-8 string.\n");
size_t remaining; // Remaining bytes
for (dataptr = value->value.binary.data + 2, remaining = value->value.binary.datalen - 2, tempptr = temp; remaining > 1 && tempptr < (temp + sizeof(temp) - 5); dataptr += 2, remaining -= 2) _pdfio_utf16cpy(temp, value->value.binary.data, value->value.binary.datalen, sizeof(temp));
{
ch = (dataptr[0] << 8) | dataptr[1];
if (ch >= 0xd800 && ch <= 0xdbff && remaining > 3)
{
// Multi-word UTF-16 char...
int lch; // Lower bits
lch = (dataptr[2] << 8) | dataptr[3];
if (lch < 0xdc00 || lch >= 0xdfff)
break;
ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
dataptr += 2;
remaining -= 2;
}
else if (ch >= 0xfffe)
{
continue;
}
if (ch < 128)
{
// ASCII
*tempptr++ = (char)ch;
}
else if (ch < 4096)
{
// 2-byte UTF-8
*tempptr++ = (char)(0xc0 | (ch >> 6));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else if (ch < 65536)
{
// 3-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 12));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else
{
// 4-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 18));
*tempptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
}
*tempptr = '\0';
}
else if (!(value->value.binary.datalen & 1) && !memcmp(value->value.binary.data, "\377\376", 2))
{
// Copy UTF-16 LE
int ch; // Unicode character
size_t remaining; // Remaining bytes
for (dataptr = value->value.binary.data + 2, remaining = value->value.binary.datalen - 2, tempptr = temp; remaining > 1 && tempptr < (temp + sizeof(temp) - 5); dataptr += 2, remaining -= 2)
{
ch = (dataptr[1] << 8) | dataptr[0];
if (ch >= 0xd800 && ch <= 0xdbff && remaining > 3)
{
// Multi-word UTF-16 char...
int lch; // Lower bits
lch = (dataptr[3] << 8) | dataptr[2];
if (lch < 0xdc00 || lch >= 0xdfff)
break;
ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
dataptr += 2;
remaining -= 2;
}
else if (ch >= 0xfffe)
{
continue;
}
if (ch < 128)
{
// ASCII
*tempptr++ = (char)ch;
}
else if (ch < 4096)
{
// 2-byte UTF-8
*tempptr++ = (char)(0xc0 | (ch >> 6));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else if (ch < 65536)
{
// 3-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 12));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else
{
// 4-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 18));
*tempptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
}
*tempptr = '\0';
} }
else else
{ {
@@ -598,10 +514,13 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
value->type = PDFIO_VALTYPE_STRING; value->type = PDFIO_VALTYPE_STRING;
value->value.string = pdfioStringCreate(dict->pdf, temp); value->value.string = pdfioStringCreate(dict->pdf, temp);
PDFIO_DEBUG("pdfioDictGetString: Returning \"%s\".\n", value->value.string);
return (value->value.string); return (value->value.string);
} }
else else
{ {
PDFIO_DEBUG("pdfioDictGetString: Returning NULL.\n");
return (NULL); return (NULL);
} }
} }
@@ -634,7 +553,7 @@ _pdfioDictGetValue(pdfio_dict_t *dict, // I - Dictionary
*match; // Matching key pair *match; // Matching key pair
PDFIO_DEBUG("_pdfioDictGetValue(dict=%p, key=\"%s\")\n", dict, key); PDFIO_DEBUG("_pdfioDictGetValue(dict=%p, key=\"%s\")\n", (void *)dict, key);
if (!dict || !dict->num_pairs || !key) if (!dict || !dict->num_pairs || !key)
{ {
@@ -677,6 +596,8 @@ _pdfioDictGetValue(pdfio_dict_t *dict, // I - Dictionary
// The iteration continues as long as the callback returns `true` or all keys // The iteration continues as long as the callback returns `true` or all keys
// have been iterated. // have been iterated.
// //
// @since PDFio v1.1@
//
void void
pdfioDictIterateKeys( pdfioDictIterateKeys(
@@ -717,7 +638,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, obj=%p, tb=%p, depth=%lu)\n", pdf, obj, tb, (unsigned long)depth); PDFIO_DEBUG("_pdfioDictRead(pdf=%p, obj=%p, tb=%p, depth=%lu)\n", (void *)pdf, (void *)obj, (void *)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)
@@ -748,11 +669,12 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
} }
else if (_pdfioDictGetValue(dict, key + 1)) else if (_pdfioDictGetValue(dict, key + 1))
{ {
// Issue 118: Discard duplicate key/value pairs, in the future this will // Issue 118: Discard duplicate key/value pairs...
// be a warning message...
_pdfioFileError(pdf, "WARNING: Discarding value for duplicate dictionary key '%s'.", key + 1);
_pdfioValueDelete(&value); _pdfioValueDelete(&value);
continue; if (_pdfioFileError(pdf, "WARNING: Discarding value for duplicate dictionary key '%s'.", key + 1))
continue;
else
break;
} }
else if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value)) else if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value))
break; break;
@@ -1089,7 +1011,7 @@ _pdfioDictSetValue(
_pdfio_pair_t *pair; // Current pair _pdfio_pair_t *pair; // Current pair
PDFIO_DEBUG("_pdfioDictSetValue(dict=%p, key=\"%s\", value=%p)\n", dict, key, (void *)value); PDFIO_DEBUG("_pdfioDictSetValue(dict=%p, key=\"%s\", value=%p)\n", (void *)dict, key, (void *)value);
// See if the key is already set... // See if the key is already set...
if (dict->num_pairs > 0) if (dict->num_pairs > 0)
@@ -1151,11 +1073,13 @@ _pdfioDictSetValue(
// //
bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
_pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary _pdfioDictWrite(
pdfio_obj_t *obj, // I - Object, if any _pdfio_printf_t cb, // I - Printf callback function
off_t *length) // I - Offset to length value void *cbdata, // I - Printf callback data
pdfio_obj_t *obj, // I - Object, if any
pdfio_dict_t *dict, // I - Dictionary
off_t *length) // I - Offset to length value
{ {
pdfio_file_t *pdf = dict->pdf; // PDF file
size_t i; // Looping var size_t i; // Looping var
_pdfio_pair_t *pair; // Current key/value pair _pdfio_pair_t *pair; // Current key/value pair
@@ -1164,28 +1088,30 @@ _pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary
*length = 0; *length = 0;
// Dictionaries are bounded by "<<" and ">>"... // Dictionaries are bounded by "<<" and ">>"...
if (!_pdfioFilePuts(pdf, "<<")) if (!(cb)(cbdata, "<<"))
return (false); return (false);
// Write all of the key/value pairs... // Write all of the key/value pairs...
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++) for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{ {
if (!_pdfioFilePrintf(pdf, "%N", pair->key)) if (!(cb)(cbdata, "%N", pair->key))
return (false); return (false);
if (length && !strcmp(pair->key, "Length") && pair->value.type == PDFIO_VALTYPE_NUMBER && pair->value.value.number <= 0.0) if (length && !strcmp(pair->key, "Length") && pair->value.type == PDFIO_VALTYPE_NUMBER && pair->value.value.number <= 0.0)
{ {
// Writing an object dictionary with an undefined length // Writing an object dictionary with an undefined length
*length = _pdfioFileTell(pdf) + 1; *length = _pdfioFileTell(dict->pdf) + 1;
if (!_pdfioFilePuts(pdf, " 9999999999")) if (!(cb)(cbdata, " 9999999999"))
return (false); return (false);
} }
else if (!_pdfioValueWrite(pdf, obj, &pair->value, NULL)) else if (!_pdfioValueWrite(cb, cbdata, obj, &pair->value, NULL))
{
return (false); return (false);
}
} }
// Close it up... // Close it up...
return (_pdfioFilePuts(pdf, ">>")); return ((cb)(cbdata, ">>"));
} }

File diff suppressed because it is too large Load Diff

338
pdfio-lzw.c Normal file
View File

@@ -0,0 +1,338 @@
//
// LZW decoding functions for PDFio.
//
// This code is used to support (legacy) PDF object streams using the LZWDecode
// filter as well as when embedding (legacy) GIF images. None of this is public
// API and we only support reading (decoding) since FlateDecode is superior in
// every way.
//
// Copyright © 2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
#include "pdfio-private.h"
//
// Local functions...
//
static void lzw_clear(_pdfio_lzw_t *lzw);
static int lzw_get_code(_pdfio_lzw_t *lzw);
//
// '_pdfioLZWCreate()' - Create a LZW decompressor.
//
_pdfio_lzw_t * // O - LZW state
_pdfioLZWCreate(int code_size, // I - Data code size in bits (typically 8 for PDF, 2-8 for GIF)
int early, // I - Number of early codes
bool reversed) // I - Reversed (GIF) LZW bit encoding?
{
_pdfio_lzw_t *lzw; // LZW state
if ((lzw = (_pdfio_lzw_t *)calloc(1, sizeof(_pdfio_lzw_t))) != NULL)
{
lzw->def_code_size = (uint8_t)(code_size + 1);
lzw->clear_code = (uint16_t)(1 << code_size);
lzw->eod_code = lzw->clear_code + 1;
lzw->early = (uint8_t)early;
lzw->reversed = reversed;
lzw_clear(lzw);
}
return (lzw);
}
//
// '_pdfioLZWDelete()' - Delete a LZW decompressor.
//
void
_pdfioLZWDelete(_pdfio_lzw_t *lzw) // I - LZW state
{
free(lzw);
}
//
// '_pdfioLZWInflate()' - Decompress pending input data.
//
bool // O - `true` on success, `false` on error
_pdfioLZWInflate(_pdfio_lzw_t *lzw) // I - LZW state
{
int cur_code, // Current code
in_code; // Input code
// Stop if we already saw the "end of data" code...
if (lzw->saw_eod)
{
PDFIO_DEBUG("_pdfioLZWInflate: EOD, returning false.\n");
lzw->error = "End of data.";
return (false);
}
// Copy pending compressed data to the output buffer...
while (lzw->stptr > lzw->stack && lzw->avail_out > 0)
{
*(lzw->next_out++) = (uint8_t)*(--lzw->stptr);
lzw->avail_out --;
PDFIO_DEBUG("_pdfioLZWInflate: Unrolled value %d, stptr=%p(%ld), avail_out=%u\n", *(lzw->stptr), (void *)lzw->stptr, lzw->stptr - lzw->stack, (unsigned)lzw->avail_out);
}
// Loop as long as we have room in the output buffer and data in the input
// buffer...
while (lzw->avail_out > 0)
{
if ((in_code = lzw_get_code(lzw)) < 0)
{
// Out of data, stop now...
PDFIO_DEBUG("_pdfioLZWInflate: Out of data.\n");
break;
}
else if (in_code == lzw->clear_code)
{
// Clear the compression tables and reset...
lzw_clear(lzw);
PDFIO_DEBUG("_pdfioLZWInflate: Clear.\n");
continue;
}
else if (in_code == lzw->eod_code)
{
// End of data...
lzw->saw_eod = true;
PDFIO_DEBUG("_pdfioLZWInflate: EOD.\n");
break;
}
// If we get this far we have something to write to the output buffer and/or
// stack...
if (lzw->first_code == 0xffff)
{
// First code...
lzw->first_code = lzw->old_code = (uint16_t)in_code;
*(lzw->next_out++) = (uint8_t)in_code;
lzw->avail_out --;
PDFIO_DEBUG("_pdfioLZWInflate: first_code=%d.\n", in_code);
continue;
}
PDFIO_DEBUG("_pdfioLZWInflate: in_code=%d, old_code=%d.\n", in_code, lzw->old_code);
cur_code = in_code;
if (cur_code >= lzw->next_code)
{
PDFIO_DEBUG("_pdfioLZWInflate: New cur_code=%d, next_code=%d\n", cur_code, lzw->next_code);
*(lzw->stptr++) = lzw->first_code;
cur_code = lzw->old_code;
}
while (cur_code >= lzw->clear_code)
{
PDFIO_DEBUG("_pdfioLZWInflate: cur_code=%d (%d,%d)\n", cur_code, lzw->table[cur_code].prefix_code, lzw->table[cur_code].suffix);
// Protect against overflow/loops...
if (lzw->stptr >= (lzw->stack + sizeof(lzw->stack) / sizeof(lzw->stack[0])))
{
PDFIO_DEBUG("_pdfioLZWInflate: Stack overflow, returning false.\n");
lzw->error = "Output overflow.";
return (false);
}
// Add this character to the output stack and move to the next character
// in the sequence...
*(lzw->stptr++) = lzw->table[cur_code].suffix;
if (cur_code == lzw->table[cur_code].prefix_code)
{
PDFIO_DEBUG("_pdfioLZWInflate: Table loop on code %d, returning false.\n", cur_code);
lzw->error = "Table loop detected.";
return (false);
}
cur_code = lzw->table[cur_code].prefix_code;
}
if (lzw->stptr >= (lzw->stack + sizeof(lzw->stack) / sizeof(lzw->stack[0])))
{
PDFIO_DEBUG("_pdfioLZWInflate: Stack overflow, returning false.\n");
lzw->error = "Output overflow.";
return (false);
}
*(lzw->stptr++) = lzw->first_code = lzw->table[cur_code].suffix;
if ((cur_code = lzw->next_code) < 4096)
{
PDFIO_DEBUG("_pdfioLZWInflate: Adding code %d (%d,%d), next_size_code=%d\n", cur_code, lzw->old_code, lzw->first_code, lzw->next_size_code);
lzw->table[cur_code].prefix_code = lzw->old_code;
lzw->table[cur_code].suffix = lzw->first_code;
lzw->next_code ++;
if (lzw->next_code >= lzw->next_size_code && lzw->cur_code_size < 12)
{
lzw->cur_code_size ++;
lzw->next_size_code = (uint16_t)((1 << lzw->cur_code_size) - lzw->early);
PDFIO_DEBUG("_pdfioLZWInflate: Increased code size to %u, next_size_code=%u\n", lzw->cur_code_size, lzw->next_size_code);
}
}
lzw->old_code = (uint16_t)in_code;
while (lzw->stptr > lzw->stack && lzw->avail_out > 0)
{
*(lzw->next_out++) = (uint8_t)*(--lzw->stptr);
lzw->avail_out --;
PDFIO_DEBUG("_pdfioLZWInflate: Unrolled value %d, stptr=%p(%ld), avail_out=%u\n", *(lzw->stptr), (void *)lzw->stptr, lzw->stptr - lzw->stack, (unsigned)lzw->avail_out);
}
}
PDFIO_DEBUG("_pdfioLZWInflate: Returning true, avail_in=%u, avail_out=%u.\n", (unsigned)lzw->avail_in, (unsigned)lzw->avail_out);
return (true);
}
//
// 'lzw_clear()' - Clear the compression table.
//
static void
lzw_clear(_pdfio_lzw_t *lzw) // I - LZW state
{
uint16_t i; // Looping var
lzw->cur_code_size = lzw->def_code_size;
lzw->next_code = lzw->clear_code + 2;
lzw->next_size_code = (uint16_t)((1 << lzw->cur_code_size) - lzw->early);
lzw->first_code = 0xffff;
lzw->old_code = 0xffff;
memset(lzw->table, 0, sizeof(lzw->table));
for (i = 0; i < lzw->clear_code; i ++)
lzw->table[i].suffix = i;
lzw->stptr = lzw->stack;
}
//
// 'lzw_get_code()' - Get a code from the input buffer.
//
static int // O - Code or -1 if there is not enough data available
lzw_get_code(_pdfio_lzw_t *lzw) // I - LZW state
{
uint16_t code, // Code
in_bit; // Bit offset in buffer
uint8_t bits, // Bits in current byte
boff, // Bit offset in current byte
byte, // Current byte
remaining; // Remaining bits for code
static uint8_t mask[8] = // Value mask
{
0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f
};
static uint8_t rbits[8] = // Right-to-left bit masks
{
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};
// Fill input bytes as needed...
if ((lzw->in_bit + lzw->cur_code_size) > lzw->in_bits)
{
uint16_t in_used = lzw->in_bits / 8,
// Number of input bytes
in_offset = lzw->in_bit / 8,
// Offset to current input
in_add; // Number of bytes to "read"
if (lzw->avail_in == 0)
{
// No more data
PDFIO_DEBUG("lzw_get_code: No data, returning -1.\n");
return (-1);
}
if (in_offset > 0)
{
// Make room in the input buffer
memmove(lzw->in_bytes, lzw->in_bytes + in_offset, in_used - in_offset);
in_used -= in_offset;
lzw->in_bit &= 7;
}
if ((in_add = sizeof(lzw->in_bytes) - in_used) > lzw->avail_in)
in_add = (uint16_t)lzw->avail_in;
memcpy(lzw->in_bytes + in_used, lzw->next_in, in_add);
lzw->next_in += in_add;
lzw->avail_in -= in_add;
lzw->in_bits = 8 * (in_used + in_add);
if ((lzw->in_bit + lzw->cur_code_size) > lzw->in_bits)
{
// Not enough data
PDFIO_DEBUG("lzw_get_code: Not enough data, returning -1.\n");
return (-1);
}
}
PDFIO_DEBUG("lzw_get_code: in_bit=%u, in_bits=%u, in_bytes=<...%02X%02X%02X...>, cur_code_size=%u\n", lzw->in_bit, lzw->in_bits, lzw->in_bytes[lzw->in_bit / 8], lzw->in_bytes[lzw->in_bit / 8 + 1], lzw->in_bytes[lzw->in_bit / 8 + 2], lzw->cur_code_size);
// Now extract the code from the buffer...
if (lzw->reversed)
{
// Insane GIF-style right-to-left bit encoding...
for (code = 0, in_bit = lzw->in_bit + lzw->cur_code_size - 1, remaining = lzw->cur_code_size; remaining > 0; in_bit --, remaining --)
{
code = (uint16_t)((code << 1) | ((lzw->in_bytes[in_bit / 8] & rbits[in_bit & 7]) != 0));
}
}
else
{
// TIFF/PDF-style left-to-right bit encoding...
for (code = 0, in_bit = lzw->in_bit, remaining = lzw->cur_code_size; remaining > 0; in_bit += bits, remaining -= bits)
{
// See how many bits we can extract from the current byte...
boff = (in_bit & 7);
byte = lzw->in_bytes[in_bit / 8];
bits = 8 - boff;
if (bits > remaining)
bits = remaining;
// Get those bits
if (bits == 8) // Full byte from buffer
code = (uint16_t)((code << 8) | byte);
else // Partial byte from buffer
code = (uint16_t)((code << bits) | ((byte >> (8 - bits - boff)) & mask[bits]));
}
}
// Update the position in the input buffer and return the code...
lzw->in_bit += lzw->cur_code_size;
#ifdef DEBUG
if (code >= 0x20 && code < 0x7f)
PDFIO_DEBUG("lzw_get_code: Returning %u('%c').\n", code, code);
else
PDFIO_DEBUG("lzw_get_code: Returning %u.\n", code);
#endif // DEBUG
return ((int)code);
}

View File

@@ -1,7 +1,7 @@
// //
// PDF object functions for PDFio. // PDF object functions for PDFio.
// //
// Copyright © 2021-2025 by Michael R Sweet. // Copyright © 2021-2026 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.
@@ -32,8 +32,43 @@ pdfioObjClose(pdfio_obj_t *obj) // I - Object
} }
// Write what remains for the object... // Write what remains for the object...
if (!obj->offset) if (!obj->offset && !obj->objstm_data)
{ {
// Are we using object streams and is this object eligible?
if (obj->pdf->objstm_dict && !obj->pdf->objstm_obj && obj != obj->pdf->encrypt_obj && obj->pdf->num_objstm < 0x10000 && (obj->value.type == PDFIO_VALTYPE_ARRAY || obj->value.type == PDFIO_VALTYPE_DICT))
{
// Add this to the object stream...
_pdfio_strbuf_t *bptr; // String buffer
if (!_pdfioStringAllocBuffer(obj->pdf, &bptr))
goto regular_obj;
if (!_pdfioValueWrite((_pdfio_printf_t)_pdfioStringPrintf, bptr, /*obj*/NULL, &(obj->value), /*length*/NULL))
{
_pdfioStringFreeBuffer(obj->pdf, bptr->buffer);
goto regular_obj;
}
_pdfioStringPrintf(bptr, "\n");
if ((obj->objstm_data = pdfioStringCreate(obj->pdf, bptr->buffer)) == NULL)
{
_pdfioStringFreeBuffer(obj->pdf, bptr->buffer);
goto regular_obj;
}
// If we got this far then we have the value string and can assign an
// object number, which is all that is required for now...
obj->objstm_datalen = (size_t)(bptr->bufptr - bptr->buffer);
obj->objstm_number = obj->pdf->num_objstm;
obj->pdf->num_objstm ++;
_pdfioStringFreeBuffer(obj->pdf, bptr->buffer);
return (true);
}
regular_obj:
// Write the object value // Write the object value
if (!_pdfioObjWriteHeader(obj)) if (!_pdfioObjWriteHeader(obj))
return (false); return (false);
@@ -69,7 +104,7 @@ pdfioObjCopy(pdfio_file_t *pdf, // I - PDF file
ssize_t bytes; // Bytes read ssize_t bytes; // Bytes read
PDFIO_DEBUG("pdfioObjCopy(pdf=%p, srcobj=%p(%p))\n", pdf, srcobj, srcobj ? srcobj->pdf : NULL); PDFIO_DEBUG("pdfioObjCopy(pdf=%p, srcobj=%p(%u,%p))\n", (void *)pdf, (void *)srcobj, srcobj ? (unsigned)srcobj->number : 0, srcobj ? (void *)srcobj->pdf : NULL);
// Range check input // Range check input
if (!pdf || !srcobj) if (!pdf || !srcobj)
@@ -77,7 +112,14 @@ pdfioObjCopy(pdfio_file_t *pdf, // I - PDF file
// Load the object value if needed... // Load the object value if needed...
if (srcobj->value.type == PDFIO_VALTYPE_NONE) if (srcobj->value.type == PDFIO_VALTYPE_NONE)
_pdfioObjLoad(srcobj); {
if (!_pdfioObjLoad(srcobj))
return (NULL);
}
// See if we have already mapped this object...
if ((dstobj = _pdfioFileFindMappedObj(pdf, srcobj->pdf, srcobj->number)) != NULL)
return (dstobj); // Yes, return that one...
// Create the new object... // Create the new object...
if ((dstobj = _pdfioFileCreateObj(pdf, srcobj->pdf, NULL)) == NULL) if ((dstobj = _pdfioFileCreateObj(pdf, srcobj->pdf, NULL)) == NULL)
@@ -141,6 +183,7 @@ pdfioObjCreateStream(
pdfio_obj_t *obj, // I - Object pdfio_obj_t *obj, // I - Object
pdfio_filter_t filter) // I - Type of compression to apply pdfio_filter_t filter) // I - Type of compression to apply
{ {
pdfio_stream_t *st; // Stream
pdfio_obj_t *length_obj = NULL; // Length object, if any pdfio_obj_t *length_obj = NULL; // Length object, if any
@@ -194,11 +237,13 @@ 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, 0, filter)); if ((st = _pdfioStreamCreate(obj, length_obj, 0, filter)) != NULL)
obj->pdf->current_obj = obj;
return (st);
} }
@@ -301,7 +346,7 @@ pdfioObjGetLength(pdfio_obj_t *obj) // I - Object
// Try getting the length, directly or indirectly // Try getting the length, directly or indirectly
if ((length = (size_t)pdfioDictGetNumber(obj->value.value.dict, "Length")) > 0) if ((length = (size_t)pdfioDictGetNumber(obj->value.value.dict, "Length")) > 0)
{ {
PDFIO_DEBUG("pdfioObjGetLength(obj=%p) returning %lu.\n", obj, (unsigned long)length); PDFIO_DEBUG("pdfioObjGetLength(obj=%p) returning %lu.\n", (void *)obj, (unsigned long)length);
return (length); return (length);
} }
@@ -321,7 +366,7 @@ pdfioObjGetLength(pdfio_obj_t *obj) // I - Object
return (0); return (0);
} }
PDFIO_DEBUG("pdfioObjGetLength(obj=%p) returning %lu.\n", obj, (unsigned long)lenobj->value.value.number); PDFIO_DEBUG("pdfioObjGetLength(obj=%p) returning %lu.\n", (void *)obj, (unsigned long)lenobj->value.value.number);
return ((size_t)lenobj->value.value.number); return ((size_t)lenobj->value.value.number);
} }
@@ -330,6 +375,8 @@ pdfioObjGetLength(pdfio_obj_t *obj) // I - Object
// //
// 'pdfioObjGetName()' - Get the name value associated with an object. // 'pdfioObjGetName()' - Get the name value associated with an object.
// //
// @since PDFio v1.4@
//
const char * // O - Dictionary or `NULL` on error const char * // O - Dictionary or `NULL` on error
pdfioObjGetName(pdfio_obj_t *obj) // I - Object pdfioObjGetName(pdfio_obj_t *obj) // I - Object
@@ -431,7 +478,7 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
_pdfio_token_t tb; // Token buffer/stack _pdfio_token_t tb; // Token buffer/stack
PDFIO_DEBUG("_pdfioObjLoad(obj=%p(%lu)), offset=%lu\n", obj, (unsigned long)obj->number, (unsigned long)obj->offset); PDFIO_DEBUG("_pdfioObjLoad(obj=%p(%lu)), offset=%lu\n", (void *)obj, (unsigned long)obj->number, (unsigned long)obj->offset);
// Seek to the start of the object and read its header... // Seek to the start of the object and read its header...
if (_pdfioFileSeek(obj->pdf, obj->offset, SEEK_SET) != obj->offset) if (_pdfioFileSeek(obj->pdf, obj->offset, SEEK_SET) != obj->offset)
@@ -493,7 +540,7 @@ _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]); PDFIO_DEBUG("_pdfioObjLoad: tb.bufptr=%p, tb.bufend=%p, tb.bufptr[0]=0x%02x, tb.bufptr[1]=0x%02x\n", (void *)tb.bufptr, (void *)tb.bufend, tb.bufptr[0], tb.bufptr[1]);
_pdfioTokenFlush(&tb); _pdfioTokenFlush(&tb);
@@ -505,7 +552,7 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
} }
// Decrypt as needed... // Decrypt as needed...
if (obj->pdf->encryption) if (obj->pdf->encryption && obj->pdf->encrypt_metadata)
{ {
PDFIO_DEBUG("_pdfioObjLoad: Decrypting value...\n"); PDFIO_DEBUG("_pdfioObjLoad: Decrypting value...\n");
@@ -532,6 +579,11 @@ pdfio_stream_t * // O - Stream or `NULL` on error
pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
bool decode) // I - Decode/decompress data? bool decode) // I - Decode/decompress data?
{ {
pdfio_stream_t *st; // Stream
PDFIO_DEBUG("pdfioObjOpenStream(obj=%p(%lu), decode=%s)\n", (void *)obj, obj ? (unsigned long)obj->number : 0, decode ? "true" : "false");
// Range check input... // Range check input...
if (!obj) if (!obj)
return (NULL); return (NULL);
@@ -551,12 +603,16 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
// No stream if there is no dict or offset to a stream... // No stream if there is no dict or offset to a stream...
if (obj->value.type != PDFIO_VALTYPE_DICT || !obj->stream_offset) if (obj->value.type != PDFIO_VALTYPE_DICT || !obj->stream_offset)
{
PDFIO_DEBUG("pdfioObjOpenStream: value.type=%d, stream_offset=%ld\n", obj->value.type, (long)obj->stream_offset);
return (NULL); return (NULL);
}
// Open the stream... // Open the stream...
obj->pdf->current_obj = obj; if ((st = _pdfioStreamOpen(obj, decode)) != NULL)
obj->pdf->current_obj = obj;
return (_pdfioStreamOpen(obj, decode)); return (st);
} }
@@ -587,7 +643,7 @@ _pdfioObjWriteHeader(pdfio_obj_t *obj) // I - Object
if (!_pdfioFilePrintf(obj->pdf, "%lu %u obj\n", (unsigned long)obj->number, obj->generation)) if (!_pdfioFilePrintf(obj->pdf, "%lu %u obj\n", (unsigned long)obj->number, obj->generation))
return (false); return (false);
if (!_pdfioValueWrite(obj->pdf, obj, &obj->value, &obj->length_offset)) if (!_pdfioValueWrite((_pdfio_printf_t)_pdfioFilePrintf, obj->pdf, obj, &obj->value, &obj->length_offset))
return (false); return (false);
return (_pdfioFilePuts(obj->pdf, "\n")); return (_pdfioFilePuts(obj->pdf, "\n"));

View File

@@ -1,7 +1,7 @@
// //
// PDF page functions for PDFio. // PDF page functions for PDFio.
// //
// Copyright © 2021-2022 by Michael R Sweet. // Copyright © 2021-2026 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.
@@ -15,6 +15,7 @@
// //
static _pdfio_value_t *get_contents(pdfio_obj_t *page); static _pdfio_value_t *get_contents(pdfio_obj_t *page);
static _pdfio_value_t *get_page_value(pdfio_obj_t *page, const char *key);
// //
@@ -28,7 +29,7 @@ pdfioPageCopy(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *dstpage; // Destination page object pdfio_obj_t *dstpage; // Destination page object
PDFIO_DEBUG("pdfioPageCopy(pdf=%p, srcpage=%p(%p))\n", pdf, srcpage, srcpage ? srcpage->pdf : NULL); PDFIO_DEBUG("pdfioPageCopy(pdf=%p, srcpage=%p(%p))\n", (void *)pdf, (void *)srcpage, srcpage ? (void *)srcpage->pdf : NULL);
// Range check input // Range check input
if (!pdf || !srcpage || srcpage->value.type != PDFIO_VALTYPE_DICT) if (!pdf || !srcpage || srcpage->value.type != PDFIO_VALTYPE_DICT)
@@ -52,6 +53,158 @@ pdfioPageCopy(pdfio_file_t *pdf, // I - PDF file
} }
//
// 'pdfioPageGetArray()' - Get an array value from the page dictionary.
//
// This function looks up an array value in the page dictionary, either in the
// specified object or one of its parents.
//
// @since PDFio v1.7@
//
pdfio_array_t * // O - Array or `NULL` if none
pdfioPageGetArray(pdfio_obj_t *page, // I - Page object
const char *key) // I - Dictionary key
{
_pdfio_value_t *v = get_page_value(page, key);
// Dictionary value
if (v && v->type == PDFIO_VALTYPE_ARRAY)
return (v->value.array);
else
return (NULL);
}
//
// 'pdfioPageGetBinary()' - Get a binary value from the page dictionary.
//
// This function looks up a binary value in the page dictionary, either in the
// specified object or one of its parents.
//
// @since PDFio v1.7@
//
unsigned char * // O - Pointer to binary data or `NULL` if none
pdfioPageGetBinary(pdfio_obj_t *page, // I - Page object
const char *key, // I - Dictionary key
size_t *length) // O - Length of value
{
_pdfio_value_t *v = get_page_value(page, key);
// Dictionary value
if (v && v->type == PDFIO_VALTYPE_BINARY)
{
if (length)
*length = v->value.binary.datalen;
return (v->value.binary.data);
}
else
{
return (NULL);
}
}
//
// 'pdfioPageGetBoolean()' - Get a boolean value from the page dictionary.
//
// This function looks up a boolean value in the page dictionary, either in the
// specified object or one of its parents.
//
// @since PDFio v1.7@
//
bool // O - Boolean value or `false` if none
pdfioPageGetBoolean(pdfio_obj_t *page, // I - Page object
const char *key) // I - Dictionary key
{
_pdfio_value_t *v = get_page_value(page, key);
// Dictionary value
if (v && v->type == PDFIO_VALTYPE_ARRAY)
return (v->value.boolean);
else
return (NULL);
}
//
// 'pdfioPageGetDate()' - Get a date value from the page dictionary.
//
// This function looks up a date value in the page dictionary, either in the
// specified object or one of its parents.
//
// @since PDFio v1.7@
//
time_t // O - Date/time or `0` if none
pdfioPageGetDate(pdfio_obj_t *page, // I - Page object
const char *key) // I - Dictionary key
{
_pdfio_value_t *v = get_page_value(page, key);
// Dictionary value
if (v && v->type == PDFIO_VALTYPE_DATE)
return (v->value.date);
else
return (0);
}
//
// 'pdfioPageGetDict()' - Get a dictionary value from the page dictionary.
//
// This function looks up a dictionary value in the page dictionary, either in
// the specified object or one of its parents.
//
// @since PDFio v1.7@
//
pdfio_dict_t * // O - Dictionary or `NULL` if none
pdfioPageGetDict(pdfio_obj_t *page, // I - Page object
const char *key) // I - Dictionary key
{
_pdfio_value_t *v = get_page_value(page, key);
// Dictionary value
if (v && v->type == PDFIO_VALTYPE_DICT)
return (v->value.dict);
else
return (NULL);
}
//
// 'pdfioPageGetName()' - Get a name value from the page dictionary.
//
// This function looks up a name value in the page dictionary, either in the
// specified object or one of its parents.
//
// @since PDFio v1.7@
//
const char * // O - Name string or `NULL` if none
pdfioPageGetName(pdfio_obj_t *page, // I - Page object
const char *key) // I - Dictionary key
{
_pdfio_value_t *v = get_page_value(page, key);
// Dictionary value
if (v && v->type == PDFIO_VALTYPE_NAME)
return (v->value.name);
else
return (NULL);
}
// //
// 'pdfioPageGetNumStreams()' - Get the number of content streams for a page object. // 'pdfioPageGetNumStreams()' - Get the number of content streams for a page object.
// //
@@ -73,6 +226,112 @@ pdfioPageGetNumStreams(
} }
//
// 'pdfioPageGetNumber()' - Get a number value from the page dictionary.
//
// This function looks up a number value in the page dictionary, either in the
// specified object or one of its parents.
//
// @since PDFio v1.7@
//
double // O - Number value or `0.0` if none
pdfioPageGetNumber(pdfio_obj_t *page, // I - Page object
const char *key) // I - Dictionary key
{
_pdfio_value_t *v = get_page_value(page, key);
// Dictionary value
if (v && v->type == PDFIO_VALTYPE_NUMBER)
return (v->value.number);
else
return (0.0);
}
//
// 'pdfioPageGetObj()' - Get an indirect object value from the page dictionary.
//
// This function looks up an indirect object value in the page dictionary,
// either in the specified object or one of its parents.
//
// @since PDFio v1.7@
//
pdfio_obj_t * // O - Object or `NULL` if none
pdfioPageGetObj(pdfio_obj_t *page, // I - Page object
const char *key) // I - Dictionary key
{
_pdfio_value_t *v = get_page_value(page, key);
// Dictionary value
if (v && v->type == PDFIO_VALTYPE_INDIRECT)
return (pdfioFileFindObj(page->pdf, v->value.indirect.number));
else
return (NULL);
}
//
// 'pdfioPageGetRect()' - Get a rectangle value from the page dictionary.
//
// This function looks up a rectangle value in the page dictionary, either in
// the specified object or one of its parents.
//
// @since PDFio v1.7@
//
pdfio_rect_t * // O - Rectangle or `NULL` if none
pdfioPageGetRect(pdfio_obj_t *page, // I - Page object
const char *key, // I - Dictionary key
pdfio_rect_t *rect) // O - Rectangle
{
_pdfio_value_t *v = get_page_value(page, key);
// Dictionary value
if (v && v->type == PDFIO_VALTYPE_ARRAY && pdfioArrayGetSize(v->value.array) == 4)
{
rect->x1 = pdfioArrayGetNumber(v->value.array, 0);
rect->y1 = pdfioArrayGetNumber(v->value.array, 1);
rect->x2 = pdfioArrayGetNumber(v->value.array, 2);
rect->y2 = pdfioArrayGetNumber(v->value.array, 3);
return (rect);
}
else
{
return (NULL);
}
}
//
// 'pdfioPageGetString()' - Get a string value from the page dictionary.
//
// This function looks up a string value in the page dictionary, either in the
// specified object or one of its parents.
//
// @since PDFio v1.7@
//
const char * // O - String value or `NULL` if none
pdfioPageGetString(pdfio_obj_t *page, // I - Page object
const char *key) // I - Dictionary key
{
_pdfio_value_t *v = get_page_value(page, key);
// Dictionary value
if (v && v->type == PDFIO_VALTYPE_STRING)
return (v->value.string);
else
return (NULL);
}
// //
// 'pdfioPageOpenStream()' - Open a content stream for a page. // 'pdfioPageOpenStream()' - Open a content stream for a page.
// //
@@ -87,14 +346,28 @@ pdfioPageOpenStream(
// Contents value // Contents value
PDFIO_DEBUG("pdfioPageOpenStream(page=%p(%lu), n=%lu, decode=%s)\n", (void *)page, page ? (unsigned long)page->number : 0, (unsigned long)n, decode ? "true" : "false");
if (!contents) if (!contents)
{
PDFIO_DEBUG("pdfioPageOpenStream: No contents.\n");
return (NULL); return (NULL);
}
else if (contents->type == PDFIO_VALTYPE_ARRAY && n < pdfioArrayGetSize(contents->value.array)) else if (contents->type == PDFIO_VALTYPE_ARRAY && n < pdfioArrayGetSize(contents->value.array))
{
PDFIO_DEBUG("pdfioPageOpenStream: Contents is array, opening numbered content stream.\n");
return (pdfioObjOpenStream(pdfioArrayGetObj(contents->value.array, n), decode)); return (pdfioObjOpenStream(pdfioArrayGetObj(contents->value.array, n), decode));
}
else if (n) else if (n)
{
PDFIO_DEBUG("pdfioPageOpenStream: Numbered stream does not exist.\n");
return (NULL); return (NULL);
}
else else
{
PDFIO_DEBUG("pdfioPageOpenStream: Opening single content stream %d.\n", (int)contents->value.indirect.number);
return (pdfioObjOpenStream(pdfioFileFindObj(page->pdf, contents->value.indirect.number), decode)); return (pdfioObjOpenStream(pdfioFileFindObj(page->pdf, contents->value.indirect.number), decode));
}
} }
@@ -105,6 +378,10 @@ pdfioPageOpenStream(
static _pdfio_value_t * // O - Value or NULL on error static _pdfio_value_t * // O - Value or NULL on error
get_contents(pdfio_obj_t *page) // I - Page object get_contents(pdfio_obj_t *page) // I - Page object
{ {
_pdfio_value_t *contents; // Contents value
pdfio_obj_t *obj; // Contents object
// Range check input... // Range check input...
if (!page) if (!page)
return (NULL); return (NULL);
@@ -119,5 +396,57 @@ get_contents(pdfio_obj_t *page) // I - Page object
if (page->value.type != PDFIO_VALTYPE_DICT) if (page->value.type != PDFIO_VALTYPE_DICT)
return (NULL); return (NULL);
return (_pdfioDictGetValue(page->value.value.dict, "Contents")); if ((contents = _pdfioDictGetValue(page->value.value.dict, "Contents")) == NULL)
return (NULL);
if (contents->type == PDFIO_VALTYPE_INDIRECT)
{
// See if the indirect object is a stream or an array of indirect object
// references...
if ((obj = pdfioFileFindObj(page->pdf, contents->value.indirect.number)) != NULL)
{
if (obj->value.type == PDFIO_VALTYPE_NONE)
{
if (!_pdfioObjLoad(obj))
return (NULL);
}
if (obj->value.type == PDFIO_VALTYPE_ARRAY)
contents = &(obj->value);
}
}
return (contents);
}
//
// 'get_page_value()' - Get a page dictionary value, including parents.
//
static _pdfio_value_t * // O - Dictionary value or `NULL` if none
get_page_value(pdfio_obj_t *page, // I - Page object
const char *key) // I - Dictionary key
{
_pdfio_value_t *v = NULL; // Dictionary value
while (page != NULL)
{
// Load the page object as needed...
if (page->value.type == PDFIO_VALTYPE_NONE && !_pdfioObjLoad(page))
break;
// If there isn't a dictionary for the object, stop...
if (page->value.type != PDFIO_VALTYPE_DICT)
break;
// Lookup the key...
if ((v = _pdfioDictGetValue(page->value.value.dict, key)) != NULL)
break;
page = pdfioDictGetObj(page->value.value.dict, "Parent");
}
return (v);
} }

View File

@@ -1,7 +1,7 @@
// //
// Private header file for PDFio. // Private header file for PDFio.
// //
// Copyright © 2021-2025 by Michael R Sweet. // Copyright © 2021-2026 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@@ -10,7 +10,7 @@
#ifndef PDFIO_PRIVATE_H #ifndef PDFIO_PRIVATE_H
# define PDFIO_PRIVATE_H # define PDFIO_PRIVATE_H
# ifdef _WIN32 # ifdef _WIN32
# define _CRT_SECURE_NO_WARNINGS // Disable bogus VS warnings/errors... # define _CRT_SECURE_NO_WARNINGS 1 // Disable bogus VS warnings/errors...
# endif // _WIN32 # endif // _WIN32
# include "pdfio.h" # include "pdfio.h"
# include <stdarg.h> # include <stdarg.h>
@@ -28,16 +28,16 @@
# define access _access // Map standard POSIX/C99 names # define access _access // Map standard POSIX/C99 names
# define close _close # define close _close
# define fileno _fileno # define fileno _fileno
# define lseek _lseek # define lseek(f,o,w) (off_t)_lseek((f),(long)(o),(w))
# define mkdir(d,p) _mkdir(d) # define mkdir(d,p) _mkdir(d)
# define open _open # define open _open
# define read _read # define read(f,b,s) _read((f),(b),(unsigned)(s))
# define rmdir _rmdir # define rmdir _rmdir
# define snprintf _snprintf # define snprintf _snprintf
# define strdup _strdup # define strdup _strdup
# define unlink _unlink # define unlink _unlink
# define vsnprintf _vsnprintf # define vsnprintf _vsnprintf
# define write _write # define write(f,b,s) _write((f),(b),(unsigned)(s))
# ifndef F_OK # ifndef F_OK
# define F_OK 00 // POSIX parameters/flags # define F_OK 00 // POSIX parameters/flags
# define W_OK 02 # define W_OK 02
@@ -94,10 +94,12 @@
// //
# define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values # define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values
# define PDFIO_MAX_STRING 65536 // Maximum length of string # define PDFIO_MAX_STRING 131072 // Maximum length of string
typedef void (*_pdfio_extfree_t)(void *); typedef void (*_pdfio_extfree_t)(void *p);
// Extension data free function // Extension data free function
typedef bool (*_pdfio_printf_t)(void *data, const char *format, ...);
// "printf" function
typedef enum _pdfio_mode_e // Read/write mode typedef enum _pdfio_mode_e // Read/write mode
{ {
@@ -117,6 +119,21 @@ typedef enum _pdfio_predictor_e // PNG predictor constants
_PDFIO_PREDICTOR_PNG_AUTO = 15 // PNG "auto" predictor (currently mapped to Paeth) _PDFIO_PREDICTOR_PNG_AUTO = 15 // PNG "auto" predictor (currently mapped to Paeth)
} _pdfio_predictor_t; } _pdfio_predictor_t;
typedef enum _pdfio_profile_e // PDF profile constants
{
_PDFIO_PROFILE_NONE = 0, // Base PDF file
_PDFIO_PROFILE_PCLM, // PCLm (PDF raster) file
_PDFIO_PROFILE_PDFA_1A, // PDF/A-1a:2005
_PDFIO_PROFILE_PDFA_1B, // PDF/A-1b:2005
_PDFIO_PROFILE_PDFA_2A, // PDF/A-2a:2011
_PDFIO_PROFILE_PDFA_2B, // PDF/A-2b:2011
_PDFIO_PROFILE_PDFA_2U, // PDF/A-2u:2011
_PDFIO_PROFILE_PDFA_3A, // PDF/A-3a:2012
_PDFIO_PROFILE_PDFA_3B, // PDF/A-3b:2012
_PDFIO_PROFILE_PDFA_3U, // PDF/A-3u:2012
_PDFIO_PROFILE_PDFA_4 // PDF/A-4:2020
} _pdfio_profile_t;
typedef ssize_t (*_pdfio_tconsume_cb_t)(void *data, size_t bytes); typedef ssize_t (*_pdfio_tconsume_cb_t)(void *data, size_t bytes);
typedef ssize_t (*_pdfio_tpeek_cb_t)(void *data, void *buffer, size_t bytes); typedef ssize_t (*_pdfio_tpeek_cb_t)(void *data, void *buffer, size_t bytes);
@@ -161,7 +178,7 @@ typedef struct _pdfio_value_s // Value structure
typedef struct _pdfio_aes_s // AES encryption state typedef struct _pdfio_aes_s // AES encryption state
{ {
size_t round_size; // Size of round key size_t round_size; // Size of round key
uint8_t round_key[240], // Round key uint8_t round_key[256], // Round key
iv[16]; // Initialization vector iv[16]; // Initialization vector
} _pdfio_aes_t; } _pdfio_aes_t;
@@ -196,6 +213,38 @@ typedef union _pdfio_crypto_ctx_u // Cryptographic contexts
} _pdfio_crypto_ctx_t; } _pdfio_crypto_ctx_t;
typedef size_t (*_pdfio_crypto_cb_t)(_pdfio_crypto_ctx_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len); typedef size_t (*_pdfio_crypto_cb_t)(_pdfio_crypto_ctx_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len);
typedef struct _pdfio_lzws_s // LZW string table
{
uint16_t prefix_code, // Prefix code
suffix; // Suffix (character)
} _pdfio_lzws_t;
typedef struct _pdfio_lzw_s // LZW state
{
uint8_t *next_in; // Next input byte
size_t avail_in; // Available input bytes
uint8_t in_bytes[256]; // Current input bytes
uint16_t in_bit, // Current input bit
in_bits; // Total input bits
uint8_t *next_out; // Next output byte
size_t avail_out; // Available output bytes
uint8_t cur_code_size, // Current code size
def_code_size, // Initial/default code size
early; // Early code change offset
bool reversed; // Reversed bit encoding?
uint16_t clear_code, // Clear code
eod_code, // End code
next_code, // Next code to be used
next_size_code, // Code where we need to increase the code size
first_code, // First code in sequence
old_code, // Previous code in sequence
stack[8192], // Output stack
*stptr; // Current stack pointer
_pdfio_lzws_t table[4096]; // String table
bool saw_eod; // Saw end-of-data code?
const char *error; // Error, if any
} _pdfio_lzw_t;
struct _pdfio_array_s struct _pdfio_array_s
{ {
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
@@ -221,23 +270,27 @@ struct _pdfio_dict_s // Dictionary
typedef struct _pdfio_objmap_s // PDF object map typedef struct _pdfio_objmap_s // PDF object map
{ {
pdfio_obj_t *obj; // Object for this file pdfio_obj_t *obj; // Object for this file
pdfio_file_t *src_pdf; // Source PDF file unsigned char src_id[32]; // Source PDF file file identifier
size_t src_number; // Source object number size_t src_number; // Source object number
} _pdfio_objmap_t; } _pdfio_objmap_t;
typedef struct _pdfio_strbuf_s // PDF string buffer typedef struct _pdfio_strbuf_s // PDF string buffer
{ {
struct _pdfio_strbuf_s *next; // Next string buffer struct _pdfio_strbuf_s *next; // Next string buffer
pdfio_file_t *pdf; // PDF file
bool bufused; // Is this string buffer being used? bool bufused; // Is this string buffer being used?
char buffer[PDFIO_MAX_STRING + 32]; char buffer[PDFIO_MAX_STRING + 32],
// String buffer // String buffer
*bufptr; // Pointer into buffer
} _pdfio_strbuf_t; } _pdfio_strbuf_t;
struct _pdfio_file_s // PDF file structure struct _pdfio_file_s // PDF file structure
{ {
char *filename; // Filename char *filename; // Filename
unsigned char file_id[32]; // File identifier bytes
struct lconv *loc; // Locale data struct lconv *loc; // Locale data
char *version; // Version number char *version; // Version number
_pdfio_profile_t profile; // PDF profile, if any
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
_pdfio_mode_t mode; // Read/write mode _pdfio_mode_t mode; // Read/write mode
@@ -266,10 +319,17 @@ struct _pdfio_file_s // PDF file structure
pdfio_obj_t *root_obj; // Root object/dictionary pdfio_obj_t *root_obj; // Root object/dictionary
pdfio_obj_t *info_obj; // Information object pdfio_obj_t *info_obj; // Information object
pdfio_obj_t *pages_obj; // Root pages object pdfio_obj_t *pages_obj; // Root pages object
pdfio_obj_t *encrypt_obj; // De/Encryption object/dictionary pdfio_obj_t *encrypt_obj; // Encryption object (not used for reading)
pdfio_obj_t *cp1252_obj, // CP1252 font encoding object pdfio_dict_t *encrypt_dict; // De/Encryption dictionary
*unicode_obj; // Unicode font encoding object pdfio_obj_t *cgats001_obj, // CGATS001 ICC profile object
*cp1252_obj, // CP1252 font encoding object
*unicode_obj, // Unicode font encoding object
*objstm_obj; // Object stream object
pdfio_dict_t *objstm_dict; // Object stream dictionary
size_t num_objstm; // Number of objects in object stream
pdfio_array_t *id_array; // ID array pdfio_array_t *id_array; // ID array
bool encrypt_metadata; // Encrypt metadata?
pdfio_dict_t *markinfo; // MarkInfo dictionary, if any
// Allocated data elements // Allocated data elements
size_t num_arrays, // Number of arrays size_t num_arrays, // Number of arrays
@@ -306,6 +366,9 @@ 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
size_t objstm_number; // Object stream number
char *objstm_data; // Object stream data, if any
size_t objstm_datalen; // Object stream data length
void *data; // Extension data, if any void *data; // Extension data, if any
_pdfio_extfree_t datafree; // Free callback for extension data _pdfio_extfree_t datafree; // Free callback for extension data
}; };
@@ -320,12 +383,20 @@ struct _pdfio_stream_s // Stream
char buffer[8192], // Read/write buffer char buffer[8192], // Read/write buffer
*bufptr, // Current position in buffer *bufptr, // Current position in buffer
*bufend; // End of buffer *bufend; // End of buffer
size_t a85size; // Size of ASCII85Decode buffer
char *a85buffer, // ASCII85Decode buffer, if any
*a85bufptr, // Pointer into ASCII85Decode buffer
*a85bufend, // End of data in ASCII85Decode buffer
a85decode[4], // Current block of decoded characters
*a85decptr, // Pointer into decoded characters
*a85decend; // Last decoded character
z_stream flate; // Flate filter state z_stream flate; // Flate filter state
_pdfio_lzw_t *lzw; // LZW filter state
_pdfio_predictor_t predictor; // Predictor function, if any _pdfio_predictor_t predictor; // Predictor function, if any
size_t pbpixel, // Size of a pixel in bytes size_t pbpixel, // Size of a pixel in bytes
pbsize, // Predictor buffer size, if any pbsize, // Predictor buffer size, if any
cbsize; // Compressed data buffer size cbsize; // Compressed data buffer size
unsigned char *cbuffer, // Compressed data buffer uint8_t *cbuffer, // Compressed data buffer
*prbuffer, // Raw buffer (previous line), as needed *prbuffer, // Raw buffer (previous line), as needed
*psbuffer; // PNG filter buffer, as needed *psbuffer; // PNG filter buffer, as needed
_pdfio_crypto_cb_t crypto_cb; // Encryption/descryption callback, if any _pdfio_crypto_cb_t crypto_cb; // Encryption/descryption callback, if any
@@ -339,6 +410,7 @@ struct _pdfio_stream_s // Stream
extern size_t _pdfio_strlcpy(char *dst, const char *src, size_t dstsize) _PDFIO_INTERNAL; extern size_t _pdfio_strlcpy(char *dst, const char *src, size_t dstsize) _PDFIO_INTERNAL;
extern double _pdfio_strtod(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL; extern double _pdfio_strtod(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
extern void _pdfio_utf16cpy(char *dst, const unsigned char *src, size_t srclen, size_t dstsize) _PDFIO_INTERNAL;
extern ssize_t _pdfio_vsnprintf(pdfio_file_t *pdf, char *buffer, size_t bufsize, const char *format, va_list ap) _PDFIO_INTERNAL; extern ssize_t _pdfio_vsnprintf(pdfio_file_t *pdf, char *buffer, size_t bufsize, const char *format, va_list ap) _PDFIO_INTERNAL;
extern bool _pdfioArrayDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_array_t *a, size_t depth) _PDFIO_INTERNAL; extern bool _pdfioArrayDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_array_t *a, size_t depth) _PDFIO_INTERNAL;
@@ -346,7 +418,7 @@ 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;
extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, size_t depth) _PDFIO_INTERNAL; extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, size_t depth) _PDFIO_INTERNAL;
extern bool _pdfioArrayWrite(pdfio_array_t *a, pdfio_obj_t *obj) _PDFIO_INTERNAL; extern bool _pdfioArrayWrite(_pdfio_printf_t cb, void *cbdata, pdfio_obj_t *obj, pdfio_array_t *a) _PDFIO_INTERNAL;
extern void _pdfioCryptoAESInit(_pdfio_aes_t *ctx, const uint8_t *key, size_t keylen, const uint8_t *iv) _PDFIO_INTERNAL; extern void _pdfioCryptoAESInit(_pdfio_aes_t *ctx, const uint8_t *key, size_t keylen, const uint8_t *iv) _PDFIO_INTERNAL;
extern size_t _pdfioCryptoAESDecrypt(_pdfio_aes_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL; extern size_t _pdfioCryptoAESDecrypt(_pdfio_aes_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
@@ -371,7 +443,7 @@ 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;
extern pdfio_dict_t *_pdfioDictRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, size_t depth) _PDFIO_INTERNAL; extern pdfio_dict_t *_pdfioDictRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, size_t depth) _PDFIO_INTERNAL;
extern bool _pdfioDictSetValue(pdfio_dict_t *dict, const char *key, _pdfio_value_t *value) _PDFIO_INTERNAL; extern bool _pdfioDictSetValue(pdfio_dict_t *dict, const char *key, _pdfio_value_t *value) _PDFIO_INTERNAL;
extern bool _pdfioDictWrite(pdfio_dict_t *dict, pdfio_obj_t *obj, off_t *length) _PDFIO_INTERNAL; extern bool _pdfioDictWrite(_pdfio_printf_t cb, void *cbdata, pdfio_obj_t *obj, pdfio_dict_t *dict, off_t *length) _PDFIO_INTERNAL;
extern bool _pdfioFileAddMappedObj(pdfio_file_t *pdf, pdfio_obj_t *dst_obj, pdfio_obj_t *src_obj) _PDFIO_INTERNAL; extern bool _pdfioFileAddMappedObj(pdfio_file_t *pdf, pdfio_obj_t *dst_obj, pdfio_obj_t *src_obj) _PDFIO_INTERNAL;
extern bool _pdfioFileAddPage(pdfio_file_t *pdf, pdfio_obj_t *obj) _PDFIO_INTERNAL; extern bool _pdfioFileAddPage(pdfio_file_t *pdf, pdfio_obj_t *obj) _PDFIO_INTERNAL;
@@ -382,7 +454,7 @@ extern bool _pdfioFileError(pdfio_file_t *pdf, const char *format, ...) _PDFIO_
extern pdfio_obj_t *_pdfioFileFindMappedObj(pdfio_file_t *pdf, pdfio_file_t *src_pdf, size_t src_number) _PDFIO_INTERNAL; extern pdfio_obj_t *_pdfioFileFindMappedObj(pdfio_file_t *pdf, pdfio_file_t *src_pdf, size_t src_number) _PDFIO_INTERNAL;
extern bool _pdfioFileFlush(pdfio_file_t *pdf) _PDFIO_INTERNAL; extern bool _pdfioFileFlush(pdfio_file_t *pdf) _PDFIO_INTERNAL;
extern int _pdfioFileGetChar(pdfio_file_t *pdf) _PDFIO_INTERNAL; extern int _pdfioFileGetChar(pdfio_file_t *pdf) _PDFIO_INTERNAL;
extern bool _pdfioFileGets(pdfio_file_t *pdf, char *buffer, size_t bufsize) _PDFIO_INTERNAL; extern bool _pdfioFileGets(pdfio_file_t *pdf, char *buffer, size_t bufsize, bool discard) _PDFIO_INTERNAL;
extern ssize_t _pdfioFilePeek(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL; extern ssize_t _pdfioFilePeek(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL;
extern bool _pdfioFilePrintf(pdfio_file_t *pdf, const char *format, ...) _PDFIO_INTERNAL; extern bool _pdfioFilePrintf(pdfio_file_t *pdf, const char *format, ...) _PDFIO_INTERNAL;
extern bool _pdfioFilePuts(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL; extern bool _pdfioFilePuts(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
@@ -391,6 +463,10 @@ extern off_t _pdfioFileSeek(pdfio_file_t *pdf, off_t offset, int whence) _PDFIO
extern off_t _pdfioFileTell(pdfio_file_t *pdf) _PDFIO_INTERNAL; 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 _pdfio_lzw_t *_pdfioLZWCreate(int def_code_size, int early, bool reversed) _PDFIO_INTERNAL;
extern void _pdfioLZWDelete(_pdfio_lzw_t *lzw) _PDFIO_INTERNAL;
extern bool _pdfioLZWInflate(_pdfio_lzw_t *lzw) _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 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;
@@ -400,9 +476,10 @@ extern bool _pdfioObjWriteHeader(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, size_t cbsize, pdfio_filter_t compression) _PDFIO_INTERNAL; extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, size_t cbsize, pdfio_filter_t compression) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL; extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
extern char *_pdfioStringAllocBuffer(pdfio_file_t *pdf); extern char *_pdfioStringAllocBuffer(pdfio_file_t *pdf, _pdfio_strbuf_t **bptr);
extern void _pdfioStringFreeBuffer(pdfio_file_t *pdf, char *buffer); extern void _pdfioStringFreeBuffer(pdfio_file_t *pdf, char *buffer);
extern bool _pdfioStringIsAllocated(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL; extern bool _pdfioStringIsAllocated(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
extern bool _pdfioStringPrintf(_pdfio_strbuf_t *bptr, const char *format, ...) _PDFIO_INTERNAL;
extern void _pdfioTokenClear(_pdfio_token_t *tb) _PDFIO_INTERNAL; extern void _pdfioTokenClear(_pdfio_token_t *tb) _PDFIO_INTERNAL;
extern void _pdfioTokenFlush(_pdfio_token_t *tb) _PDFIO_INTERNAL; extern void _pdfioTokenFlush(_pdfio_token_t *tb) _PDFIO_INTERNAL;
@@ -416,7 +493,7 @@ extern bool _pdfioValueDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_valu
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_printf_t cb, void *cbdata, pdfio_obj_t *obj, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL;
#endif // !PDFIO_PRIVATE_H #endif // !PDFIO_PRIVATE_H

View File

@@ -1,7 +1,7 @@
// //
// PDF stream functions for PDFio. // PDF stream functions for PDFio.
// //
// Copyright © 2021-2025 by Michael R Sweet. // Copyright © 2021-2026 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.
@@ -14,6 +14,8 @@
// Local functions... // Local functions...
// //
static ssize_t stream_get_bytes(pdfio_stream_t *st, void *buffer, size_t bytes);
static ssize_t stream_inflate(pdfio_stream_t *st, uint8_t *buffer, size_t bytes, bool exactly);
static unsigned char stream_paeth(unsigned char a, unsigned char b, unsigned char c); static unsigned char stream_paeth(unsigned char a, unsigned char b, unsigned char c);
static ssize_t stream_read(pdfio_stream_t *st, char *buffer, size_t bytes); static ssize_t stream_read(pdfio_stream_t *st, char *buffer, size_t bytes);
static bool stream_write(pdfio_stream_t *st, const void *buffer, size_t bytes); static bool stream_write(pdfio_stream_t *st, const void *buffer, size_t bytes);
@@ -39,6 +41,8 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
{ {
if (st->filter == PDFIO_FILTER_FLATE) if (st->filter == PDFIO_FILTER_FLATE)
inflateEnd(&(st->flate)); inflateEnd(&(st->flate));
else if (st->filter == PDFIO_FILTER_LZW)
_pdfioLZWDelete(st->lzw);
} }
else else
{ {
@@ -172,6 +176,7 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
st->pdf->current_obj = NULL; st->pdf->current_obj = NULL;
free(st->a85buffer);
free(st->cbuffer); free(st->cbuffer);
free(st->prbuffer); free(st->prbuffer);
free(st->psbuffer); free(st->psbuffer);
@@ -259,7 +264,7 @@ _pdfioStreamCreate(
{ {
colors = 1; colors = 1;
} }
else if (colors < 0 || colors > 4) else if (colors < 0 || colors > 32)
{ {
_pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors); _pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors);
free(st); free(st);
@@ -270,7 +275,7 @@ _pdfioStreamCreate(
{ {
columns = 1; columns = 1;
} }
else if (columns < 0) else if (columns < 0 || columns > 65536)
{ {
_pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns); _pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns);
free(st); free(st);
@@ -427,7 +432,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
const char *type; // Object type const char *type; // Object type
PDFIO_DEBUG("_pdfioStreamOpen(obj=%p(%u), decode=%s)\n", obj, (unsigned)obj->number, decode ? "true" : "false"); PDFIO_DEBUG("_pdfioStreamOpen(obj=%p(%u), decode=%s)\n", (void *)obj, (unsigned)obj->number, decode ? "true" : "false");
// Allocate a new stream object... // Allocate a new stream object...
if ((st = (pdfio_stream_t *)calloc(1, sizeof(pdfio_stream_t))) == NULL) if ((st = (pdfio_stream_t *)calloc(1, sizeof(pdfio_stream_t))) == NULL)
@@ -479,10 +484,34 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
pdfio_array_t *fa = pdfioDictGetArray(dict, "Filter"); pdfio_array_t *fa = pdfioDictGetArray(dict, "Filter");
// Filter array // Filter array
if (!filter && fa && pdfioArrayGetSize(fa) == 1) if (!filter && fa)
{ {
// Support single-valued arrays... const char *filter0 = pdfioArrayGetName(fa, 0);
filter = pdfioArrayGetName(fa, 0); // First filter
if (pdfioArrayGetSize(fa) == 1)
{
// Support single-valued arrays...
filter = filter0;
}
else if (pdfioArrayGetSize(fa) == 2 && filter0 && !strcmp(filter0, "ASCII85Decode"))
{
// Support ASCII85Decode + something else
st->a85size = 5200; // Enough for 4k of decoded data
// Allocate the ASCII85Decode buffer...
if ((st->a85buffer = malloc(st->a85size)) == NULL)
{
_pdfioFileError(st->pdf, "Unable to allocate ASCII85Decode buffer.");
goto error;
}
st->a85bufptr = st->a85buffer;
st->a85bufend = st->a85buffer;
// Get the second filter...
filter = pdfioArrayGetName(fa, 1);
}
} }
if (!filter) if (!filter)
@@ -490,7 +519,6 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
// No single filter name, do we have a compound filter? // No single filter name, do we have a compound filter?
if (fa) if (fa)
{ {
// TODO: Implement compound filters...
_pdfioFileError(st->pdf, "Unsupported compound stream filter."); _pdfioFileError(st->pdf, "Unsupported compound stream filter.");
goto error; goto error;
} }
@@ -498,9 +526,9 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
// No filter, read as-is... // No filter, read as-is...
st->filter = PDFIO_FILTER_NONE; st->filter = PDFIO_FILTER_NONE;
} }
else if (!strcmp(filter, "FlateDecode")) else if (!strcmp(filter, "FlateDecode") || !strcmp(filter, "LZWDecode"))
{ {
// Flate compression // Flate or LZW compression
pdfio_dict_t *params = pdfioDictGetDict(dict, "DecodeParms"); pdfio_dict_t *params = pdfioDictGetDict(dict, "DecodeParms");
// Decoding parameters // Decoding parameters
int bpc = (int)pdfioDictGetNumber(params, "BitsPerComponent"); int bpc = (int)pdfioDictGetNumber(params, "BitsPerComponent");
@@ -511,12 +539,11 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
// Number of columns // Number of columns
int predictor = (int)pdfioDictGetNumber(params, "Predictor"); int predictor = (int)pdfioDictGetNumber(params, "Predictor");
// Predictory value, if any // Predictory value, if any
int status; // ZLIB status
ssize_t rbytes; // Bytes read ssize_t rbytes; // Bytes read
PDFIO_DEBUG("_pdfioStreamOpen: FlateDecode - BitsPerComponent=%d, Colors=%d, Columns=%d, Predictor=%d\n", bpc, colors, columns, predictor); PDFIO_DEBUG("_pdfioStreamOpen: %s - BitsPerComponent=%d, Colors=%d, Columns=%d, Predictor=%d\n", filter, bpc, colors, columns, predictor);
st->filter = PDFIO_FILTER_FLATE; st->filter = !strcmp(filter, "FlateDecode") ? PDFIO_FILTER_FLATE : PDFIO_FILTER_LZW;
if (bpc == 0) if (bpc == 0)
{ {
@@ -532,7 +559,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
{ {
colors = 1; colors = 1;
} }
else if (colors < 0 || colors > 4) else if (colors < 0 || colors > 32)
{ {
_pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors); _pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors);
goto error; goto error;
@@ -542,7 +569,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
{ {
columns = 1; columns = 1;
} }
else if (columns < 0) else if (columns < 0 || columns > 65536)
{ {
_pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns); _pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns);
goto error; goto error;
@@ -562,6 +589,13 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
if (predictor >= 10) if (predictor >= 10)
st->pbsize ++; // Add PNG predictor byte st->pbsize ++; // Add PNG predictor byte
if (st->pbsize < 2)
{
_pdfioFileError(st->pdf, "Bad Predictor buffer size %lu.", (unsigned long)st->pbsize);
goto error;
}
PDFIO_DEBUG("_pdfioStreamOpen: st->predictor=%d, st->pbpixel=%u, st->pbsize=%lu\n", st->predictor, (unsigned)st->pbpixel, (unsigned long)st->pbsize);
if ((st->prbuffer = calloc(1, st->pbsize - 1)) == NULL || (st->psbuffer = calloc(1, st->pbsize)) == NULL) if ((st->prbuffer = calloc(1, st->pbsize - 1)) == NULL || (st->psbuffer = calloc(1, st->pbsize)) == NULL)
{ {
_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Predictor buffers.", (unsigned long)st->pbsize); _pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Predictor buffers.", (unsigned long)st->pbsize);
@@ -576,47 +610,62 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
st->cbsize = 4096; st->cbsize = 4096;
if ((st->cbuffer = malloc(st->cbsize)) == NULL) if ((st->cbuffer = malloc(st->cbsize)) == NULL)
{ {
_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Flate compression buffer.", (unsigned long)st->cbsize); _pdfioFileError(st->pdf, "Unable to allocate %lu bytes for FlateDecode decompression buffer.", (unsigned long)st->cbsize);
goto error; goto error;
} }
PDFIO_DEBUG("_pdfioStreamOpen: pos=%ld\n", (long)_pdfioFileTell(st->pdf)); PDFIO_DEBUG("_pdfioStreamOpen: pos=%ld\n", (long)_pdfioFileTell(st->pdf));
if (st->cbsize > st->remaining) if ((rbytes = stream_get_bytes(st, st->cbuffer, st->cbsize)) <= 0)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->cbsize);
if (rbytes <= 0)
{ {
_pdfioFileError(st->pdf, "Unable to read bytes for stream."); _pdfioFileError(st->pdf, "Unable to read bytes for stream.");
goto error; goto error;
} }
if (st->crypto_cb) if (st->filter == PDFIO_FILTER_FLATE)
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
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)
{ {
_pdfioFileError(st->pdf, "Unable to start Flate filter: %s", zstrerror(status)); // Flate decompression...
goto error; int status; // ZLIB status
}
st->remaining -= st->flate.avail_in; st->flate.next_in = (Bytef *)st->cbuffer;
} st->flate.avail_in = (uInt)rbytes;
else if (!strcmp(filter, "LZWDecode"))
{ 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]);
// LZW compression
st->filter = PDFIO_FILTER_LZW; if ((status = inflateInit(&(st->flate))) != Z_OK)
{
_pdfioFileError(st->pdf, "Unable to start FlateDecode filter: %s", zstrerror(status));
goto error;
}
}
else
{
// LZW decompression...
int early = 1;
if (pdfioDictGetType(params, "EarlyChange") == PDFIO_VALTYPE_NUMBER)
{
early = (int)pdfioDictGetNumber(params, "EarlyChange");
if (early < 0 || early > 100)
{
_pdfioFileError(st->pdf, "Bad EarlyChange value %d for LZWDecode filter.", early);
goto error;
}
}
if ((st->lzw = _pdfioLZWCreate(/*code_size*/8, early, /*reversed*/false)) == NULL)
{
_pdfioFileError(st->pdf, "Unable to initialize LZWDecode filter: %s", strerror(errno));
goto error;
}
st->lzw->next_in = st->cbuffer;
st->lzw->avail_in = (size_t)rbytes;
}
} }
else else
{ {
// Something else we don't support // Something else we don't support
_pdfioFileError(st->pdf, "Unsupported stream filter '%N'.", filter); _pdfioFileError(st->pdf, "Unsupported stream filter '/%s'.", filter);
goto error; goto error;
} }
} }
@@ -631,12 +680,13 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
// If we get here something went wrong... // If we get here something went wrong...
error: error:
free(st->a85buffer);
free(st->cbuffer); free(st->cbuffer);
free(st->prbuffer); free(st->prbuffer);
free(st->psbuffer); free(st->psbuffer);
free(st); free(st);
return (NULL);
return (NULL);
} }
@@ -690,8 +740,9 @@ pdfioStreamPeek(pdfio_stream_t *st, // I - Stream
// 'pdfioStreamPrintf()' - Write a formatted string to a stream. // 'pdfioStreamPrintf()' - Write a formatted string to a stream.
// //
// This function writes a formatted string to a stream. In addition to the // This function writes a formatted string to a stream. In addition to the
// standard `printf` format characters, you can use "%N" to format a PDF name // standard `printf` format characters, you can use "%H" to format a HTML/XML
// value ("/Name") and "%S" to format a PDF string ("(String)") value. // string value, "%N" to format a PDF name value ("/Name"), and "%S" to format
// a PDF string ("(String)") value.
// //
bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
@@ -840,7 +891,7 @@ pdfioStreamWrite(
*pptr; // Previous raw buffer *pptr; // Previous raw buffer
PDFIO_DEBUG("pdfioStreamWrite(st=%p, buffer=%p, bytes=%lu)\n", st, buffer, (unsigned long)bytes); PDFIO_DEBUG("pdfioStreamWrite(st=%p, buffer=%p, bytes=%lu)\n", (void *)st, buffer, (unsigned long)bytes);
// Range check input... // Range check input...
if (!st || st->pdf->mode != _PDFIO_MODE_WRITE || !buffer || !bytes) if (!st || st->pdf->mode != _PDFIO_MODE_WRITE || !buffer || !bytes)
@@ -1003,6 +1054,271 @@ pdfioStreamWrite(
} }
//
// 'stream_get_bytes()' - Read and decrypt raw or ASCII85Decode-encoded data.
//
static ssize_t // O - Bytes read or `-1` on error
stream_get_bytes(
pdfio_stream_t *st, // I - Stream
void *buffer, // I - Buffer
size_t bytes) // I - Maximum number of bytes to read
{
ssize_t rbytes; // Bytes read
if (st->a85buffer)
{
// Decode through the ASCII85Decode buffer...
char *bufptr = (char *)buffer; // Pointer into read buffer
rbytes = 0;
// Read as much as we can...
while (bytes > 0 && (st->a85bufptr < st->a85bufend || st->remaining > 0))
{
unsigned count, // Number of ASCII85 chars
a85val; // ASCII85 "chunk" value
char *a85bufptr; // Local copy of buffer pointer
size_t declen; // Decoded length
// First use up any remaining decoded chars...
if (st->a85decptr)
{
declen = (size_t)(st->a85decend - st->a85decptr);
if (bytes >= declen)
{
memcpy(bufptr, st->a85decptr, declen);
bufptr += declen;
rbytes += (ssize_t)declen;
st->a85decptr = NULL;
bytes -= declen;
if (bytes == 0)
break;
}
else
{
memcpy(bufptr, st->a85decptr, bytes);
rbytes += (ssize_t)bytes;
st->a85decptr += bytes;
break;
}
}
if ((st->a85bufend - st->a85bufptr) < 5 && st->remaining > 0)
{
// Fill the ASCII85Decode buffer...
ssize_t a85bytes = st->a85bufend - st->a85bufptr;
// Bytes in the ASCII85Decode buffer
size_t a85remaining = st->a85size - (size_t)a85bytes;
// Remaining capacity in the buffer
// First move any remaining bytes to the front of the buffer...
if (a85bytes > 0)
memmove(st->a85buffer, st->a85bufptr, (size_t)a85bytes);
st->a85bufptr = st->a85buffer;
st->a85bufend = st->a85buffer + a85bytes;
// Then read more data from the file...
if (a85remaining > st->remaining)
a85bytes = _pdfioFileRead(st->pdf, st->a85bufend, st->remaining);
else
a85bytes = _pdfioFileRead(st->pdf, st->a85bufend, a85remaining);
if (a85bytes > 0)
{
st->remaining -= (size_t)a85bytes;
if (st->crypto_cb)
(st->crypto_cb)(&st->crypto_ctx, (uint8_t *)st->a85bufend, (uint8_t *)st->a85bufend, (size_t)a85bytes);
st->a85bufend += a85bytes;
}
else
{
_pdfioFileError(st->pdf, "Read error in stream.");
return (-1);
}
}
// Grab the next chunk...
for (a85bufptr = st->a85bufptr, a85val = 0, count = 0; a85bufptr < st->a85bufend && count < 5; a85bufptr ++)
{
char a85ch = *a85bufptr; // Current character
if (a85ch >= '!' && a85ch <= 'u')
{
// Valid ASCII85Decode character...
a85val = a85val * 85 + (unsigned)a85ch - '!';
count ++;
}
else if (a85ch == 'z' && count == 0)
{
// 'z' == 0's
a85val = 0;
count = 5;
a85bufptr++;
}
else if (a85ch == '~')
{
break;
}
else if (!isspace(a85ch & 255))
{
// Invalid ASCII85Decode character...
_pdfioFileError(st->pdf, "Invalid ASCII85Decode character '%c' in stream.", a85ch);
return (-1);
}
}
st->a85bufptr = a85bufptr;
if (*a85bufptr == '~')
break;
if (count < 2)
{
// Need at least 2 characters to decode a single byte...
_pdfioFileError(st->pdf, "Invalid ASCII85Decode sequence in stream.");
return (-1);
}
declen = count - 1;
// Add rounds to properly align the decoded value...
while (count < 5)
{
a85val = a85val * 85 + 84;
count ++;
}
// Copy the bytes to the decode buffer...
st->a85decode[0] = (char)(a85val >> 24);
st->a85decode[1] = (char)((a85val >> 16) & 255);
st->a85decode[2] = (char)((a85val >> 8) & 255);
st->a85decode[3] = (char)(a85val & 255);
st->a85decptr = st->a85decode;
st->a85decend = st->a85decode + declen;
}
PDFIO_DEBUG("stream_get_bytes: Returning %ld ASCII85 bytes for stream.\n", (long)rbytes);
return (rbytes);
}
else
{
// Limit reads to the length of the stream...
if (bytes > st->remaining)
rbytes = _pdfioFileRead(st->pdf, buffer, st->remaining);
else
rbytes = _pdfioFileRead(st->pdf, buffer, bytes);
if (rbytes > 0)
{
st->remaining -= (size_t)rbytes;
if (st->crypto_cb)
(st->crypto_cb)(&st->crypto_ctx, (uint8_t *)buffer, (uint8_t *)buffer, (size_t)rbytes);
}
PDFIO_DEBUG("stream_get_bytes: Returning %ld raw bytes for stream.\n", (long)rbytes);
return (rbytes);
}
}
//
// 'stream_inflate()' - Decompress bytes from a stream (Flate or LZW) into the specified buffer.
//
static ssize_t
stream_inflate(pdfio_stream_t *st, // I - Stream
uint8_t *buffer, // I - Output buffer
size_t bytes, // I - Number of bytes
bool exactly) // I - Require exactly the number of bytes
{
ssize_t rbytes; // Bytes read
// Setup decompression to the output buffer...
if (st->filter == PDFIO_FILTER_FLATE)
{
st->flate.next_out = (Bytef *)buffer;
st->flate.avail_out = (uInt)bytes;
}
else
{
st->lzw->next_out = buffer;
st->lzw->avail_out = bytes;
}
// Loop to get the bytes...
do
{
if (st->filter == PDFIO_FILTER_FLATE)
{
// Flate decompress
int status; // Status of decompression
PDFIO_DEBUG("stream_inflate: avail_in=%u, avail_out=%u\n", st->flate.avail_in, st->flate.avail_out);
if (st->flate.avail_in == 0)
{
// Read more from the file...
if ((rbytes = stream_get_bytes(st, st->cbuffer, st->cbsize)) <= 0)
return (-1); // End of file...
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
}
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
PDFIO_DEBUG("stream_inflate: inflate() returned %d\n", status);
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
bytes = (size_t)st->flate.avail_out;
}
else
{
// LZW decompress
if (st->lzw->avail_in == 0)
{
// Read more from the file...
if ((rbytes = stream_get_bytes(st, st->cbuffer, st->cbsize)) <= 0)
return (-1); // End of file...
st->lzw->next_in = st->cbuffer;
st->lzw->avail_in = (size_t)rbytes;
}
if (!_pdfioLZWInflate(st->lzw) && !st->lzw->saw_eod)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, st->lzw->error);
return (-1);
}
bytes = st->lzw->avail_out;
}
}
while (bytes > 0 && exactly);
if (st->filter == PDFIO_FILTER_FLATE)
return (st->flate.next_out - (Bytef *)buffer);
else
return (st->lzw->next_out - (uint8_t *)buffer);
}
// //
// 'stream_paeth()' - PaethPredictor function for PNG decompression filter. // 'stream_paeth()' - PaethPredictor function for PNG decompression filter.
// //
@@ -1030,67 +1346,20 @@ stream_read(pdfio_stream_t *st, // I - Stream
char *buffer, // I - Buffer char *buffer, // I - Buffer
size_t bytes) // I - Number of bytes to read size_t bytes) // I - Number of bytes to 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)
{ {
// No filtering, but limit reads to the length of the stream... // No filtering...
if (bytes > st->remaining) return (stream_get_bytes(st, buffer, bytes));
rbytes = _pdfioFileRead(st->pdf, buffer, st->remaining);
else
rbytes = _pdfioFileRead(st->pdf, buffer, bytes);
if (rbytes > 0)
{
st->remaining -= (size_t)rbytes;
if (st->crypto_cb)
(st->crypto_cb)(&st->crypto_ctx, (uint8_t *)buffer, (uint8_t *)buffer, (size_t)rbytes);
}
return (rbytes);
} }
else if (st->filter == PDFIO_FILTER_FLATE) else if (st->filter == PDFIO_FILTER_FLATE || st->filter == PDFIO_FILTER_LZW)
{ {
// Deflate compression... // Flate or LZW compression...
int status; // Status of decompression
if (st->predictor == _PDFIO_PREDICTOR_NONE) if (st->predictor == _PDFIO_PREDICTOR_NONE)
{ {
// Decompress into the buffer... // Decompress into the buffer...
PDFIO_DEBUG("stream_read: No predictor.\n"); PDFIO_DEBUG("stream_read: No predictor.\n");
if (st->flate.avail_in == 0) return (stream_inflate(st, (uint8_t *)buffer, bytes, /*exactly*/false));
{
// Read more from the file...
if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->cbsize);
if (rbytes <= 0)
return (-1); // End of file...
if (st->crypto_cb)
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
st->remaining -= (size_t)rbytes;
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
}
st->flate.next_out = (Bytef *)buffer;
st->flate.avail_out = (uInt)bytes;
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
return (st->flate.next_out - (Bytef *)buffer);
} }
else if (st->predictor == _PDFIO_PREDICTOR_TIFF2) else if (st->predictor == _PDFIO_PREDICTOR_TIFF2)
{ {
@@ -1098,9 +1367,9 @@ stream_read(pdfio_stream_t *st, // I - Stream
// Size of pixel in bytes // Size of pixel in bytes
remaining = st->pbsize; remaining = st->pbsize;
// Remaining bytes // Remaining bytes
unsigned char *bufptr = (unsigned char *)buffer, uint8_t *bufptr = (uint8_t *)buffer,
// Pointer into buffer // Pointer into buffer
*bufsecond = (unsigned char *)buffer + pbpixel, *bufsecond = (uint8_t *)buffer + pbpixel,
// Pointer to second pixel in buffer // Pointer to second pixel in buffer
*sptr = st->psbuffer; *sptr = st->psbuffer;
// Current (raw) line // Current (raw) line
@@ -1113,43 +1382,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
return (-1); return (-1);
} }
st->flate.next_out = (Bytef *)sptr; if (stream_inflate(st, sptr, st->pbsize, /*exactly*/true) < 0)
st->flate.avail_out = (uInt)st->pbsize;
while (st->flate.avail_out > 0)
{
if (st->flate.avail_in == 0)
{
// Read more from the file...
if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->cbsize);
if (rbytes <= 0)
return (-1); // End of file...
if (st->crypto_cb)
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
st->remaining -= (size_t)rbytes;
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
}
avail_in = st->flate.avail_in;
avail_out = st->flate.avail_out;
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
break;
}
if (st->flate.avail_out > 0)
return (-1); // Early end of stream return (-1); // Early end of stream
for (; bufptr < bufsecond; remaining --, sptr ++) for (; bufptr < bufsecond; remaining --, sptr ++)
@@ -1166,9 +1399,9 @@ stream_read(pdfio_stream_t *st, // I - Stream
// Size of pixel in bytes // Size of pixel in bytes
remaining = st->pbsize - 1; remaining = st->pbsize - 1;
// Remaining bytes // Remaining bytes
unsigned char *bufptr = (unsigned char *)buffer, uint8_t *bufptr = (uint8_t *)buffer,
// Pointer into buffer // Pointer into buffer
*bufsecond = (unsigned char *)buffer + pbpixel, *bufsecond = (uint8_t *)buffer + pbpixel,
// Pointer to second pixel in buffer // Pointer to second pixel in buffer
*sptr = st->psbuffer + 1, *sptr = st->psbuffer + 1,
// Current (raw) line // Current (raw) line
@@ -1183,51 +1416,26 @@ stream_read(pdfio_stream_t *st, // I - Stream
return (-1); return (-1);
} }
st->flate.next_out = (Bytef *)sptr - 1; if (stream_inflate(st, sptr - 1, st->pbsize, /*exactly*/true) < 0)
st->flate.avail_out = (uInt)st->pbsize;
while (st->flate.avail_out > 0)
{
if (st->flate.avail_in == 0)
{
// Read more from the file...
if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->cbsize);
if (rbytes <= 0)
return (-1); // End of file...
if (st->crypto_cb)
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
st->remaining -= (size_t)rbytes;
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
}
avail_in = st->flate.avail_in;
avail_out = st->flate.avail_out;
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
break;
}
if (st->flate.avail_out > 0)
{ {
// Early end of stream // Early end of stream
PDFIO_DEBUG("stream_read: Early EOF (remaining=%u, avail_in=%d, avail_out=%d, data_type=%d, next_in=<%02X%02X%02X%02X...>).\n", (unsigned)st->remaining, st->flate.avail_in, st->flate.avail_out, st->flate.data_type, st->flate.next_in[0], st->flate.next_in[1], st->flate.next_in[2], st->flate.next_in[3]); PDFIO_DEBUG("stream_read: Early EOF (remaining=%u).\n", (unsigned)st->remaining);
return (-1); return (-1);
} }
// Apply predictor for this line // Apply predictor for this line
PDFIO_DEBUG("stream_read: Line %02X %02X %02X %02X %02X.\n", sptr[-1], sptr[0], sptr[0], sptr[2], sptr[3]); #ifdef DEBUG
if (remaining > 4)
PDFIO_DEBUG("stream_read: Line %02X %02X %02X %02X %02X ...\n", sptr[-1], sptr[0], sptr[1], sptr[2], sptr[3]);
else if (remaining > 3)
PDFIO_DEBUG("stream_read: Line %02X %02X %02X %02X %02X.\n", sptr[-1], sptr[0], sptr[1], sptr[2], sptr[3]);
else if (remaining > 2)
PDFIO_DEBUG("stream_read: Line %02X %02X %02X %02X.\n", sptr[-1], sptr[0], sptr[1], sptr[2]);
else if (remaining > 1)
PDFIO_DEBUG("stream_read: Line %02X %02X %02X.\n", sptr[-1], sptr[0], sptr[1]);
else
PDFIO_DEBUG("stream_read: Line %02X %02X.\n", sptr[-1], sptr[0]);
#endif // DEBUG
switch (sptr[-1]) switch (sptr[-1])
{ {
@@ -1314,8 +1522,6 @@ stream_write(pdfio_stream_t *st, // I - Stream
outbytes = cbytes; outbytes = cbytes;
} }
// fprintf(stderr, "stream_write: bytes=%u, outbytes=%u\n", (unsigned)bytes, (unsigned)outbytes);
if (!_pdfioFileWrite(st->pdf, st->cbuffer, outbytes)) if (!_pdfioFileWrite(st->pdf, st->cbuffer, outbytes))
return (false); return (false);

View File

@@ -1,7 +1,7 @@
// //
// PDF string functions for PDFio. // PDF string functions for PDFio.
// //
// Copyright © 2021-2025 by Michael R Sweet. // Copyright © 2021-2026 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.
@@ -158,6 +158,89 @@ _pdfio_strtod(pdfio_file_t *pdf, // I - PDF file
} }
//
// '_pdfio_utf16cpy()' - Convert UTF-16 to UTF-8.
//
void
_pdfio_utf16cpy(
char *dst, // I - Destination buffer for UTF-8
const unsigned char *src, // I - Source UTF-16
size_t srclen, // I - Length of UTF-16
size_t dstsize) // I - Destination buffer size
{
char *dstptr = dst, // Pointer into buffer
*dstend = dst + dstsize - 5; // End of buffer
int ch; // Unicode character
bool is_be = !memcmp(src, "\376\377", 2);
// Big-endian strings?
// Loop through the UTF-16 string, converting to Unicode then UTF-8...
for (src += 2, srclen -= 2; srclen > 1 && dstptr < dstend; src += 2, srclen -= 2)
{
// Initial character...
if (is_be)
ch = (src[0] << 8) | src[1];
else
ch = (src[1] << 8) | src[0];
if (ch >= 0xd800 && ch <= 0xdbff && srclen > 3)
{
// Multi-word UTF-16 char...
int lch; // Lower bits
if (is_be)
lch = (src[2] << 8) | src[3];
else
lch = (src[3] << 8) | src[2];
if (lch < 0xdc00 || lch >= 0xdfff)
break;
ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
src += 2;
srclen -= 2;
}
else if (ch >= 0xfffe)
{
continue;
}
// Convert Unicode to UTF-8...
if (ch < 128)
{
// ASCII
*dstptr++ = (char)ch;
}
else if (ch < 4096)
{
// 2-byte UTF-8
*dstptr++ = (char)(0xc0 | (ch >> 6));
*dstptr++ = (char)(0x80 | (ch & 0x3f));
}
else if (ch < 65536)
{
// 3-byte UTF-8
*dstptr++ = (char)(0xe0 | (ch >> 12));
*dstptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*dstptr++ = (char)(0x80 | (ch & 0x3f));
}
else
{
// 4-byte UTF-8
*dstptr++ = (char)(0xe0 | (ch >> 18));
*dstptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
*dstptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*dstptr++ = (char)(0x80 | (ch & 0x3f));
}
}
// Nul-terminate the UTF-8 string...
*dstptr = '\0';
}
// //
// '_pdfio_vsnprintf()' - Format a string. // '_pdfio_vsnprintf()' - Format a string.
// //
@@ -403,6 +486,59 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
} }
break; break;
case 'H' : // XML/HTML string
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
// Loop through the literal string...
while (*s)
{
// Escape special characters
if (*s == '&')
{
// &amp;
if (bufptr < bufend)
{
_pdfio_strlcpy(bufptr, "&amp;", (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr);
}
bytes += 5;
}
else if (*s == '<')
{
// &lt;
if (bufptr < bufend)
{
_pdfio_strlcpy(bufptr, "&lt;", (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr);
}
bytes += 4;
}
else if (*s == '>')
{
// &gt;
if (bufptr < bufend)
{
_pdfio_strlcpy(bufptr, "&gt;", (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr);
}
bytes += 4;
}
else
{
// Literal character...
if (bufptr < bufend)
*bufptr++ = *s;
bytes ++;
}
s ++;
}
break;
case 'S' : // PDF string case 'S' : // PDF string
if ((s = va_arg(ap, char *)) == NULL) if ((s = va_arg(ap, char *)) == NULL)
s = "(null)"; s = "(null)";
@@ -538,7 +674,8 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
char * // O - Buffer or `NULL` on error char * // O - Buffer or `NULL` on error
_pdfioStringAllocBuffer( _pdfioStringAllocBuffer(
pdfio_file_t *pdf) // I - PDF file pdfio_file_t *pdf, // I - PDF file
_pdfio_strbuf_t **bptr) // O - String buffer pointer
{ {
_pdfio_strbuf_t *current; // Current string buffer _pdfio_strbuf_t *current; // Current string buffer
@@ -547,21 +684,34 @@ _pdfioStringAllocBuffer(
for (current = pdf->strbuffers; current; current = current->next) for (current = pdf->strbuffers; current; current = current->next)
{ {
if (!current->bufused) if (!current->bufused)
{ goto done;
current->bufused = true;
return (current->buffer);
}
} }
// Didn't find one, allocate a new one... // Didn't find one, allocate a new one...
if ((current = calloc(1, sizeof(_pdfio_strbuf_t))) == NULL) if ((current = calloc(1, sizeof(_pdfio_strbuf_t))) == NULL)
{
if (bptr)
*bptr = NULL;
return (NULL); return (NULL);
}
// Add to the linked list of string buffers... // Add to the linked list of string buffers...
current->next = pdf->strbuffers; current->pdf = pdf;
current->next = pdf->strbuffers;
pdf->strbuffers = current;
// Claim and return the free string buffer...
done:
current->bufused = true; current->bufused = true;
pdf->strbuffers = current; if (bptr)
{
*bptr = current;
current->buffer[0] = '\0';
current->bufptr = current->buffer;
}
return (current->buffer); return (current->buffer);
} }
@@ -587,7 +737,7 @@ pdfioStringCreate(
int diff; // Different int diff; // Different
PDFIO_DEBUG("pdfioStringCreate(pdf=%p, s=\"%s\")\n", pdf, s); PDFIO_DEBUG("pdfioStringCreate(pdf=%p, s=\"%s\")\n", (void *)pdf, s);
// Range check input... // Range check input...
if (!pdf || !s) if (!pdf || !s)
@@ -721,6 +871,36 @@ _pdfioStringIsAllocated(
} }
//
// '_pdfioStringPrintf()' - Append a formatted string to a string buffer.
//
bool // O - `true` on success, `false` on failure
_pdfioStringPrintf(
_pdfio_strbuf_t *bptr, // I - String buffer
const char *format, // I - Format string
...) // I - Additional arguments as needed
{
va_list ap; // Pointer to additional arguments
size_t remaining; // Remaining bytes
ssize_t bytes; // Formatted bytes
// Format the string in the buffer...
va_start(ap, format);
remaining = sizeof(bptr->buffer) - (size_t)(bptr->bufptr - bptr->buffer);
bytes = _pdfio_vsnprintf(bptr->pdf, bptr->bufptr, remaining, format, ap);
va_end(ap);
// Advance the current position in the buffer and return.
bptr->bufptr += strlen(bptr->bufptr);
return (bytes < (ssize_t)remaining && bytes >= 0);
}
// //
// 'find_string()' - Find an element in the array. // 'find_string()' - Find an element in the array.
// //

View File

@@ -54,7 +54,7 @@ static int get_char(_pdfio_token_t *tb);
void void
_pdfioTokenClear(_pdfio_token_t *tb) // I - Token buffer/stack _pdfioTokenClear(_pdfio_token_t *tb) // I - Token buffer/stack
{ {
PDFIO_DEBUG("_pdfioTokenClear(tb=%p)\n", tb); PDFIO_DEBUG("_pdfioTokenClear(tb=%p)\n", (void *)tb);
while (tb->num_tokens > 0) while (tb->num_tokens > 0)
{ {
@@ -132,7 +132,7 @@ _pdfioTokenGet(_pdfio_token_t *tb, // I - Token buffer/stack
if ((len = strlen(tb->tokens[tb->num_tokens])) > (bufsize - 1)) if ((len = strlen(tb->tokens[tb->num_tokens])) > (bufsize - 1))
{ {
// Value too large... // 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]); PDFIO_DEBUG("_pdfioTokenGet(tb=%p, buffer=%p, bufsize=%u): Token '%s' from stack too large.\n", (void *)tb, (void *)buffer, (unsigned)bufsize, tb->tokens[tb->num_tokens]);
*buffer = '\0'; *buffer = '\0';
return (false); return (false);
} }
@@ -140,7 +140,7 @@ _pdfioTokenGet(_pdfio_token_t *tb, // I - Token buffer/stack
memcpy(buffer, tb->tokens[tb->num_tokens], len); memcpy(buffer, tb->tokens[tb->num_tokens], len);
buffer[len] = '\0'; 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", (void *)tb, (void *)buffer, (unsigned)bufsize, buffer);
free(tb->tokens[tb->num_tokens]); free(tb->tokens[tb->num_tokens]);
tb->tokens[tb->num_tokens] = NULL; tb->tokens[tb->num_tokens] = NULL;
@@ -380,7 +380,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
else else
{ {
// Out of space // Out of space
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "String token too large.");
*bufptr = '\0'; *bufptr = '\0';
return (false); return (false);
} }
@@ -393,9 +393,18 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
return (false); return (false);
} }
if (saw_nul) if ((bufptr - buffer) > 3 && ((bufptr - buffer) & 1) != 0 && (!memcmp(buffer, "(\377\376", 3) || !memcmp(buffer, "(\376\377", 3)))
{
// UTF-16 string, convert to UTF-8...
PDFIO_DEBUG("_pdfioTokenRead: Converting string to UTF-8.\n", stderr);
_pdfio_utf16cpy(buffer + 1, (unsigned char *)buffer + 1, (size_t)(bufptr - buffer - 1), bufsize - 1);
PDFIO_DEBUG("_pdfioTokenRead: Read '%s'.\n", buffer);
return (true);
}
else if (saw_nul)
{ {
// Convert to a hex (binary) string... // Contains nul characters, convert to a hex (binary) string...
char *litptr, // Pointer to literal character char *litptr, // Pointer to literal character
*hexptr; // Pointer to hex character *hexptr; // Pointer to hex character
size_t bytes = (size_t)(bufptr - buffer - 1); size_t bytes = (size_t)(bufptr - buffer - 1);
@@ -408,7 +417,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
if ((2 * (bytes + 1)) > bufsize) if ((2 * (bytes + 1)) > bufsize)
{ {
// Out of space... // Out of space...
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Binary string token too large.");
*bufptr = '\0'; *bufptr = '\0';
return (false); return (false);
} }
@@ -442,7 +451,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
else else
{ {
// Out of space... // Out of space...
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Keyword token too large.");
*bufptr = '\0'; *bufptr = '\0';
return (false); return (false);
} }
@@ -474,7 +483,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
else else
{ {
// Out of space... // Out of space...
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Number token too large.");
*bufptr = '\0'; *bufptr = '\0';
return (false); return (false);
} }
@@ -523,7 +532,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
else else
{ {
// Out of space // Out of space
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Name token too large.");
*bufptr = '\0'; *bufptr = '\0';
return (false); return (false);
} }
@@ -566,7 +575,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
else else
{ {
// Too large // Too large
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Hex string token too large.");
*bufptr = '\0'; *bufptr = '\0';
return (false); return (false);
} }

View File

@@ -1,7 +1,7 @@
// //
// PDF value functions for PDFio. // PDF value functions for PDFio.
// //
// Copyright © 2021-2025 by Michael R Sweet. // Copyright © 2021-2026 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.
@@ -46,7 +46,7 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file
#endif // DEBUG #endif // DEBUG
PDFIO_DEBUG("_pdfioValueCopy(pdfdst=%p, vdst=%p, pdfsrc=%p, vsrc=%p(%s))\n", pdfdst, vdst, pdfsrc, vsrc, types[vsrc->type]); PDFIO_DEBUG("_pdfioValueCopy(pdfdst=%p, vdst=%p, pdfsrc=%p, vsrc=%p(%s))\n", (void *)pdfdst, (void *)vdst, (void *)pdfsrc, (void *)vsrc, types[vsrc->type]);
if (pdfdst == pdfsrc && vsrc->type != PDFIO_VALTYPE_BINARY) if (pdfdst == pdfsrc && vsrc->type != PDFIO_VALTYPE_BINARY)
{ {
@@ -76,7 +76,8 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file
return (NULL); return (NULL);
case PDFIO_VALTYPE_ARRAY : case PDFIO_VALTYPE_ARRAY :
vdst->value.array = pdfioArrayCopy(pdfdst, vsrc->value.array); if ((vdst->value.array = pdfioArrayCopy(pdfdst, vsrc->value.array)) == NULL)
return (NULL);
break; break;
case PDFIO_VALTYPE_BINARY : case PDFIO_VALTYPE_BINARY :
@@ -97,12 +98,14 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file
return (vdst); return (vdst);
case PDFIO_VALTYPE_DICT : case PDFIO_VALTYPE_DICT :
vdst->value.dict = pdfioDictCopy(pdfdst, vsrc->value.dict); if ((vdst->value.dict = pdfioDictCopy(pdfdst, vsrc->value.dict)) == NULL)
return (NULL);
break; break;
case PDFIO_VALTYPE_NAME : case PDFIO_VALTYPE_NAME :
case PDFIO_VALTYPE_STRING : case PDFIO_VALTYPE_STRING :
vdst->value.name = pdfioStringCreate(pdfdst, vsrc->value.name); if ((vdst->value.name = pdfioStringCreate(pdfdst, vsrc->value.name)) == NULL)
return (NULL);
break; break;
} }
@@ -157,7 +160,7 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long."); _pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
return (false); return (false);
} }
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf)) == NULL) else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf, /*bptr*/NULL)) == NULL)
{ {
_pdfioFileError(pdf, "Unable to read encrypted binary string - out of memory."); _pdfioFileError(pdf, "Unable to read encrypted binary string - out of memory.");
return (false); return (false);
@@ -172,7 +175,7 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
// Copy the decrypted string back to the value and adjust the length... // Copy the decrypted string back to the value and adjust the length...
memcpy(v->value.binary.data, temp, templen); memcpy(v->value.binary.data, temp, templen);
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128) if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128 && temp[templen - 1] <= templen)
v->value.binary.datalen = templen - temp[templen - 1]; v->value.binary.datalen = templen - temp[templen - 1];
else else
v->value.binary.datalen = templen; v->value.binary.datalen = templen;
@@ -183,20 +186,48 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
case PDFIO_VALTYPE_STRING : case PDFIO_VALTYPE_STRING :
// Decrypt regular string... // Decrypt regular string...
templen = strlen(v->value.string); templen = strlen(v->value.string);
if (templen > (sizeof(temp) - 33)) if (templen > (PDFIO_MAX_STRING - 1))
{ {
_pdfioFileError(pdf, "Unable to read encrypted string - too long."); _pdfioFileError(pdf, "Unable to read encrypted string - too long.");
return (false); return (false);
} }
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf, /*bptr*/NULL)) == NULL)
{
_pdfioFileError(pdf, "Unable to read encrypted binary string - out of memory.");
return (false);
}
ivlen = templen; ivlen = templen;
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, (uint8_t *)v->value.string, &ivlen)) == NULL) if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, (uint8_t *)v->value.string, &ivlen)) == NULL)
return (false); return (false);
templen = (cb)(&ctx, temp, (uint8_t *)v->value.string + ivlen, templen - ivlen); templen = (cb)(&ctx, temp, (uint8_t *)v->value.string + ivlen, templen - ivlen);
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128 && temp[templen - 1] <= templen)
templen -= temp[templen - 1];
temp[templen] = '\0'; temp[templen] = '\0';
if ((timeval = get_date_time((char *)temp)) != 0) if ((templen & 1) == 0 && (!memcmp(temp, "\376\377", 2) || !memcmp(temp, "\377\376", 2)))
{
// Convert UTF-16 to UTF-8...
char utf8[4096]; // Temporary string
_pdfio_utf16cpy(utf8, temp, templen, sizeof(utf8));
if ((timeval = get_date_time((char *)utf8)) != 0)
{
// Change the type to date...
v->type = PDFIO_VALTYPE_DATE;
v->value.date = timeval;
}
else
{
// Copy the decrypted string back to the value...
v->value.string = pdfioStringCreate(pdf, utf8);
}
}
else if ((timeval = get_date_time((char *)temp)) != 0)
{ {
// Change the type to date... // Change the type to date...
v->type = PDFIO_VALTYPE_DATE; v->type = PDFIO_VALTYPE_DATE;
@@ -207,6 +238,8 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
// Copy the decrypted string back to the value... // Copy the decrypted string back to the value...
v->value.string = pdfioStringCreate(pdf, (char *)temp); v->value.string = pdfioStringCreate(pdf, (char *)temp);
} }
_pdfioStringFreeBuffer(pdf, (char *)temp);
break; break;
} }
@@ -308,7 +341,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
size_t depth) // I - Depth of value size_t depth) // I - Depth of value
{ {
_pdfio_value_t *ret = NULL; // Return value _pdfio_value_t *ret = NULL; // Return value
char *token = _pdfioStringAllocBuffer(pdf); char *token = _pdfioStringAllocBuffer(pdf, /*bptr*/NULL);
// Token buffer // Token buffer
time_t timeval; // Date/time value time_t timeval; // Date/time value
#ifdef DEBUG #ifdef DEBUG
@@ -329,7 +362,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
#endif // DEBUG #endif // DEBUG
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v); PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", (void *)pdf, (void *)obj, (void *)v);
if (!token) if (!token)
goto done; goto done;
@@ -367,18 +400,22 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
ret = v; ret = v;
} }
else if ((timeval = get_date_time(token + 1)) != 0)
{
v->type = PDFIO_VALTYPE_DATE;
v->value.date = timeval;
ret = v;
}
else if (token[0] == '(') else if (token[0] == '(')
{ {
// String if ((timeval = get_date_time(token + 1)) != 0)
v->type = PDFIO_VALTYPE_STRING; {
v->value.string = pdfioStringCreate(pdf, token + 1); // Date
ret = v; v->type = PDFIO_VALTYPE_DATE;
v->value.date = timeval;
ret = v;
}
else
{
// String
v->type = PDFIO_VALTYPE_STRING;
v->value.string = pdfioStringCreate(pdf, token + 1);
ret = v;
}
} }
else if (token[0] == '/') else if (token[0] == '/')
{ {
@@ -481,7 +518,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
// Integer... // Integer...
long generation = 0; // Generation number long generation = 0; // Generation number
while (tempptr < tb->bufend && isdigit(*tempptr & 255)) while (tempptr < tb->bufend && generation < 65536 && isdigit(*tempptr & 255))
{ {
generation = generation * 10 + *tempptr - '0'; generation = generation * 10 + *tempptr - '0';
tempptr ++; tempptr ++;
@@ -566,10 +603,12 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
// //
bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
_pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file _pdfioValueWrite(
pdfio_obj_t *obj, // I - Object, if any _pdfio_printf_t cb, // I - Printf callback function
_pdfio_value_t *v, // I - Value void *cbdata, // I - Printf callback data
off_t *length)// O - Offset to /Length value, if any pdfio_obj_t *obj, // I - Object, if any
_pdfio_value_t *v, // I - Value
off_t *length) // O - Offset to /Length value, if any
{ {
switch (v->type) switch (v->type)
{ {
@@ -577,7 +616,7 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
return (false); return (false);
case PDFIO_VALTYPE_ARRAY : case PDFIO_VALTYPE_ARRAY :
return (_pdfioArrayWrite(v->value.array, obj)); return (_pdfioArrayWrite(cb, cbdata, obj, v->value.array));
case PDFIO_VALTYPE_BINARY : case PDFIO_VALTYPE_BINARY :
{ {
@@ -587,26 +626,26 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
bool ret = false; // Return value bool ret = false; // Return value
if (obj && pdf->encryption) if (obj && obj->pdf->encryption)
{ {
// Write encrypted string... // Write encrypted string...
_pdfio_crypto_ctx_t ctx; // Encryption context _pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback _pdfio_crypto_cb_t ccb; // Encryption callback
size_t ivlen; // Number of initialization vector bytes size_t ivlen; // Number of initialization vector bytes
if (v->value.binary.datalen > PDFIO_MAX_STRING) if (v->value.binary.datalen > PDFIO_MAX_STRING)
{ {
_pdfioFileError(pdf, "Unable to write encrypted binary string - too long."); _pdfioFileError(obj->pdf, "Unable to write encrypted binary string - too long.");
return (false); return (false);
} }
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf)) == NULL) else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(obj->pdf, /*bptr*/NULL)) == NULL)
{ {
_pdfioFileError(pdf, "Unable to write encrypted binary string - out of memory."); _pdfioFileError(obj->pdf, "Unable to write encrypted binary string - out of memory.");
return (false); return (false);
} }
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen); ccb = _pdfioCryptoMakeWriter(obj->pdf, obj, &ctx, temp, &ivlen);
databytes = (cb)(&ctx, temp + ivlen, v->value.binary.data, v->value.binary.datalen) + ivlen; databytes = (ccb)(&ctx, temp + ivlen, v->value.binary.data, v->value.binary.datalen) + ivlen;
dataptr = temp; dataptr = temp;
} }
else else
@@ -615,33 +654,33 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
databytes = v->value.binary.datalen; databytes = v->value.binary.datalen;
} }
if (!_pdfioFilePuts(pdf, "<")) if (!(cb)(cbdata, "<"))
goto bindone; goto bindone;
for (; databytes > 1; databytes -= 2, dataptr += 2) for (; databytes > 1; databytes -= 2, dataptr += 2)
{ {
if (!_pdfioFilePrintf(pdf, "%02X%02X", dataptr[0], dataptr[1])) if (!(cb)(cbdata, "%02X%02X", dataptr[0], dataptr[1]))
goto bindone; goto bindone;
} }
if (databytes > 0 && !_pdfioFilePrintf(pdf, "%02X", dataptr[0])) if (databytes > 0 && !(cb)(cbdata, "%02X", dataptr[0]))
goto bindone; goto bindone;
ret = _pdfioFilePuts(pdf, ">"); ret = (cb)(cbdata, ">");
bindone: bindone:
if (temp) if (temp)
_pdfioStringFreeBuffer(pdf, (char *)temp); _pdfioStringFreeBuffer(obj->pdf, (char *)temp);
return (ret); return (ret);
} }
case PDFIO_VALTYPE_BOOLEAN : case PDFIO_VALTYPE_BOOLEAN :
if (v->value.boolean) if (v->value.boolean)
return (_pdfioFilePuts(pdf, " true")); return ((cb)(cbdata, " true"));
else else
return (_pdfioFilePuts(pdf, " false")); return ((cb)(cbdata, " false"));
case PDFIO_VALTYPE_DATE : case PDFIO_VALTYPE_DATE :
{ {
@@ -656,64 +695,64 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
snprintf(datestr, sizeof(datestr), "D:%04d%02d%02d%02d%02d%02dZ", date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec); snprintf(datestr, sizeof(datestr), "D:%04d%02d%02d%02d%02d%02dZ", date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec);
if (obj && pdf->encryption) if (obj && obj->pdf->encryption)
{ {
// Write encrypted string... // Write encrypted string...
uint8_t temp[64], // Encrypted bytes uint8_t temp[64], // Encrypted bytes
*tempptr; // Pointer into encrypted bytes *tempptr; // Pointer into encrypted bytes
_pdfio_crypto_ctx_t ctx; // Encryption context _pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback _pdfio_crypto_cb_t ccb; // Encryption callback
size_t len = strlen(datestr), size_t len = strlen(datestr),
// Length of value // Length of value
ivlen, // Number of initialization vector bytes ivlen, // Number of initialization vector bytes
tempbytes; // Number of output bytes tempbytes; // Number of output bytes
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen); ccb = _pdfioCryptoMakeWriter(obj->pdf, obj, &ctx, temp, &ivlen);
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)datestr, len) + ivlen; tempbytes = (ccb)(&ctx, temp + ivlen, (const uint8_t *)datestr, len) + ivlen;
if (!_pdfioFilePuts(pdf, "<")) if (!(cb)(cbdata, "<"))
return (false); return (false);
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2) for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
{ {
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1])) if (!(cb)(cbdata, "%02X%02X", tempptr[0], tempptr[1]))
return (false); return (false);
} }
if (tempbytes > 0) if (tempbytes > 0)
return (_pdfioFilePrintf(pdf, "%02X>", *tempptr)); return ((cb)(cbdata, "%02X>", *tempptr));
else else
return (_pdfioFilePuts(pdf, ">")); return ((cb)(cbdata, ">"));
} }
else else
{ {
return (_pdfioFilePrintf(pdf, "%S", datestr)); return ((cb)(cbdata, "%S", datestr));
} }
} }
case PDFIO_VALTYPE_DICT : case PDFIO_VALTYPE_DICT :
return (_pdfioDictWrite(v->value.dict, obj, length)); return (_pdfioDictWrite(cb, cbdata, obj, v->value.dict, length));
case PDFIO_VALTYPE_INDIRECT : case PDFIO_VALTYPE_INDIRECT :
return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation)); return ((cb)(cbdata, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation));
case PDFIO_VALTYPE_NAME : case PDFIO_VALTYPE_NAME :
return (_pdfioFilePrintf(pdf, "%N", v->value.name)); return ((cb)(cbdata, "%N", v->value.name));
case PDFIO_VALTYPE_NULL : case PDFIO_VALTYPE_NULL :
return (_pdfioFilePuts(pdf, " null")); return ((cb)(cbdata, " null"));
case PDFIO_VALTYPE_NUMBER : case PDFIO_VALTYPE_NUMBER :
return (_pdfioFilePrintf(pdf, " %.6f", v->value.number)); return ((cb)(cbdata, " %.6f", v->value.number));
case PDFIO_VALTYPE_STRING : case PDFIO_VALTYPE_STRING :
if (obj && pdf->encryption) if (obj && obj->pdf->encryption)
{ {
// Write encrypted string... // Write encrypted string...
uint8_t *temp = NULL, // Encrypted bytes uint8_t *temp = NULL, // Encrypted bytes
*tempptr; // Pointer into encrypted bytes *tempptr; // Pointer into encrypted bytes
_pdfio_crypto_ctx_t ctx; // Encryption context _pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback _pdfio_crypto_cb_t ccb; // Encryption callback
size_t len = strlen(v->value.string), size_t len = strlen(v->value.string),
// Length of value // Length of value
ivlen, // Number of initialization vector bytes ivlen, // Number of initialization vector bytes
@@ -722,42 +761,42 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
if (len > PDFIO_MAX_STRING) if (len > PDFIO_MAX_STRING)
{ {
_pdfioFileError(pdf, "Unable to write encrypted string - too long."); _pdfioFileError(obj->pdf, "Unable to write encrypted string - too long.");
return (false); return (false);
} }
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf)) == NULL) else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(obj->pdf, /*bptr*/NULL)) == NULL)
{ {
_pdfioFileError(pdf, "Unable to write encrypted string - out of memory."); _pdfioFileError(obj->pdf, "Unable to write encrypted string - out of memory.");
return (false); return (false);
} }
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen); ccb = _pdfioCryptoMakeWriter(obj->pdf, obj, &ctx, temp, &ivlen);
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)v->value.string, len) + ivlen; tempbytes = (ccb)(&ctx, temp + ivlen, (const uint8_t *)v->value.string, len) + ivlen;
if (!_pdfioFilePuts(pdf, "<")) if (!(cb)(cbdata, "<"))
goto strdone; goto strdone;
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2) for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
{ {
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1])) if (!(cb)(cbdata, "%02X%02X", tempptr[0], tempptr[1]))
goto strdone; goto strdone;
} }
if (tempbytes > 0 && !_pdfioFilePrintf(pdf, "%02X", *tempptr)) if (tempbytes > 0 && !(cb)(cbdata, "%02X", *tempptr))
goto strdone; goto strdone;
ret = _pdfioFilePuts(pdf, ">"); ret = (cb)(cbdata, ">");
strdone : strdone :
_pdfioStringFreeBuffer(pdf, (char *)temp); _pdfioStringFreeBuffer(obj->pdf, (char *)temp);
return (ret); return (ret);
} }
else else
{ {
// Write unencrypted string... // Write unencrypted string...
return (_pdfioFilePrintf(pdf, "%S", v->value.string)); return ((cb)(cbdata, "%S", v->value.string));
} }
} }

26
pdfio.h
View File

@@ -1,7 +1,7 @@
// //
// Public header file for PDFio. // Public header file for PDFio.
// //
// Copyright © 2021-2025 by Michael R Sweet. // Copyright © 2021-2026 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@@ -23,9 +23,9 @@ extern "C" {
// Version numbers... // Version numbers...
// //
# define PDFIO_VERSION "1.5.2" # define PDFIO_VERSION "1.7.0"
# define PDFIO_VERSION_MAJOR 1 # define PDFIO_VERSION_MAJOR 1
# define PDFIO_VERSION_MINOR 5 # define PDFIO_VERSION_MINOR 7
// //
@@ -46,7 +46,7 @@ extern "C" {
// //
# if _WIN32 # if _WIN32
typedef __int64 ssize_t; // POSIX type not present on Windows... @private@ typedef __int64 ssize_t; // POSIX type not present on Windows @private@
# endif // _WIN32 # endif // _WIN32
typedef struct _pdfio_array_s pdfio_array_t; typedef struct _pdfio_array_s pdfio_array_t;
@@ -62,7 +62,7 @@ typedef bool (*pdfio_error_cb_t)(pdfio_file_t *pdf, const char *message, void *d
typedef enum pdfio_encryption_e // PDF encryption modes typedef enum pdfio_encryption_e // PDF encryption modes
{ {
PDFIO_ENCRYPTION_NONE = 0, // No encryption PDFIO_ENCRYPTION_NONE = 0, // No encryption
PDFIO_ENCRYPTION_RC4_40, // 40-bit RC4 encryption (PDF 1.3) PDFIO_ENCRYPTION_RC4_40, // 40-bit RC4 encryption (PDF 1.3, reading only)
PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4) PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4)
PDFIO_ENCRYPTION_AES_128, // 128-bit AES encryption (PDF 1.6) PDFIO_ENCRYPTION_AES_128, // 128-bit AES encryption (PDF 1.6)
PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0) @exclude all@ PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0) @exclude all@
@@ -72,11 +72,11 @@ typedef enum pdfio_filter_e // Compression/decompression filters for streams
PDFIO_FILTER_NONE, // No filter PDFIO_FILTER_NONE, // No filter
PDFIO_FILTER_ASCIIHEX, // ASCIIHexDecode filter (reading only) PDFIO_FILTER_ASCIIHEX, // ASCIIHexDecode filter (reading only)
PDFIO_FILTER_ASCII85, // ASCII85Decode filter (reading only) PDFIO_FILTER_ASCII85, // ASCII85Decode filter (reading only)
PDFIO_FILTER_CCITTFAX, // CCITTFaxDecode filter PDFIO_FILTER_CCITTFAX, // CCITTFaxDecode filter (reading only)
PDFIO_FILTER_CRYPT, // Encryption filter PDFIO_FILTER_CRYPT, // Encryption filter
PDFIO_FILTER_DCT, // DCTDecode (JPEG) filter PDFIO_FILTER_DCT, // DCTDecode (JPEG) filter
PDFIO_FILTER_FLATE, // FlateDecode filter PDFIO_FILTER_FLATE, // FlateDecode filter
PDFIO_FILTER_JBIG2, // JBIG2Decode filter PDFIO_FILTER_JBIG2, // JBIG2Decode filter (reading only)
PDFIO_FILTER_JPX, // JPXDecode filter (reading only) PDFIO_FILTER_JPX, // JPXDecode filter (reading only)
PDFIO_FILTER_LZW, // LZWDecode filter (reading only) PDFIO_FILTER_LZW, // LZWDecode filter (reading only)
PDFIO_FILTER_RUNLENGTH, // RunLengthDecode filter (reading only) PDFIO_FILTER_RUNLENGTH, // RunLengthDecode filter (reading only)
@@ -201,6 +201,7 @@ extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetKeywords(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern const char *pdfioFileGetKeywords(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetLanguage(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern time_t pdfioFileGetModificationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern time_t pdfioFileGetModificationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetName(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern const char *pdfioFileGetName(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern size_t pdfioFileGetNumObjs(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern size_t pdfioFileGetNumObjs(pdfio_file_t *pdf) _PDFIO_PUBLIC;
@@ -217,6 +218,7 @@ extern void pdfioFileSetAuthor(pdfio_file_t *pdf, const char *value) _PDFIO_PUB
extern void pdfioFileSetCreationDate(pdfio_file_t *pdf, time_t value) _PDFIO_PUBLIC; extern void pdfioFileSetCreationDate(pdfio_file_t *pdf, time_t value) _PDFIO_PUBLIC;
extern void pdfioFileSetCreator(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC; extern void pdfioFileSetCreator(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetKeywords(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC; extern void pdfioFileSetKeywords(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetLanguage(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetModificationDate(pdfio_file_t *pdf, time_t value) _PDFIO_PUBLIC; extern void pdfioFileSetModificationDate(pdfio_file_t *pdf, time_t value) _PDFIO_PUBLIC;
extern bool pdfioFileSetPermissions(pdfio_file_t *pdf, pdfio_permission_t permissions, pdfio_encryption_t encryption, const char *owner_password, const char *user_password) _PDFIO_PUBLIC; extern bool pdfioFileSetPermissions(pdfio_file_t *pdf, pdfio_permission_t permissions, pdfio_encryption_t encryption, const char *owner_password, const char *user_password) _PDFIO_PUBLIC;
extern void pdfioFileSetSubject(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC; extern void pdfioFileSetSubject(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
@@ -236,7 +238,17 @@ extern const char *pdfioObjGetType(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern pdfio_stream_t *pdfioObjOpenStream(pdfio_obj_t *obj, bool decode) _PDFIO_PUBLIC; extern pdfio_stream_t *pdfioObjOpenStream(pdfio_obj_t *obj, bool decode) _PDFIO_PUBLIC;
extern bool pdfioPageCopy(pdfio_file_t *pdf, pdfio_obj_t *srcpage) _PDFIO_PUBLIC; extern bool pdfioPageCopy(pdfio_file_t *pdf, pdfio_obj_t *srcpage) _PDFIO_PUBLIC;
extern pdfio_array_t *pdfioPageGetArray(pdfio_obj_t *page, const char *key) _PDFIO_PUBLIC;
extern unsigned char *pdfioPageGetBinary(pdfio_obj_t *page, const char *key, size_t *length) _PDFIO_PUBLIC;
extern bool pdfioPageGetBoolean(pdfio_obj_t *page, const char *key) _PDFIO_PUBLIC;
extern time_t pdfioPageGetDate(pdfio_obj_t *page, const char *key) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioPageGetDict(pdfio_obj_t *page, const char *key) _PDFIO_PUBLIC;
extern const char *pdfioPageGetName(pdfio_obj_t *page, const char *key) _PDFIO_PUBLIC;
extern size_t pdfioPageGetNumStreams(pdfio_obj_t *page) _PDFIO_PUBLIC; extern size_t pdfioPageGetNumStreams(pdfio_obj_t *page) _PDFIO_PUBLIC;
extern double pdfioPageGetNumber(pdfio_obj_t *page, const char *key) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioPageGetObj(pdfio_obj_t *page, const char *key) _PDFIO_PUBLIC;
extern pdfio_rect_t *pdfioPageGetRect(pdfio_obj_t *page, const char *key, pdfio_rect_t *rect) _PDFIO_PUBLIC;
extern const char *pdfioPageGetString(pdfio_obj_t *page, const char *key) _PDFIO_PUBLIC;
extern pdfio_stream_t *pdfioPageOpenStream(pdfio_obj_t *page, size_t n, bool decode) _PDFIO_PUBLIC; extern pdfio_stream_t *pdfioPageOpenStream(pdfio_obj_t *page, size_t n, bool decode) _PDFIO_PUBLIC;
extern bool pdfioStreamClose(pdfio_stream_t *st) _PDFIO_PUBLIC; extern bool pdfioStreamClose(pdfio_stream_t *st) _PDFIO_PUBLIC;

View File

@@ -11,3 +11,4 @@ Cflags: @PKGCONFIG_CFLAGS@
Libs: @PKGCONFIG_LIBS@ Libs: @PKGCONFIG_LIBS@
Libs.private: @PKGCONFIG_LIBS_PRIVATE@ Libs.private: @PKGCONFIG_LIBS_PRIVATE@
Requires: @PKGCONFIG_REQUIRES@ Requires: @PKGCONFIG_REQUIRES@
Requires.private: @PKGCONFIG_REQUIRES_PRIVATE@

View File

@@ -87,6 +87,7 @@
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>ttf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
@@ -101,6 +102,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>ttf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
@@ -115,6 +117,7 @@
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>ttf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>HAVE_LIBPNG;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>HAVE_LIBPNG;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
@@ -130,6 +133,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>ttf;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>HAVE_LIBPNG;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>HAVE_LIBPNG;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
@@ -145,7 +149,8 @@
<ClInclude Include="pdfio-content.h" /> <ClInclude Include="pdfio-content.h" />
<ClInclude Include="pdfio-private.h" /> <ClInclude Include="pdfio-private.h" />
<ClInclude Include="pdfio.h" /> <ClInclude Include="pdfio.h" />
<ClInclude Include="ttf.h" /> <ClInclude Include="ttf\ttf.h" />
<ClInclude Include="ttf\ttf-private.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="pdfio-aes.c" /> <ClCompile Include="pdfio-aes.c" />
@@ -155,6 +160,7 @@
<ClCompile Include="pdfio-crypto.c" /> <ClCompile Include="pdfio-crypto.c" />
<ClCompile Include="pdfio-dict.c" /> <ClCompile Include="pdfio-dict.c" />
<ClCompile Include="pdfio-file.c" /> <ClCompile Include="pdfio-file.c" />
<ClCompile Include="pdfio-lzw.c" />
<ClCompile Include="pdfio-md5.c" /> <ClCompile Include="pdfio-md5.c" />
<ClCompile Include="pdfio-object.c" /> <ClCompile Include="pdfio-object.c" />
<ClCompile Include="pdfio-page.c" /> <ClCompile Include="pdfio-page.c" />
@@ -164,7 +170,8 @@
<ClCompile Include="pdfio-string.c" /> <ClCompile Include="pdfio-string.c" />
<ClCompile Include="pdfio-token.c" /> <ClCompile Include="pdfio-token.c" />
<ClCompile Include="pdfio-value.c" /> <ClCompile Include="pdfio-value.c" />
<ClCompile Include="ttf.c" /> <ClCompile Include="ttf\ttf-cache.c" />
<ClCompile Include="ttf\ttf-file.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />

View File

@@ -22,8 +22,11 @@
273440CD263D727800FBFD63 /* pdfio-page.c in Sources */ = {isa = PBXBuildFile; fileRef = 273440C2263D727800FBFD63 /* pdfio-page.c */; }; 273440CD263D727800FBFD63 /* pdfio-page.c in Sources */ = {isa = PBXBuildFile; fileRef = 273440C2263D727800FBFD63 /* pdfio-page.c */; };
273440D8263D72E100FBFD63 /* testpdfio.c in Sources */ = {isa = PBXBuildFile; fileRef = 273440D7263D72E100FBFD63 /* testpdfio.c */; }; 273440D8263D72E100FBFD63 /* testpdfio.c in Sources */ = {isa = PBXBuildFile; fileRef = 273440D7263D72E100FBFD63 /* testpdfio.c */; };
273440E4263DD7EA00FBFD63 /* pdfio-token.c in Sources */ = {isa = PBXBuildFile; fileRef = 273440E3263DD7EA00FBFD63 /* pdfio-token.c */; }; 273440E4263DD7EA00FBFD63 /* pdfio-token.c in Sources */ = {isa = PBXBuildFile; fileRef = 273440E3263DD7EA00FBFD63 /* pdfio-token.c */; };
279E1035267D043B00D3A349 /* ttf.h in Headers */ = {isa = PBXBuildFile; fileRef = 279E1033267D043B00D3A349 /* ttf.h */; }; 273707A52F299C3100953C95 /* pdfio-lzw.c in Sources */ = {isa = PBXBuildFile; fileRef = 273707A42F299C3100953C95 /* pdfio-lzw.c */; };
279E1036267D043B00D3A349 /* ttf.c in Sources */ = {isa = PBXBuildFile; fileRef = 279E1034267D043B00D3A349 /* ttf.c */; }; 2741C9A22F05872C002D93F2 /* ttf-cache.c in Sources */ = {isa = PBXBuildFile; fileRef = 2741C99F2F05872C002D93F2 /* ttf-cache.c */; };
2741C9A32F05872C002D93F2 /* ttf-file.c in Sources */ = {isa = PBXBuildFile; fileRef = 2741C9A02F05872C002D93F2 /* ttf-file.c */; };
2741C9A42F05872C002D93F2 /* ttf-private.h in Headers */ = {isa = PBXBuildFile; fileRef = 2741C9A12F05872C002D93F2 /* ttf-private.h */; };
2741C9A52F05872C002D93F2 /* ttf.h in Headers */ = {isa = PBXBuildFile; fileRef = 2741C99E2F05872C002D93F2 /* ttf.h */; };
279E103B267D04E600D3A349 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 279E103A267D04E600D3A349 /* libz.tbd */; }; 279E103B267D04E600D3A349 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 279E103A267D04E600D3A349 /* libz.tbd */; };
27CF90442711DFFE00E50FE4 /* pdfio-aes.c in Sources */ = {isa = PBXBuildFile; fileRef = 27CF90432711DFFE00E50FE4 /* pdfio-aes.c */; }; 27CF90442711DFFE00E50FE4 /* pdfio-aes.c in Sources */ = {isa = PBXBuildFile; fileRef = 27CF90432711DFFE00E50FE4 /* pdfio-aes.c */; };
27ECBD8926419DAB0025312A /* libpdfio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 273440B0263D6FE200FBFD63 /* libpdfio.a */; }; 27ECBD8926419DAB0025312A /* libpdfio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 273440B0263D6FE200FBFD63 /* libpdfio.a */; };
@@ -82,8 +85,11 @@
273440E1263D73A300FBFD63 /* pdfio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = pdfio.html; path = doc/pdfio.html; sourceTree = "<group>"; }; 273440E1263D73A300FBFD63 /* pdfio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; name = pdfio.html; path = doc/pdfio.html; sourceTree = "<group>"; };
273440E2263D73A300FBFD63 /* pdfio.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = pdfio.3; path = doc/pdfio.3; sourceTree = "<group>"; }; 273440E2263D73A300FBFD63 /* pdfio.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = pdfio.3; path = doc/pdfio.3; sourceTree = "<group>"; };
273440E3263DD7EA00FBFD63 /* pdfio-token.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-token.c"; sourceTree = "<group>"; }; 273440E3263DD7EA00FBFD63 /* pdfio-token.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-token.c"; sourceTree = "<group>"; };
279E1033267D043B00D3A349 /* ttf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ttf.h; sourceTree = "<group>"; }; 273707A42F299C3100953C95 /* pdfio-lzw.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = "pdfio-lzw.c"; sourceTree = "<group>"; };
279E1034267D043B00D3A349 /* ttf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ttf.c; sourceTree = "<group>"; }; 2741C99E2F05872C002D93F2 /* ttf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ttf.h; path = ttf/ttf.h; sourceTree = "<group>"; };
2741C99F2F05872C002D93F2 /* ttf-cache.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "ttf-cache.c"; path = "ttf/ttf-cache.c"; sourceTree = "<group>"; };
2741C9A02F05872C002D93F2 /* ttf-file.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "ttf-file.c"; path = "ttf/ttf-file.c"; sourceTree = "<group>"; };
2741C9A12F05872C002D93F2 /* ttf-private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "ttf-private.h"; path = "ttf/ttf-private.h"; sourceTree = "<group>"; };
279E103A267D04E600D3A349 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; 279E103A267D04E600D3A349 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
27CF90432711DFFE00E50FE4 /* pdfio-aes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-aes.c"; sourceTree = "<group>"; }; 27CF90432711DFFE00E50FE4 /* pdfio-aes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-aes.c"; sourceTree = "<group>"; };
27F2F05D2710BE92008ECD36 /* pdfio-md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-md5.c"; sourceTree = "<group>"; }; 27F2F05D2710BE92008ECD36 /* pdfio-md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-md5.c"; sourceTree = "<group>"; };
@@ -170,6 +176,7 @@
27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */, 27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */,
273440BE263D727800FBFD63 /* pdfio-dict.c */, 273440BE263D727800FBFD63 /* pdfio-dict.c */,
273440BD263D727800FBFD63 /* pdfio-file.c */, 273440BD263D727800FBFD63 /* pdfio-file.c */,
273707A42F299C3100953C95 /* pdfio-lzw.c */,
27F2F05D2710BE92008ECD36 /* pdfio-md5.c */, 27F2F05D2710BE92008ECD36 /* pdfio-md5.c */,
273440BC263D727800FBFD63 /* pdfio-object.c */, 273440BC263D727800FBFD63 /* pdfio-object.c */,
273440C2263D727800FBFD63 /* pdfio-page.c */, 273440C2263D727800FBFD63 /* pdfio-page.c */,
@@ -179,8 +186,10 @@
273440B9263D727800FBFD63 /* pdfio-string.c */, 273440B9263D727800FBFD63 /* pdfio-string.c */,
273440E3263DD7EA00FBFD63 /* pdfio-token.c */, 273440E3263DD7EA00FBFD63 /* pdfio-token.c */,
273440C0263D727800FBFD63 /* pdfio-value.c */, 273440C0263D727800FBFD63 /* pdfio-value.c */,
279E1034267D043B00D3A349 /* ttf.c */, 2741C99E2F05872C002D93F2 /* ttf.h */,
279E1033267D043B00D3A349 /* ttf.h */, 2741C99F2F05872C002D93F2 /* ttf-cache.c */,
2741C9A02F05872C002D93F2 /* ttf-file.c */,
2741C9A12F05872C002D93F2 /* ttf-private.h */,
); );
name = Library; name = Library;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -209,10 +218,11 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
27FCBDE42D19F9B300485EEE /* pdfio-base-font-widths.h in Headers */, 27FCBDE42D19F9B300485EEE /* pdfio-base-font-widths.h in Headers */,
2741C9A42F05872C002D93F2 /* ttf-private.h in Headers */,
2741C9A52F05872C002D93F2 /* ttf.h in Headers */,
273440CC263D727800FBFD63 /* pdfio.h in Headers */, 273440CC263D727800FBFD63 /* pdfio.h in Headers */,
271EA706265B2B1000ACDD39 /* pdfio-content.h in Headers */, 271EA706265B2B1000ACDD39 /* pdfio-content.h in Headers */,
273440C3263D727800FBFD63 /* pdfio-private.h in Headers */, 273440C3263D727800FBFD63 /* pdfio-private.h in Headers */,
279E1035267D043B00D3A349 /* ttf.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -261,7 +271,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
BuildIndependentTargetsInParallel = YES; BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1600; LastUpgradeCheck = 2600;
TargetAttributes = { TargetAttributes = {
273440AF263D6FE200FBFD63 = { 273440AF263D6FE200FBFD63 = {
CreatedOnToolsVersion = 12.5; CreatedOnToolsVersion = 12.5;
@@ -295,14 +305,16 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
279E1036267D043B00D3A349 /* ttf.c in Sources */,
273440C9263D727800FBFD63 /* pdfio-dict.c in Sources */, 273440C9263D727800FBFD63 /* pdfio-dict.c in Sources */,
273440C8263D727800FBFD63 /* pdfio-file.c in Sources */, 273440C8263D727800FBFD63 /* pdfio-file.c in Sources */,
273440CB263D727800FBFD63 /* pdfio-value.c in Sources */, 273440CB263D727800FBFD63 /* pdfio-value.c in Sources */,
273707A52F299C3100953C95 /* pdfio-lzw.c in Sources */,
273440CA263D727800FBFD63 /* pdfio-stream.c in Sources */, 273440CA263D727800FBFD63 /* pdfio-stream.c in Sources */,
273440CD263D727800FBFD63 /* pdfio-page.c in Sources */, 273440CD263D727800FBFD63 /* pdfio-page.c in Sources */,
27F2F0622710BE92008ECD36 /* pdfio-crypto.c in Sources */, 27F2F0622710BE92008ECD36 /* pdfio-crypto.c in Sources */,
27F2F0642711243D008ECD36 /* pdfio-sha256.c in Sources */, 27F2F0642711243D008ECD36 /* pdfio-sha256.c in Sources */,
2741C9A22F05872C002D93F2 /* ttf-cache.c in Sources */,
2741C9A32F05872C002D93F2 /* ttf-file.c in Sources */,
273440C5263D727800FBFD63 /* pdfio-array.c in Sources */, 273440C5263D727800FBFD63 /* pdfio-array.c in Sources */,
273440E4263DD7EA00FBFD63 /* pdfio-token.c in Sources */, 273440E4263DD7EA00FBFD63 /* pdfio-token.c in Sources */,
273440C7263D727800FBFD63 /* pdfio-object.c in Sources */, 273440C7263D727800FBFD63 /* pdfio-object.c in Sources */,
@@ -353,12 +365,14 @@
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_IMPLICIT_FALLTHROUGH = NO;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
@@ -377,7 +391,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.1.2; CURRENT_PROJECT_VERSION = 1.6.1;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -388,10 +402,16 @@
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)", "$(inherited)",
"DEBUG=1", "DEBUG=1",
"HAVE_LIBPNG=1",
"HAVE_TM_GMTOFF=1",
"HAVE_TIMEGM=1",
); );
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
@@ -399,16 +419,29 @@
GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
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;
HEADER_SEARCH_PATHS = (
/usr/local/include,
/opt/local/include,
/opt/homebrew/include,
);
MACOSX_DEPLOYMENT_TARGET = 11.0; 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;
OTHER_LDFLAGS = (
"-L/usr/local/lib",
"-L/opt/local/lib",
"-L/opt/homebrew/lib",
"-lpng16",
);
RUN_CLANG_STATIC_ANALYZER = YES; RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx; SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Debug; name = Debug;
@@ -432,12 +465,14 @@
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_IMPLICIT_FALLTHROUGH = NO;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
@@ -456,7 +491,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.1.2; CURRENT_PROJECT_VERSION = 1.6.1;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@@ -465,10 +500,17 @@
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"HAVE_LIBPNG=1",
"HAVE_TM_GMTOFF=1",
"HAVE_TIMEGM=1",
);
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
@@ -476,15 +518,28 @@
GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
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;
HEADER_SEARCH_PATHS = (
/usr/local/include,
/opt/local/include,
/opt/homebrew/include,
);
MACOSX_DEPLOYMENT_TARGET = 11.0; MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
"-L/usr/local/lib",
"-L/opt/local/lib",
"-L/opt/homebrew/lib",
"-lpng16",
);
RUN_CLANG_STATIC_ANALYZER = YES; RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx; SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Release; name = Release;

View File

@@ -1,9 +1,23 @@
LIBRARY pdfio1 LIBRARY pdfio1
VERSION 1.5 VERSION 1.7
EXPORTS EXPORTS
_pdfio_strlcpy
_pdfio_strtod
_pdfio_utf16cpy
_pdfio_vsnprintf
_pdfioArrayDebug
_pdfioArrayDecrypt
_pdfioArrayDelete
_pdfioArrayGetValue
_pdfioArrayRead
_pdfioArrayWrite
_pdfioCryptoAESDecrypt _pdfioCryptoAESDecrypt
_pdfioCryptoAESEncrypt _pdfioCryptoAESEncrypt
_pdfioCryptoAESInit _pdfioCryptoAESInit
_pdfioCryptoLock
_pdfioCryptoMakeRandom
_pdfioCryptoMakeReader
_pdfioCryptoMakeWriter
_pdfioCryptoMD5Append _pdfioCryptoMD5Append
_pdfioCryptoMD5Finish _pdfioCryptoMD5Finish
_pdfioCryptoMD5Init _pdfioCryptoMD5Init
@@ -12,9 +26,56 @@ _pdfioCryptoRC4Init
_pdfioCryptoSHA256Append _pdfioCryptoSHA256Append
_pdfioCryptoSHA256Finish _pdfioCryptoSHA256Finish
_pdfioCryptoSHA256Init _pdfioCryptoSHA256Init
_pdfioCryptoUnlock
_pdfioDictDebug
_pdfioDictDecrypt
_pdfioDictDelete
_pdfioDictGetValue
_pdfioDictRead
_pdfioDictSetValue
_pdfioDictWrite
_pdfioFileAddMappedObj
_pdfioFileAddPage
_pdfioFileConsume
_pdfioFileCreateObj
_pdfioFileDefaultError
_pdfioFileError
_pdfioFileFindMappedObj
_pdfioFileFlush
_pdfioFileGetChar
_pdfioFileGets
_pdfioFilePeek
_pdfioFilePrintf
_pdfioFilePuts
_pdfioFileRead
_pdfioFileSeek
_pdfioFileTell
_pdfioFileWrite
_pdfioLZWCreate
_pdfioLZWDelete
_pdfioLZWInflate
_pdfioObjDelete
_pdfioObjGetExtension
_pdfioObjLoad
_pdfioObjSetExtension
_pdfioObjWriteHeader
_pdfioStreamCreate
_pdfioStreamOpen
_pdfioStringAllocBuffer
_pdfioStringFreeBuffer
_pdfioStringIsAllocated
_pdfioTokenClear
_pdfioTokenFlush
_pdfioTokenGet
_pdfioTokenInit _pdfioTokenInit
_pdfioTokenPush
_pdfioTokenRead
_pdfioValueCopy
_pdfioValueDebug _pdfioValueDebug
_pdfioValueDecrypt
_pdfioValueDelete
_pdfioValueRead _pdfioValueRead
_pdfioValueWrite
pdfioArrayAppendArray pdfioArrayAppendArray
pdfioArrayAppendBinary pdfioArrayAppendBinary
pdfioArrayAppendBoolean pdfioArrayAppendBoolean
@@ -43,8 +104,10 @@ pdfioArrayGetSize
pdfioArrayGetString pdfioArrayGetString
pdfioArrayGetType pdfioArrayGetType
pdfioArrayRemove pdfioArrayRemove
pdfioContentBeginMarked
pdfioContentClip pdfioContentClip
pdfioContentDrawImage pdfioContentDrawImage
pdfioContentEndMarked
pdfioContentFill pdfioContentFill
pdfioContentFillAndStroke pdfioContentFillAndStroke
pdfioContentMatrixConcat pdfioContentMatrixConcat
@@ -98,8 +161,8 @@ pdfioContentTextNewLineShow
pdfioContentTextNewLineShowf pdfioContentTextNewLineShowf
pdfioContentTextNextLine pdfioContentTextNextLine
pdfioContentTextShow pdfioContentTextShow
pdfioContentTextShowJustified
pdfioContentTextShowf pdfioContentTextShowf
pdfioContentTextShowJustified
pdfioDictClear pdfioDictClear
pdfioDictCopy pdfioDictCopy
pdfioDictCreate pdfioDictCreate
@@ -110,8 +173,8 @@ pdfioDictGetDate
pdfioDictGetDict pdfioDictGetDict
pdfioDictGetKey pdfioDictGetKey
pdfioDictGetName pdfioDictGetName
pdfioDictGetNumPairs
pdfioDictGetNumber pdfioDictGetNumber
pdfioDictGetNumPairs
pdfioDictGetObj pdfioDictGetObj
pdfioDictGetRect pdfioDictGetRect
pdfioDictGetString pdfioDictGetString
@@ -129,10 +192,12 @@ pdfioDictSetObj
pdfioDictSetRect pdfioDictSetRect
pdfioDictSetString pdfioDictSetString
pdfioDictSetStringf pdfioDictSetStringf
pdfioFileAddOutputIntent
pdfioFileClose pdfioFileClose
pdfioFileCreate pdfioFileCreate
pdfioFileCreateArrayObj pdfioFileCreateArrayObj
pdfioFileCreateFontObjFromBase pdfioFileCreateFontObjFromBase
pdfioFileCreateFontObjFromData
pdfioFileCreateFontObjFromFile pdfioFileCreateFontObjFromFile
pdfioFileCreateICCObjFromData pdfioFileCreateICCObjFromData
pdfioFileCreateICCObjFromFile pdfioFileCreateICCObjFromFile
@@ -152,6 +217,7 @@ pdfioFileGetCreationDate
pdfioFileGetCreator pdfioFileGetCreator
pdfioFileGetID pdfioFileGetID
pdfioFileGetKeywords pdfioFileGetKeywords
pdfioFileGetLanguage
pdfioFileGetModificationDate pdfioFileGetModificationDate
pdfioFileGetName pdfioFileGetName
pdfioFileGetNumObjs pdfioFileGetNumObjs
@@ -168,6 +234,7 @@ pdfioFileSetAuthor
pdfioFileSetCreationDate pdfioFileSetCreationDate
pdfioFileSetCreator pdfioFileSetCreator
pdfioFileSetKeywords pdfioFileSetKeywords
pdfioFileSetLanguage
pdfioFileSetModificationDate pdfioFileSetModificationDate
pdfioFileSetPermissions pdfioFileSetPermissions
pdfioFileSetSubject pdfioFileSetSubject
@@ -191,7 +258,17 @@ pdfioPageCopy
pdfioPageDictAddColorSpace pdfioPageDictAddColorSpace
pdfioPageDictAddFont pdfioPageDictAddFont
pdfioPageDictAddImage pdfioPageDictAddImage
pdfioPageGetArray
pdfioPageGetBinary
pdfioPageGetBoolean
pdfioPageGetDate
pdfioPageGetDict
pdfioPageGetName
pdfioPageGetNumber
pdfioPageGetNumStreams pdfioPageGetNumStreams
pdfioPageGetObj
pdfioPageGetRect
pdfioPageGetString
pdfioPageOpenStream pdfioPageOpenStream
pdfioStreamClose pdfioStreamClose
pdfioStreamConsume pdfioStreamConsume

View File

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

View File

@@ -3,7 +3,7 @@
<metadata> <metadata>
<id>pdfio_native.redist</id> <id>pdfio_native.redist</id>
<title>PDFio Library for VS2019+</title> <title>PDFio Library for VS2019+</title>
<version>1.5.2</version> <version>1.7.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>

44
test-corpus.sh Executable file
View File

@@ -0,0 +1,44 @@
#!/bin/sh
#
# Script to test PDFio against a directory of PDF files.
#
# Copyright © 2025-2026 by Michael R Sweet.
#
# Licensed under Apache License v2.0. See the file "LICENSE" for more
# information.
#
# Usage:
#
# ./test-corpus.sh DIRECTORY
#
if test $# = 0; then
echo "Usage: ./test-corpus.sh DIRECTORY"
exit 1
fi
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
ac_n=-n
ac_c=
else
ac_n=
ac_c='\c'
fi
for file in $(find "$@" -name \*.pdf -print); do
# Run testpdfio to test loading the file...
echo $ac_n "\r$file: $ac_c"
./testpdfio --verbose $file >/dev/null 2>$file.log
if test $? = 0; then
# Passed
echo PASS
rm -f $file.log
else
# Failed, preserve log and write to stdout...
echo FAIL
cat $file.log
echo ""
fi
done

292
test-internal.h Normal file
View File

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

Binary file not shown.

View File

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

BIN
testfiles/color-8bit-i.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

BIN
testfiles/color.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
testfiles/gray-4bit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
testfiles/gray.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
testfiles/pdfio-1bit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
testfiles/pdfio-2bit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
testfiles/pdfio-4bit-it.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
testfiles/pdfio-4bit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
testfiles/pdfio-8bit-a.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
testfiles/pdfio-8bit-i.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
testfiles/pdfio-8bit-it.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
testfiles/pdfio-color.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
testfiles/pdfio-gray.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
testfiles/pdfio-rgba.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
testfiles/pdfio-rgba.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

File diff suppressed because it is too large Load Diff

305
testttf.c
View File

@@ -1,305 +0,0 @@
//
// Unit test program for TTF library
//
// https://github.com/michaelrsweet/ttf
//
// Copyright © 2018-2024 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);
fflush(stdout);
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);
}

1
ttf Submodule

Submodule ttf added at f2e6f45ab3

2100
ttf.c

File diff suppressed because it is too large Load Diff

110
ttf.h
View File

@@ -1,110 +0,0 @@
//
// Header file for TTF library
//
// https://github.com/michaelrsweet/ttf
//
// Copyright © 2018-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
#ifndef TTF_H
# define TTF_H
# include <stddef.h>
# include <stdbool.h>
# include <sys/types.h>
# ifdef __cplusplus
extern "C" {
# endif // __cplusplus
//
// Types...
//
typedef struct _ttf_s ttf_t; // Font object
typedef void (*ttf_err_cb_t)(void *data, const char *message);
// Font error callback
typedef enum ttf_stretch_e // Font stretch
{
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
} ttf_stretch_t;
typedef enum ttf_style_e // Font style
{
TTF_STYLE_NORMAL, // Normal font
TTF_STYLE_ITALIC, // Italic font
TTF_STYLE_OBLIQUE // Oblique (angled) font
} ttf_style_t;
typedef enum ttf_variant_e // Font variant
{
TTF_VARIANT_NORMAL, // Normal font
TTF_VARIANT_SMALL_CAPS // Font whose lowercase letters are small capitals
} ttf_variant_t;
typedef enum ttf_weight_e // Font weight
{
TTF_WEIGHT_100 = 100, // Weight 100 (Thin)
TTF_WEIGHT_200 = 200, // Weight 200 (Extra/Ultra-Light)
TTF_WEIGHT_300 = 300, // Weight 300 (Light)
TTF_WEIGHT_400 = 400, // Weight 400 (Normal/Regular)
TTF_WEIGHT_500 = 500, // Weight 500 (Medium)
TTF_WEIGHT_600 = 600, // Weight 600 (Semi/Demi-Bold)
TTF_WEIGHT_700 = 700, // Weight 700 (Bold)
TTF_WEIGHT_800 = 800, // Weight 800 (Extra/Ultra-Bold)
TTF_WEIGHT_900 = 900 // Weight 900 (Black/Heavy)
} ttf_weight_t;
typedef struct ttf_rect_s // Bounding rectangle
{
float left; // Left offset
float top; // Top offset
float right; // Right offset
float bottom; // Bottom offset
} ttf_rect_t;
//
// Functions...
//
extern ttf_t *ttfCreate(const char *filename, size_t idx, ttf_err_cb_t err_cb, void *err_data);
extern void ttfDelete(ttf_t *font);
extern int ttfGetAscent(ttf_t *font);
extern ttf_rect_t *ttfGetBounds(ttf_t *font, ttf_rect_t *bounds);
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 int ttfGetDescent(ttf_t *font);
extern ttf_rect_t *ttfGetExtents(ttf_t *font, float size, const char *s, ttf_rect_t *extents);
extern const char *ttfGetFamily(ttf_t *font);
extern float ttfGetItalicAngle(ttf_t *font);
extern int ttfGetMaxChar(ttf_t *font);
extern int ttfGetMinChar(ttf_t *font);
extern size_t ttfGetNumFonts(ttf_t *font);
extern const char *ttfGetPostScriptName(ttf_t *font);
extern ttf_stretch_t ttfGetStretch(ttf_t *font);
extern ttf_style_t ttfGetStyle(ttf_t *font);
extern const char *ttfGetVersion(ttf_t *font);
extern int ttfGetWidth(ttf_t *font, int ch);
extern ttf_weight_t ttfGetWeight(ttf_t *font);
extern int ttfGetXHeight(ttf_t *font);
extern bool ttfIsFixedPitch(ttf_t *font);
# ifdef __cplusplus
}
# endif // __cplusplus
#endif // !TTF_H