374 Commits

Author SHA1 Message Date
ebd5aab39b Fix handling of 0-length streams (Issue #111) 2025-03-27 12:44:42 -04:00
71d33c03ff Add PDF merge example. 2025-03-27 11:48:41 -04:00
cfe91b4ea2 Fix output of special characters in name values (Issue #106)
Fix output of special characters in string values (Issue #107)
Fi output of large integers in dictionaries (Issue #108)

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

Update DLL exports file.

Update docos and changelog.
2025-02-13 18:56:43 -05:00
4f880bc0c1 Merge pull request #88 from tlaronde/info
Extend by adding pdfioGetModDate and extend the pdfioinfo example
2025-02-13 18:47:28 -05:00
d032483ed4 Merge branch 'michaelrsweet:master' into info 2025-02-12 15:54:47 +01:00
b2fc82f3a8 Update CI dependencies.
Add libpng_native to VC++ projects.
2025-02-12 09:25:57 -05:00
b81d01f319 Fix builds without libpng. 2025-02-11 22:59:23 -05:00
1b35321615 Add PngSuite to testpdfio (Issue #90) 2025-02-11 22:54:59 -05:00
990342f2a5 Add masking, color space, and variable bit depth support (Issue #90) 2025-02-11 22:07:02 -05:00
7f5fc456bc Fix image dictionary for new libpng-based PNG image support (Issue #90) 2025-02-11 20:23:59 -05:00
7c527cc908 Fix pdfio-512.png file. 2025-02-11 20:23:28 -05:00
41d17fc4e3 Update version number in NuGet files. 2025-02-11 20:23:17 -05:00
4e89137689 Use pkg-config for compiler options.
Fix some issues with the image2pdf example code.
2025-02-11 20:22:36 -05:00
e686669b9d Save work on libpng PNG loader (Issue #90) 2025-02-10 21:25:59 -05:00
1e5cc6ffd5 Do cleanup of PNG loading code, in preparation of adding full support (Issue #90) 2025-02-10 15:54:29 -05:00
4f1b373232 Add PngSuite from http://www.schaik.com/pngsuite/ for testing PNG image
support (Issue #90)
2025-02-10 11:04:39 -05:00
6f4bfe107f Refactor pdfioFileCreateImageObjFromData to do the image writing in a separate
function (Issue #90)
2025-02-10 10:28:28 -05:00
5b5de3aff6 Update pdf2txt example to support font encodings. 2025-01-28 14:26:33 -05:00
48fe8d1bc9 Bump version. 2025-01-24 15:31:31 -05:00
a4026bfe00 Prep for release. 2025-01-24 15:30:59 -05:00
1e945cb750 Add LICENSE files to example install list. 2025-01-24 14:44:44 -05:00
4cb4ceaadd Update docos with fixed codedoc. 2025-01-24 14:42:41 -05:00
cca7383c73 Fix support for UTF-16 string values in dictionaries (Issue #92)
Specifically to support Unicode Title and Author values.
2025-01-24 10:43:41 -05:00
6c68b9fa5a Add URLs and copyrights for Code 128 font and ProPhoto ICC profile (Issue #91) 2025-01-24 09:56:51 -05:00
dd7ed67ec1 Update makesrcdist to validate CHANGES.md. 2025-01-23 15:34:43 -05:00
9e2f3aba10 Fix reading of compressed object streams (Issue #92) 2025-01-23 15:27:22 -05:00
fca4dbd395 Make sure we have license files for the example fonts (Issue #91) 2025-01-23 13:03:23 -05:00
41ac7a0b4b Changelog. 2025-01-18 09:45:29 -05:00
5fc571b711 Merge pull request #89 from vlasovsoft1979/master
Fix undefined behavior in _pdfioFileSeek
2025-01-18 09:42:58 -05:00
acf27d29c6 Fix undefined behavior 2025-01-18 13:56:25 +03:00
8b2b013b36 Extend by adding pdfioGetModDate and extend the pdfioinfo example
When exploring a PDF, it may be convenient to have the typical
informations delivered by some "Document Properties"---and some more
about the MediaBox(es).

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

Signed-off-by: Thierry LARONDE <tlaronde@kergis.com>
2025-01-18 11:25:36 +01:00
026f653e07 Fix loading of last 1024 bytes for small PDF files (Issue #87) 2025-01-17 16:58:33 -05:00
3bc041e6d3 Delay loading of the Info object and clean up the pdfioinfo example (Issue #87) 2025-01-17 16:50:30 -05:00
fbd61d1fe9 Bump copyright and version, changelog for example makefile fix. 2025-01-10 14:54:11 -05:00
ee2794199c Merge pull request #86 from tlaronde/master
examples/Makefile: libm is not added by default by all
2025-01-10 14:50:44 -05:00
31c3400f23 examples/Makefile: libm is not added by default by all
-lm has to be added for system/compilers that don't add the lib by
default (the case on NetBSD).
2025-01-10 20:18:06 +01:00
6d65a609e5 Update documentation and examples makefile. 2024-12-26 15:12:56 -05:00
e96f9bfa6b Fix compiler warning and update Xcode project. 2024-12-23 15:07:32 -05:00
10c15fc281 Bump NuGet package versions. 2024-12-22 21:33:35 -05:00
fd8427d68a Add pdf2text example docos, install examples to doc directory. 2024-12-22 21:29:32 -05:00
ed1421287f Move pdfiototext to examples. 2024-12-22 19:00:17 -05:00
aa91b141a8 Finalize md2pdf example docos. 2024-12-22 12:09:03 -05:00
5dc68f3285 Save work on docos. 2024-12-21 23:20:36 -05:00
52b508bdd2 Block quote rendering changes. 2024-12-21 14:15:48 -05:00
41ebe39f3b Save work. 2024-12-21 14:04:27 -05:00
62df5f5c78 Add CODE_PADDING and use it for code blocks. 2024-12-21 12:16:36 -05:00
a1237db52c Use regular font for whitespace before monospace text. 2024-12-21 11:50:35 -05:00
a24fdee335 Fix an uninitialized pointer issue in format_block, and some margin issues on the top of the page. 2024-12-21 11:31:54 -05:00
e4081f2ba3 Add table-of-contents outline. 2024-12-20 07:18:10 -05:00
5bc7ebee2c Add actual example programs from prior examples, start documentation updates. 2024-12-19 16:43:21 -05:00
b872df5a1e Fix validation of date/time values (Issue #83) 2024-12-19 15:41:43 -05:00
53967552df Add some documentation on the code128 example.
Clean up the code128 code a bit and use the Helvetica font for the text.

Add support for writing to a PDF file on the command-line vs. just doing
redirection.
2024-12-15 22:52:03 -05:00
f8639fbd64 Add "need_bottom" argument to keep the following block on the same page. 2024-12-15 18:17:31 -05:00
9020e92928 Make sure we have room for at least two lines in a paragraph or code block at the bottom of the page. 2024-12-15 17:46:03 -05:00
48e6597337 Make block quote bar a thick orange so it stands out. 2024-12-15 17:35:16 -05:00
d4e3bbcf16 Doco updates. 2024-12-15 11:28:09 -05:00
2c8a996875 Add green bar next to block quote text (for notes). 2024-12-15 11:27:32 -05:00
3d6d9e3e3e Fix name of ZapfDingbats. 2024-12-15 11:27:18 -05:00
62fdf48ff9 Fix links for code text, background of code blocks. 2024-12-15 11:00:30 -05:00
294f5e07c5 Add support for internal hyperlinks. 2024-12-15 10:23:59 -05:00
4baafde74b Add support for remote URL links. 2024-12-14 19:56:45 -05:00
2d175fdf70 Drop Apache badge (not needed), rearrange functions in alphabetical order, and reset top margin for list items to 0. 2024-12-14 10:18:01 -05:00
56a0f290aa Start work on example documentation. 2024-12-13 23:12:48 -05:00
2e5319a623 Fix widths for base fonts (was converting back to UTF-8 instead of preserving
the CP-1252 character set.
2024-12-13 19:56:00 -05:00
d3d6683041 Add support for measuring base fonts (Issue #84)
Update md2pdf example code to support using embedded TrueType or PDF base
fonts.
2024-12-13 17:39:16 -05:00
0d08dd5f1b Add support for tables. 2024-12-13 15:17:25 -05:00
00c9905317 Update rendering of checkboxes. 2024-12-12 13:07:09 -05:00
b8b9d7ef8a Split out code block formatting and add light gray background behind code.
Add support for hard breaks.

Add (preliminary) support for checkboxes.
2024-12-12 11:44:53 -05:00
63cdb13b1b Fix image name property - forgot to call pdfioStringCreate* APIs for formatted
string...
2024-12-12 07:11:25 -05:00
72e55b5bd1 Refactor block formatter to split content into lines and render the lines.
Also cache the current font for the whole page.
2024-12-11 20:14:32 -05:00
dc65eb8d2f Save work on image support. 2024-12-11 15:37:03 -05:00
a39b01ec9c Add metadata support. 2024-12-10 18:53:51 -05:00
4b29c9a1c2 Add text color and optimize text groups into whole blocks.
Add UNICODE_VALUE define to allow toggling between Unicode and ISO-8859-1 modes.
2024-12-10 18:41:23 -05:00
5a4afad566 Save work on markdown formatting example code. 2024-12-10 16:35:12 -05:00
7a45adb7f5 Drop cert.py from cppcheck invocation. 2024-12-10 08:24:47 -05:00
45ac66874c Add readme for example programs. 2024-12-09 19:28:15 -05:00
eb9dad9b51 Add Code 128 example code. 2024-12-09 19:13:48 -05:00
2ecb9cfb2d Changelog. 2024-12-08 19:19:13 -05:00
91a467e55c Merge pull request #81 from vlasovsoft1979/ttf_h_size_t_error
Fixed compilation error MSVC 19.16.27039.0 32 bit
2024-12-08 19:18:46 -05:00
d705d7eb5d Fix reading PDF files whose trailer is missing a newline (Issue #80) 2024-12-08 19:14:58 -05:00
55745bcea8 Fixed compilation error MSVC 19.16.27039.0 32 bit 2024-12-08 22:56:25 +03:00
2ea99597cc Update Windows DLL exports. 2024-10-25 17:57:17 -04:00
a3a3512ed8 Update docos. 2024-10-25 17:50:51 -04:00
afac83530f Add pdfioDictGetKey and pdfioDictGetNumPairs APIs (Issue #63)
Add pdfioArrayRemove and pdfioDictClear APIs (Issue #74)
2024-10-25 17:48:19 -04:00
21ac2b52d1 Clean up updated docos (Issue #78) 2024-10-25 17:32:38 -04:00
21b8e3b06f Changelog. 2024-10-25 17:17:39 -04:00
91392a931f Changelog. 2024-10-25 17:17:38 -04:00
1d8bcf4d73 Start v1.4.0. 2024-10-25 17:17:38 -04:00
1e55779906 Merge pull request #78 from uddhavphatak/master
Update in documentation
2024-10-25 17:16:46 -04:00
0e45e49ea4 Merge pull request #76 from vlasovsoft1979/master
Get name from object
2024-10-25 17:14:59 -04:00
0ab291a78b Update pdfio.md 2024-10-21 20:37:25 +05:30
cac6d4891c Update pdfio.md 2024-10-21 19:52:34 +05:30
4f29ad89da Merge branch 'michaelrsweet:master' into master 2024-10-21 17:09:38 +05:30
9c04d1dc20 Update changelog. 2024-10-15 13:10:06 -04:00
335472023e Bump version in header. 2024-10-15 13:06:40 -04:00
25834e07ef Update pdfio.md
addition of lines requeested
2024-10-15 09:38:01 +05:30
2d2a7126d2 Update pdfio.md
updated doc
2024-10-14 13:34:27 +05:30
df1064ff39 Update pdfio.md 2024-10-14 13:20:44 +05:30
853fa4fe8f Update pdfio.md 2024-10-14 13:14:59 +05:30
2cadfd8a1e Update pdfio.md 2024-10-14 13:10:57 +05:30
f5d40a305e Update pdfio.md 2024-10-14 13:09:13 +05:30
eb5be57b4a Update pdfio.md
basics of pdf file
2024-10-14 13:06:06 +05:30
3de47ea63d Update pdfio.md
update documentation
2024-10-14 12:43:40 +05:30
8f2c47cb07 Make sure memory is freed on error conditions. 2024-10-09 15:32:48 -04:00
74dfefdcc1 Update documentation (Issue #77)
- Explain pdfioObjGetSubtype and pdfioObjGetType values
- Provide example code and documentation for accessing common page object values
2024-10-09 15:07:57 -04:00
ee31096019 PR comment 2024-09-27 20:38:15 +03:00
121b933307 minor 2024-09-25 18:44:34 +03:00
f4409146e3 minor 2024-09-25 18:42:38 +03:00
4312933409 pdfioFileCreateNameObj implemented 2024-09-25 18:40:36 +03:00
a19949834b PR comments 2024-09-25 18:06:17 +03:00
04c4f44324 Get name from simple object. For example, Image ColorSpace is the reference to other object. 2024-09-25 17:04:25 +03:00
206f75403a Add debug printfs. 2024-08-26 09:19:34 -04:00
7d22477917 Fix opening of certain encrypted PDF files (Issue #62) 2024-08-21 11:28:39 -04:00
7c3651671b Add NULL checks in the private debug APIs that testpdfio calls. 2024-08-21 09:22:58 -04:00
6cb661f0f4 Cleanup changelog. 2024-08-21 08:25:11 -04:00
7e01451b18 Merge 0-character font fix from TTF. 2024-08-21 08:22:31 -04:00
138f3955d1 Add --password option to PDFio test program. 2024-08-19 17:12:16 -04:00
82844ad2ce Merge TTF v1.0.0 source files. 2024-08-19 16:59:00 -04:00
d7cce4dfbc Merge TTF v1.0.0 source files. 2024-08-19 16:58:38 -04:00
1cec42f399 Bump version to 1.3.2. 2024-08-09 10:55:32 -04:00
f3f70e7877 Merge some TTF sanity check fixes from the TTF project. 2024-08-09 10:54:28 -04:00
90923c3818 Update DLL exports. 2024-08-05 21:55:32 -04:00
986cc512cd Bump NuGet project versions. 2024-08-05 21:50:18 -04:00
c35ddbec00 Changelog 2024-08-05 21:49:26 -04:00
e4e1c39578 Merge commit from fork
Add range checking to TTF loader.
2024-08-05 21:47:48 -04:00
1d4f77cab1 Add examples to documentation (Issue #69) 2024-08-05 21:44:56 -04:00
b035130cde Merge pull request #68 from devnibo/master
Update documentation
2024-08-05 19:56:40 -04:00
d6d5813b04 Update changelog with CVE number. 2024-08-05 16:34:12 -04:00
6492f210cf Bump version and changelog. 2024-08-05 10:23:51 -04:00
207062a996 Add size limiting for num_cmap and nGlyphs. 2024-08-05 10:16:00 -04:00
7d37abb0df Update documentation 2024-07-07 16:35:56 +02:00
0c1122b689 Prep for release. 2024-06-28 19:06:44 -04:00
d4f8dd46b5 Add Windows test script. 2024-06-28 19:00:51 -04:00
986c5f0438 Update docos. 2024-06-24 11:51:50 -04:00
a81907bdb9 Refactor get_info_string to rely on pdfioDictGetString to convert binary strings to regular ones. 2024-06-24 11:49:38 -04:00
63a7a2cdbd Add unit tests for new pdfioFileGetCatalog API (Issue #67)
Fix pdfioDictGetString to convert (formerly) encrypted binary strings to
regular strings.
2024-06-24 11:46:15 -04:00
f040cc41c2 Add #define guard to allow MingW to build PDFio; note that MingW is NOT a supported toolchain for PDFio (Issue #66) 2024-06-24 09:03:46 -04:00
23883268e3 Add pdfioFileGetCatalog function (Issue #67)
Refactor the pdfioFileCreateXxx functions to use a common (private) function to
handle creating/initializing the pdfio_file_t object and base file objects.

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

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

Look at the last 1k of the file to find the startxref marker.
2023-12-12 12:24:49 -05:00
038fd8686b Fix trailer dictionary handling (Issue #58)
Fix generation number handling for object 0 (Issue #59)
2023-12-11 19:56:00 -05:00
7084105dc4 Merge pull request #57 from eli-schwartz/pdfio-pc-redundancy
pdfio.pc: use -lm as specified in configure
2023-12-10 19:35:37 -05:00
9f06f22281 pdfio.pc: use -lm as specified in configure
It is already configured in, in the correct place. Currently, it is
listed twice in Libs.private, if --enable-shared is used. And it is
redundant if the build is static instead, since it is recorded in Libs.
2023-12-10 16:32:52 -05:00
cb6b493df6 Update configure script. 2023-12-10 15:38:35 -05:00
2753a82eb9 Merge pull request #56 from eli-schwartz/misspelled
fix misspelled variable: PKCONFIG
2023-12-10 15:38:12 -05:00
ddb8ddff9c fix misspelled variable: PKCONFIG
This prevented using pkg-config for zlib lookup.
2023-12-10 01:39:23 -05:00
c992b2ba89 Update the token reading code to protect against obvious format abuses.
Update the xref loading code to protect against looping xref tables.
2023-12-07 17:50:52 -05:00
ed723a46dc Make sure buffer is terminated on error. 2023-12-06 11:21:33 -05:00
6906a9a708 Fix docos for pdfioFileOpen. 2023-12-05 19:22:47 -05:00
6a381a55fe Update macOS build docos. 2023-12-05 18:41:26 -05:00
fc3580a948 Update build docos. 2023-12-05 18:39:20 -05:00
6b5c30b4be Remove PDFIO_ENCRYPTION_AES_256 from docos. 2023-12-05 14:30:41 -05:00
a0cdb261ff Update CONTRIBUTING docos. 2023-12-05 14:07:52 -05:00
34dbf6c2fe Documentation cleanup. 2023-12-05 13:49:58 -05:00
86d842167a Bring back mis-named pdfioContentTextNextLine. 2023-12-05 13:33:07 -05:00
16c8b830b8 Add pdfioFileCreateNumber/StringObj functions (Issue #14) 2023-12-05 08:16:41 -05:00
7ff051fc8b Add pdfioContentTextNewLineShow/f functions (Issue #24) 2023-12-05 07:49:49 -05:00
927452d1eb Changelog and exports updates. 2023-12-04 21:22:13 -05:00
f1ad982fd1 Update docos. 2023-12-04 21:21:18 -05:00
c188cb8dad Finish implementation of pdfioContentTextMeasure (Issue #17) 2023-12-04 21:20:51 -05:00
4919783da5 Save work on string measure support (Issue #17) 2023-12-04 18:54:33 -05:00
86281750e5 Fix man page installation to use mandir (Issue #55) 2023-12-04 08:50:28 -05:00
d92b72ed02 Add math library. 2023-12-03 20:26:27 -05:00
a195c023af Fix CI scripts. 2023-12-03 20:20:23 -05:00
43000ff01f Fix up targets. 2023-12-03 19:56:25 -05:00
c6f17cc20f Fix some warnings. 2023-12-03 19:23:36 -05:00
41146adbdf Adopt autoconf (Issue #54) 2023-12-03 19:16:34 -05:00
cd80c3037d Prep for 1.1.4 release. 2023-12-03 16:55:57 -05:00
97934ab995 Add ToUnicode map. 2023-11-19 20:49:30 -05:00
088646e1cf Drop the FEFF prefix on Unicode strings. 2023-11-19 07:30:17 -05:00
3f0aad7564 Fix another bug in the CMAPv4 code, and a bug in the unit test program. 2023-11-19 07:06:35 -05:00
d36df63b57 Fix a TrueType CMAP decoding bug and add a NotoSans-Regular test page. 2023-11-18 22:15:52 -05:00
a5dfac7495 Bump version. 2023-11-18 18:23:22 -05:00
0258384d53 Range check encrypted string length (Issue #52) 2023-11-18 18:22:11 -05:00
9fec2195d0 Update changelog. 2023-11-15 10:14:02 -05:00
8ccbdaed94 Update documentation. 2023-11-15 09:28:57 -05:00
4804db38a5 Fix missing ivlen initializer for 40-bit RC4 (Issue #51) 2023-11-15 08:43:07 -05:00
ddd984215a Save work (debug printfs, etc.) 2023-11-15 08:38:47 -05:00
efe7c01015 Fix typo and 'make debug'. 2023-11-14 18:38:26 -05:00
600fa4ce59 Fix Unicode font handling (Issue #16) 2023-11-14 18:19:34 -05:00
688810f143 Save work. 2023-11-13 16:18:02 -05:00
858cc101b6 Save work. 2023-11-13 13:39:06 -05:00
43114f43bf Bump version. 2023-10-10 10:22:27 -04:00
c4abceef79 Make Visual Studio compiler happy. 2023-10-10 07:24:27 -04:00
2e91e05d7b Allow "compound" filters that consist of a single named filter (Issue #47) 2023-10-10 07:14:12 -04:00
7e3db6b639 Merge pull request #48 from crystalidea/master
added windows.h header for GetTempPathA
2023-10-10 07:07:46 -04:00
acb6b66bdb added windows.h header for GetTempPathA 2023-10-10 09:12:03 +02:00
b0a66eef78 Fix reading of PDF files from Crystal Reports (Issue #45) 2023-10-09 10:04:20 -04:00
ed88322496 Debug logging, work in progress for Unicode text support. 2023-10-07 12:05:18 -04:00
59959bf0e5 Merge TTF changes to fix off-by-one error. 2023-10-06 16:44:20 -04:00
19c45871fa Update pdfioContentSetDashPattern to support setting solid line styles (Issue #41) 2023-10-06 15:47:27 -04:00
b0e4646f9d Rework CR/LF skip code to be more consistent. 2023-10-06 14:41:55 -04:00
12ef2fe2c3 Remove LGTM badges. 2023-10-06 14:40:40 -04:00
4630060ee7 Update security reporting and contribution text. 2023-10-06 14:40:28 -04:00
74a6fb1860 Get rid of superfluous comments. 2023-10-06 14:40:08 -04:00
a3ea0a99ff Cleanup spacing and comments. 2023-10-06 14:39:42 -04:00
fdfa700442 Update ignored files. 2023-10-06 14:39:10 -04:00
d759baf11e Bump version and put PDFIO_VERSION definition in the pdfio.h header. 2023-10-06 14:38:38 -04:00
7f6ffcda22 Fix a couple issues with parsing PDF files produced by Microsoft Reporting
Services (Issue #46)

- Odd cross-reference stream containing 3-byte generation number field for this
  16-bit value
- Odd empty hex strings
2023-10-06 10:46:30 -04:00
87ca4db73f Clean up private header. 2023-10-02 05:27:40 -04:00
a83f7f50ff Allow extra whitespace/newlines after stream tokens (Issue #40) 2023-10-02 05:06:33 -04:00
6a4ce57d09 Bump versions for Mac/Windows project files. 2023-03-20 10:40:25 -04:00
d4c594cec4 Bump copyright in readme. 2023-03-20 10:22:19 -04:00
35c674b633 Fix another build issue. 2023-03-20 10:11:05 -04:00
97d4955666 Fix potential denial-of-service in flate stream code. 2023-03-20 09:27:19 -04:00
e138232a93 Fix build error due to mismatched function declarations. 2023-03-20 08:19:31 -04:00
8d8225f4a1 Fix release date. 2023-02-07 17:10:48 -05:00
7045d9dad9 Bump Windows version numbers and update exports file. 2023-02-06 17:36:54 -05:00
4f10021e7e Fix denial-of-service attack when reading corrupt PDF files. 2023-02-03 20:39:04 -05:00
57d5894f33 Update pdfioStreamGetToken documentation (Issue #37) 2023-01-11 17:13:58 -05:00
2b8a1c8481 Fix CodeQL config file syntax. 2022-12-09 11:31:56 -05:00
948ee16b06 Fix the one "value" complaint from CodeQL and suppress all useless queries. 2022-12-09 11:22:10 -05:00
c7101ae9dd Add CodeQL scanning. 2022-12-09 11:09:34 -05:00
599640eda1 Update makefile to be silent with basic progress reporting. 2022-08-02 09:41:13 -04:00
a3f3bbfe11 Fix pdfioFileGetAuthor, etc. APIs (Issue #33) 2022-07-12 18:36:08 -04:00
26d485cfc5 Update Windows DLL exports file. 2022-07-06 15:25:45 -04:00
64d306a322 Cleanup. 2022-07-06 08:47:52 -04:00
067683cbcd Add some protection against opening multiple streams in the same file at the same time. 2022-07-04 13:03:11 -04:00
50f27974cf Update documentation. 2022-07-03 10:01:20 -04:00
ae9a91719b Add pdfioContentPathEnd function. 2022-07-03 10:01:10 -04:00
1a17933635 Fix pdfioContentMatrixRotate function. 2022-07-01 20:30:40 -04:00
acea6fdbed Changelog. 2022-06-27 17:17:44 -04:00
66fa12f928 Update Windows DLL exports file. 2022-06-27 10:17:21 -04:00
f4b8983c61 Implement pdfioDictIterateKeys API (Issue #31) 2022-06-27 10:17:00 -04:00
ed4e2fc38a Merge pull request #32 from ire4ever1190/patch-1
Fix `install-shared` Make task
2022-06-09 09:46:23 -04:00
1ed7f0089c Update Makefile 2022-06-09 14:33:53 +10:00
563d53edd4 Update Windows DLL exports file. 2022-05-24 19:16:20 -04:00
316b0ad559 Add pdfioFileCreateTemporary function (Issue #29) 2022-05-15 22:52:53 -04:00
f8b471acfd Update README and NOTICE files... 2022-03-02 09:50:14 -05:00
cedd7d104f Changelog update. 2022-03-02 09:47:14 -05:00
6378047026 Update VC project. 2022-03-02 09:31:33 -05:00
54578144a0 Update documentation and prep for 1.0.1 release. 2022-03-02 09:30:01 -05:00
f7f2969e3a Fix pdfioStreamGetToken implementation (wasn't flushing input), update
pdfiototext code to better handle different text operators that affect the
location of the text.
2022-03-01 09:18:56 -05:00
93a3fcea6c Add missing pdfioPageGetNumStreams and pdfioPageOpenStream functions.
Add initial version of pdfiototext text extraction utility.
2022-02-28 15:00:25 -05:00
fa20982e5d Coverity certs are fixed. 2021-12-15 18:20:54 -05:00
44d20eba1b Add stub code for AES-256 to force Coverity to re-analyze... 2021-12-15 07:35:55 -05:00
c0b7925cdf Fix typo. 2021-12-15 07:28:17 -05:00
68dcf021b2 Download Entrust root cert for validation. 2021-12-15 07:25:44 -05:00
b0a8e60968 Also allow posts to coverity.com while we wait for Ubuntu to pick up the new Entrust root certificate. 2021-12-15 07:10:13 -05:00
9d47745e43 Prep for 1.0rc1. 2021-12-15 06:53:09 -05:00
b0bf2e04b9 Coverity's certificate has expired. 2021-12-14 16:26:57 -05:00
f030112372 See what is happening when downloading Coverity build tool (drop quiet option). 2021-12-14 16:21:49 -05:00
79c4b6f8a8 See what is happening when downloading Coverity build tool. 2021-12-14 16:20:34 -05:00
bd2f9d44d4 Prep for 1.0.0 release. 2021-12-14 12:36:33 -05:00
3c7a980a0b Don't include AFL files in source archives. 2021-11-30 08:46:43 -05:00
019c05d04a Fix AFL target, remove excess PDF test files. 2021-11-30 08:13:41 -05:00
7ab550254a Add AFL make target (runs for 10 minutes). 2021-11-29 20:59:30 -05:00
fa8e54cca2 Add some files to use for AFL++. 2021-11-29 18:54:40 -05:00
d92fcb7bfb Add AFL++ PDF dictionary. 2021-11-29 18:47:04 -05:00
001dcbb123 Fix testpdfio build - dependencies on pdfio-private.h were missing. 2021-11-29 17:57:49 -05:00
a431d7806f Fix a few stack/buffer overflow bugs discovered by Bart, Steffan, and Mark from
the Radboud University NL (thanks!)

- Add depth argument to all value read functions that recurse
- Add depth argument to page tree loading code
- Validate xref stream sizes individually to avoid out-of-bounds access to local
  xref buffer.
2021-11-29 17:46:56 -05:00
ec8e900ea5 Add math library to libs. 2021-11-18 19:23:42 -05:00
c73aa7ae20 Add link for builds. 2021-11-11 06:52:23 -05:00
c53786e0e1 Changelog. 2021-11-07 11:29:18 -05:00
1d5310a5f3 Bump version to 1.0b2. 2021-11-07 11:15:35 -05:00
1e33878506 Fix conversion of nul-containing strings to binary.
Move key length checks to a common place.
2021-11-02 09:12:43 -04:00
af07f64bc3 Fix 'make test'. 2021-11-02 07:50:14 -04:00
2f0d622873 Save work on resolving PDF loading issues with random PDFs using different encryption methods and line endings. 2021-11-01 21:30:46 -04:00
6432187dea Fix sporadic test suite failures caused by greedy whitespace removal in token
reader.

Update read code to handle signal/temporary failures.

Add some more useful debug messages for the encryption code.

Eliminate more warnings from Clang.
2021-10-31 11:12:54 -04:00
9d121335f5 Make sure we free memory used for binary data. 2021-10-31 08:30:08 -04:00
9014ab7a20 Fix some minor Coverity-reported issues (added a check to suppress a warning,
removed an unnecessary check, and removed some dead code)
2021-10-31 07:04:17 -04:00
b3ca129a58 Bump NuGet versions. 2021-10-29 07:42:24 -04:00
fafe24bdb6 Fix Windows builds. 2021-10-26 07:12:41 -04:00
b865390b5d Update docos. 2021-10-25 22:00:25 -04:00
1d1ff88ebc Merge pull request #26 from michaelrsweet/crypto
Merge RC4/AES-128 crypto implementation.
2021-10-25 21:56:58 -04:00
8dfc2c6045 Fix LGTM issues. 2021-10-25 21:43:32 -04:00
895738682e Update DLL exports file. 2021-10-25 21:39:44 -04:00
90ad1e694a Fix early closing of input PDF. 2021-10-25 21:36:01 -04:00
e2b33a6cbb Merge branch 'master' into crypto 2021-10-25 21:25:12 -04:00
790cd440ea Fix up copying objects from unencrypted to AES-encrypted documents (still looks
like there are some issues with strings in dicts)
2021-10-25 21:22:59 -04:00
038046e6d5 Save work on encrypted PDF reading. 2021-10-25 19:36:39 -04:00
45c5a00252 Update Windows DLL exports file. 2021-10-24 11:05:33 -04:00
7e9c0afc23 Update summary text. 2021-10-24 11:03:19 -04:00
234c3a7381 Do some reorganization and start the implementation of decryption. 2021-10-24 10:59:25 -04:00
b7ecaeee07 Implement partial write buffering for AES. 2021-10-23 20:33:12 -04:00
208c3419ff Fix AES-128 writing/encryption. 2021-10-23 20:09:02 -04:00
dd56317635 Need object when reading/writing encrypted PDFs (to decrypt/encrypt strings),
RC4 writing is now working, AES-128 needs work, AES-256 hasn't been done yet.
2021-10-23 18:08:16 -04:00
3af39d5d1f Update crypto callback to return the number of output bytes (to account for AES
expansion).
2021-10-23 14:37:25 -04:00
19571d00f2 Fix AES cipher implementation.
Update test program to validate the key expansion using the FIPS-197 example.

Add password-protected RC4 test output.

Add no-password AES-128 test output.
2021-10-23 00:07:13 -04:00
af13376e6d Update docos. 2021-10-18 23:08:13 -04:00
22c245ffd1 Update pdfioContentSetDashPattern to accept doubles (Issue #25) 2021-10-16 09:41:19 -04:00
095a4c10d4 Fix some memory leaks (Issue #23) 2021-10-16 00:02:31 -04:00
f3689d6b3d Fix all-shared on Linux (Issue #22) 2021-10-15 19:32:08 -04:00
ea126c7e8d Save work. 2021-10-15 10:40:42 -04:00
e031254531 Fix 'all-shared' target. 2021-10-13 17:15:59 -04:00
493fbca31c Save work on unit tests for crypto. RC4 and AES are having trouble for some reason... 2021-10-12 17:11:10 -04:00
c24243a2bc Refactor crypto callback to have separate input/output pointers. Add initial writing support. 2021-10-12 09:13:30 -04:00
0caea44f32 Implement MakeReader/Writer functions. 2021-10-10 23:08:56 -04:00
3de55421b5 New member names to specify type of value. 2021-10-10 22:40:42 -04:00
61a7964d90 Implement pdfioFileSetPermissions. 2021-10-10 22:27:09 -04:00
37e80d67b1 Use new random number function to generate file IDs. 2021-10-09 23:10:46 -04:00
953de26f6b Add random number generation support. 2021-10-09 23:05:39 -04:00
2245c9d4f5 Move AES code to separate file, prep private API for making keys/contexts/callbacks for encryption. 2021-10-09 10:49:22 -04:00
27e4ce9f42 Update Xcode project to build the SHA-256 code. 2021-10-08 21:14:11 -04:00
9c05f802fc Add SHA-256 code from RFC 6234. 2021-10-08 18:55:25 -04:00
8aef2bfedd Fix warnings, update Xcode project. 2021-10-08 14:08:07 -04:00
f425952f36 Tweak coverity Github Actions. 2021-10-08 13:49:48 -04:00
0c6b8f49d2 Tweak coverity Github Actions. 2021-10-08 13:49:48 -04:00
8ad699c93a Tweak coverity Github Actions. 2021-10-08 13:49:48 -04:00
a259c3a6b9 Tweak coverity Github Actions. 2021-10-08 13:49:48 -04:00
0ec1dd936f Tweak coverity Github Actions. 2021-10-08 13:49:48 -04:00
1d63c6edd6 Add prototype coverity Github Actions integration. 2021-10-08 13:49:48 -04:00
d5173d14da Fix some Coverity-detected issues. 2021-10-08 13:49:48 -04:00
1168fd974f Fix pdfio_native.redist package name. 2021-10-08 13:49:48 -04:00
5cff1ca13c Bump NuGet versions. 2021-10-08 13:49:47 -04:00
835fbda363 Tweak coverity Github Actions. 2021-10-05 18:37:16 -04:00
494924a78c Tweak coverity Github Actions. 2021-10-05 18:26:51 -04:00
f23fd8de59 Tweak coverity Github Actions. 2021-10-05 18:21:37 -04:00
3c702096b7 Tweak coverity Github Actions. 2021-10-05 18:19:58 -04:00
e67866e29d Tweak coverity Github Actions. 2021-10-05 18:17:49 -04:00
89d9a7c471 Add prototype coverity Github Actions integration. 2021-10-05 18:13:14 -04:00
00fb962e84 Add prototype coverity Github Actions integration. 2021-10-05 18:08:07 -04:00
fd08ce1b1a Add prototype coverity Github Actions integration. 2021-10-05 18:06:03 -04:00
7fe093f3bd Save work on AES and RC4. 2021-10-04 21:13:01 -04:00
d1e8c966ed Fix some Coverity-detected issues. 2021-10-01 11:38:04 -04:00
85bfab49ab Fix pdfio_native.redist package name. 2021-09-29 11:48:50 -04:00
768cb33c47 Bump NuGet versions. 2021-09-29 11:05:40 -04:00
bb91fb4b13 Tweak macOS build command. 2021-09-27 10:23:32 -04:00
76b2faee0e Add EPUB book cover. 2021-09-27 10:02:10 -04:00
2d90b1325b Ignore new streamed output test file. 2021-09-27 08:57:15 -04:00
6b9f4ba8c9 Fix Windows DLL exports file. 2021-09-27 08:38:04 -04:00
43239eaf8a Add placeholder password callback to support reading of encrypted PDF files in the future. 2021-09-27 08:37:14 -04:00
ba9d03ecac Update docos. 2021-09-27 08:11:53 -04:00
7473bc3cd9 Add some supporting documentation files. 2021-09-27 07:42:19 -04:00
d6746c08a4 Add pdfioFileCreateOutput API (Issue #21) 2021-09-27 07:41:50 -04:00
9f1cadf78b Add redistributable NuGet package for DLL. 2021-09-03 09:13:16 -04:00
760871b8db Update NuGet package info. 2021-09-01 17:09:05 -04:00
186 changed files with 35450 additions and 2483 deletions

2
.gitattributes vendored
View File

@ -1,2 +1,4 @@
.git* export-ignore
afl-pdf.dict export-ignore
afl-input export-ignore
makesrcdist export-ignore

22
.github/codeql.yml vendored Normal file
View File

@ -0,0 +1,22 @@
paths-ignore:
- testpdfio.c
query-filters:
- exclude:
id: cpp/commented-out-code
- exclude:
id: cpp/toctou-race-condition
- exclude:
id: cpp/weak-cryptographic-algorithm
- exclude:
id: cpp/world-writable-file-creation
- exclude:
id: cpp/uncontrolled-allocation-size
- exclude:
id: cpp/path-injection
- exclude:
id: cpp/stack-address-escape
- exclude:
id: cpp/loop-variable-changed
- exclude:
id: cpp/long-switch

View File

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

50
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,50 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "46 3 * * 0"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ cpp ]
steps:
- name: Checkout PDFio sources
uses: actions/checkout@v4
with:
submodules: recursive
- name: Update Build Environment
run: sudo apt-get update --fix-missing -y
- name: Install Prerequisites
run: sudo apt-get install -y zlib1g-dev libpng-dev
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql.yml
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"

43
.github/workflows/coverity.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: Coverity Scan
on: workflow_dispatch
jobs:
coverity-scan:
runs-on: ubuntu-latest
environment: Coverity
steps:
- name: Checkout PDFio sources
uses: actions/checkout@v4
- name: Update Build Environment
run: sudo apt-get update --fix-missing -y
- name: Install Prerequisites
run: sudo apt-get install -y zlib1g-dev libpng-dev
- name: Download Coverity Build Tool
run: |
wget -q https://scan.coverity.com/download/linux64 --post-data token="$TOKEN&project=$GITHUB_REPOSITORY" -O cov-analysis-linux64.tar.gz
mkdir cov-analysis-linux64
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
- name: Configure PDFio
run: ./configure --enable-debug --enable-maintainer
- name: Build PDFio with cov-build
run: |
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
cov-build --dir cov-int make
- name: Submit the Result to Coverity Scan
run: |
tar czvf cov.tgz cov-int
curl \
--form token=$TOKEN \
--form email=michael.r.sweet@gmail.com \
--form file=@cov.tgz \
--form version="$GITHUB_REF" \
--form description="$GITHUB_SHA" \
"https://scan.coverity.com/builds?project=$GITHUB_REPOSITORY"
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}

20
.gitignore vendored
View File

@ -1,11 +1,29 @@
*.1.dylib
*.a
*.dSYM
*.log
*.o
*.so.1
/.vs
/afl-output
/autom4te.cache
/config.log
/config.status
/configure~
/doc/pdfio.epub
/examples/code128
/examples/image2pdf
/examples/md2pdf
/examples/pdf2text
/examples/pdfioinfo
/examples/pdfiomerge
/Makefile
/packages
/pdfio.pc
/pdfio.xcodeproj/xcshareddata
/pdfio-*.tar.gz*
/pdfio-*.zip*
/testpdfio
/testpdfio-out.pdf
/testpdfio-*.pdf
/testttf
/x64

View File

@ -1,2 +0,0 @@
queries:
- exclude: cpp/toctou-race-condition

200
CHANGES.md Normal file
View File

@ -0,0 +1,200 @@
Changes in PDFio
================
v1.5.1 - YYYY-MM-DD
-------------------
- Fixed output of special characters in name values (Issue #106)
- Fixed output of special characters in string values (Issue #107)
- Fixed output of large integers in dictionaries (Issue #108)
- Fixed handling of 0-length streams (Issue #111)
v1.5.0 - 2025-03-06
-------------------
- Added support for embedded color profiles in JPEG images (Issue #7)
- Added `pdfioFileCreateICCObjFromData` API.
- Added support for writing cross-reference streams for PDF 1.5 and newer files
(Issue #10)
- Added `pdfioFileGetModDate()` API (Issue #88)
- Added support for using libpng to embed PNG images in PDF output (Issue #90)
- Added support for writing the PCLm subset of PDF (Issue #99)
- Now support opening damaged PDF files (Issue #45)
- Updated documentation (Issue #95)
- Updated the pdf2txt example to support font encodings.
- Fixed potential heap/integer overflow issues in the TrueType cmap code.
- Fixed an output issue for extremely small `double` values with the
`pdfioContent` APIs.
- Fixed a missing Widths array issue for embedded TrueType fonts.
- Fixed some Unicode font embedding issues.
v1.4.1 - 2025-01-24
-------------------
- Added license files for the example fonts now bundled with PDFio (Issue #91)
- Fixed the link libraries for the example source code (Issue #86)
- Fixed handling of the Info object (Issue #87)
- Fixed opening of PDF files less than 1024 bytes in length (Issue #87)
- Fixed potential `NULL` dereference when reading (Issue #89)
- Fixed reading of compressed object streams (Issue #92)
- Fixed reading of UTF-16 string values (Issue #92)
v1.4.0 - 2024-12-26
-------------------
- Added new `pdfioDictGetKey` and `pdfioDictGetNumPairs` APIs (Issue #63)
- Added new `pdfioArrayRemove` and `pdfioDictClear` APIs (Issue #74)
- Added new `pdfioFileCreateNameObj` and `pdfioObjGetName` APIs for creating and
getting name object values (Issue #76)
- Updated documentation (Issue #78)
- Updated `pdfioContentTextMeasure` to support measuring PDF base fonts created
with `pdfioFileCreateFontObjFromBase` (Issue #84)
- Fixed reading of PDF files whose trailer is missing a newline (Issue #80)
- Fixed builds with some versions of VC++ (Issue #81)
- Fixed validation of date/time values (Issue #83)
v1.3.2 - 2024-08-15
-------------------
- Added some more sanity checks to the TrueType font reader.
- Updated documentation (Issue #77)
- Fixed an issue when opening certain encrypted PDF files (Issue #62)
v1.3.1 - 2024-08-05
-------------------
- CVE 2024-42358: Updated TrueType font reader to avoid large memory
allocations.
- Fixed some documentation errors and added examples (Issue #68, Issue #69)
v1.3.0 - 2024-06-28
-------------------
- Added `pdfioFileGetCatalog` API for accessing the root/catalog object of a
PDF file (Issue #67)
- Updated number support to avoid locale issues (Issue #61)
- Updated the PDFio private header to allow compilation with MingW; note that
MingW is NOT a supported toolchain for PDFio (Issue #66)
- Optimized string pool code.
v1.2.0 - 2024-01-24
-------------------
- Now use autoconf to configure the PDFio sources (Issue #54)
- Added `pdfioFileCreateNumberObj` and `pdfioFileCreateStringObj` functions
(Issue #14)
- Added `pdfioContentTextMeasure` function (Issue #17)
- Added `pdfioContentTextNewLineShow` and `pdfioContentTextNewLineShowf`
functions (Issue #24)
- Renamed `pdfioContentTextNextLine` to `pdfioContentTextNewLine`.
- Updated the maximum number of object streams in a single file from 4096 to
8192 (Issue #58)
- Updated the token reading code to protect against some obvious abuses of the
PDF format.
- Updated the xref reading code to protect against loops.
- Updated the object handling code to use a binary insertion algorithm -
provides a significant (~800x) improvement in open times.
- Fixed handling of encrypted PDFs with per-object file IDs (Issue #42)
- Fixed handling of of trailer dictionaries that started immediately after the
"trailer" keyword (Issue #58)
- Fixed handling of invalid, but common, PDF files with a generation number of
65536 in the xref table (Issue #59)
v1.1.4 - 2023-12-03
-------------------
- Fixed detection of encrypted strings that are too short (Issue #52)
- Fixed a TrueType CMAP decoding bug.
- Fixed a text rendering issue for Asian text.
- Added a ToUnicode map for Unicode text to support text copying.
v1.1.3 - 2023-11-15
-------------------
- Fixed Unicode font support (Issue #16)
- Fixed missing initializer for 40-bit RC4 encryption (Issue #51)
v1.1.2 - 2023-10-10
-------------------
- Updated `pdfioContentSetDashPattern` to support setting a solid (0 length)
dash pattern (Issue #41)
- Fixed an issue with broken PDF files containing extra CR and/or LF separators
after the object stream token (Issue #40)
- Fixed an issue with PDF files produced by Crystal Reports (Issue #45)
- Fixed an issue with PDF files produced by Microsoft Reporting Services
(Issue #46)
- Fixed support for compound filters where the filter array consists of a
single named filter (Issue #47)
- Fixed builds on Windows - needed windows.h header for temporary files
(Issue #48)
v1.1.1 - 2023-03-20
-------------------
- CVE-2023-28428: Fixed a potential denial-of-service with corrupt PDF files.
- Fixed a few build issues.
v1.1.0 - 2023-02-06
-------------------
- CVE-2023-24808: Fixed a potential denial-of-service with corrupt PDF files.
- Added `pdfioFileCreateTemporary` function (Issue #29)
- Added `pdfioDictIterateKeys` function (Issue #31)
- Added `pdfioContentPathEnd` function.
- Added protection against opening multiple streams in the same file at the
same time.
- Documentation updates (Issue #37)
- Fixed "install-shared" target (Issue #32)
- Fixed `pdfioFileGet...` metadata APIs (Issue #33)
- Fixed `pdfioContentMatrixRotate` function.
v1.0.1 - 2022-03-02
-------------------
- Added missing `pdfioPageGetNumStreams` and `pdfioPageOpenStream` functions.
- Added demo pdfiototext utility.
- Fixed bug in `pdfioStreamGetToken`.
v1.0.0 - 2021-12-14
-------------------
- First stable release.
v1.0rc1 - 2021-11-30
--------------------
- Fixed a few stack/buffer overflow bugs discovered via fuzzing.
v1.0b2 - 2021-11-07
-------------------
- Added `pdfioFileCreateOutput` API to support streaming output of PDF
(Issue #21)
- Fixed `all-shared` target (Issue #22)
- Fixed memory leaks (Issue #23)
- Updated `pdfioContentSetDashPattern` to accept `double` values (Issue #25)
- Added support for reading and writing encrypted PDFs (Issue #26)
- Fixed some issues identified by a Coverity scan.
v1.0b1 - 2021-08-30
-------------------
- Initial release

13
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,13 @@
Code of Conduct
===============
My goal is to provide quality open source software that everyone can use.
While I may not be able to address every request or accept every contribution
to this project, I will do my best to develop and maintain it for the common
good. As part of the open source community, I expect everyone to:
- Be friendly and patient.
- Be respectful, even if we disagree.
- Be honest.
- Be accepting of all people.
- Fully explain your concerns, issues, or ideas.

399
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,399 @@
Contributing to PDFio
=====================
PDFio is developed and distributed as open source software under the Apache
License, Version 2.0. Contributions should be submitted as pull requests on
the Github site:
http://github.com/michaelrsweet/pdfio/pulls
Contents
--------
- [Build System](#build-system)
- [Version Numbering](#version-numbering)
- [Coding Guidelines](#coding-guidelines)
- [Source Files](#source-files)
- [Header Files](#header-files)
- [Comments](#comments)
- [Indentation](#indentation)
- [Spacing](#spacing)
- [Return Values](#return-values)
- [Functions](#functions)
- [Variables](#variables)
- [Types](#types)
- [Structures](#structures)
- [Constants](#constants)
- [Shell Script Guidelines](#shell-script-guidelines)
- [Makefile Guidelines](#makefile-guidelines)
- [General Organization](#general-organization)
- [Makefile Documentation](#makefile-documentation)
- [Portable Makefile Construction](#portable-makefile-construction)
- [Standard Variables](#standard-variables)
- [Standard Targets](#standard-targets)
- [Object Files](#object-files)
- [Programs](#programs)
- [Static Libraries](#static-libraries)
- [Shared Libraries](#shared-libraries)
- [Dependencies](#dependencies)
- [Install/Uninstall Support](#installuninstall-support)
Build System
------------
The build system uses [GNU autoconf][AUTOCONF] to create a simple POSIX makefile
to build static and/or shared libraries. To improve portability, makefiles
*must not* make use of features unique to GNU make. See the
[Makefile Guidelines](#makefile-guidelines) section for a description of the
allowed make features and makefile guidelines.
An Xcode project is provided for macOS/iOS developers, and a Visual Studio
solution and projects for Windows developers.
[AUTOCONF]: https://www.gnu.org/software/autoconf/
Version Numbering
-----------------
PDFio uses a three-part version number separated by periods to represent the
major, minor, and patch release numbers. Major release numbers indicate large
design changes or backwards-incompatible changes to the library. Minor release
numbers indicate new features and other smaller changes which are backwards-
compatible with previous releases. Patch numbers indicate bug fixes to the
previous feature or patch release.
Production releases use the plain version numbers:
MAJOR.MINOR.PATCH
1.0.0
1.0.1
1.0.2
...
1.1.0
...
2.0.0
The first production release in a MAJOR.MINOR series (MAJOR.MINOR.0) is called
a feature release. Feature releases are the only releases that may contain new
features. Subsequent production releases in a MAJOR.MINOR series may only
contain bug fixes.
Beta-test releases are identified by appending the letter B to the major and
minor version numbers followed by the beta release number:
MAJOR.MINORbNUMBER
1.0b1
Release candidates are identified by appending the letters RC to the major and
minor version numbers followed by the release candidate number:
MAJOR.MINORrcNUMBER
1.0rc1
> Note: While the beta/release candidate syntax is *not* strictly compatible
> with [Semantic Versioning](https://semver.org), it is better supported by the
> various traditional package formats. When packaging a pre-release version of
> PDFio in a format that requires the use of semantic version numbers, the
> version number should simply be converted to the form "MAJOR.MINOR.0-suffix".
Coding Guidelines
-----------------
Contributed source code must follow the guidelines below. While the examples
are for C source files, source code for other languages should conform to the
same guidelines as allowed by the language.
### Source Files
All source files names must be 16 characters or less in length to ensure
compatibility with older UNIX filesystems. Source files containing functions
have an extension of ".c" for C source files. All "include" files have an
extension of ".h". Tabs are set to 8 characters or columns.
The top of each source file contains a header giving the purpose or nature of
the source file and the copyright and licensing notice:
//
// Description of file contents.
//
// Copyright © YYYY by AUTHOR.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
### Header Files
Private API header files must be named with the suffix "-private", for example
the "pdfio.h" header file defines all of the public APIs while the
"pdfio-private.h" header file defines all of the private APIs. Typically a
private API header file will include the corresponding public API header file.
### Comments
All source code utilizes block comments within functions to describe the
operations being performed by a group of statements; avoid putting a comment
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
("// comment"):
// Clear the state array before we begin...
for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
array[i] = PDFIO_STATE_IDLE;
// Wait for state changes on another thread...
do
{
for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
if (array[i] != PDFIO_STATE_IDLE)
break;
if (i == (sizeof(array) / sizeof(array[0])))
sleep(1);
} while (i == (sizeof(array) / sizeof(array[0])));
### Indentation
All code blocks enclosed by brackets begin with the opening brace on a new
line. The code then follows starting on a new line after the brace and is
indented 2 spaces. The closing brace is then placed on a new line following
the code at the original indentation:
{
int i; // Looping var
// Process foobar values from 0 to 999...
for (i = 0; i < 1000; i ++)
{
do_this(i);
do_that(i);
}
}
Single-line statements following "do", "else", "for", "if", and "while" are
indented 2 spaces as well. Blocks of code in a "switch" block are indented 4
spaces after each "case" and "default" case:
switch (array[i])
{
case PDFIO_STATE_IDLE :
do_this(i);
do_that(i);
break;
default :
do_nothing(i);
break;
}
### Spacing
A space follows each reserved word such as `if`, `while`, etc. Spaces are not
inserted between a function name and the arguments in parenthesis.
### Return Values
Parenthesis surround values returned from a function:
return (PDFIO_STATE_IDLE);
### Functions
Functions with a global scope have a lowercase prefix followed by capitalized
words, e.g., `pdfioDoThis`, `pdfioDoThat`, `pdfioDoSomethingElse`, etc. Private
global functions begin with a leading underscore, e.g., `_pdfioDoThis`,
`_pdfioDoThat`, etc.
Functions with a local scope are declared static with lowercase names and
underscores between words, e.g., `do_this`, `do_that`, `do_something_else`, etc.
Function names follow the following pattern:
- "pdfioFooCreate" to create a Foo object,
- "pdfioFooDelete" to destroy (free) a Foo object,
- "pdfioFooGetBar" to get data element Bar from object Foo,
- "pdfioFooIsBar" to test condition Bar for object Foo, and
- "pdfioFooSetBar" to set data element Bar in object Foo.
- "pdfioFooVerb" to take an action with object Foo.
Each function begins with a comment header describing what the function does,
the possible input limits (if any), the possible output values (if any), and
any special information needed:
//
// 'pdfioDoThis()' - Short description of function.
//
// Longer documentation for function with examples using a subset of
// markdown. This is a bulleted list:
//
// - One fish
// - Two fish
// - Red fish
// - Blue fish
//
// > *Note:* Special notes for developer should be markdown block quotes.
//
float // O - Inverse power value, 0.0 <= y <= 1.1
pdfioDoThis(float x) // I - Power value (0.0 <= x <= 1.1)
{
...
return (y);
}
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
the "IO" prefix for the corresponding in-line comment.
The [`codedoc` documentation generator][1] also understands the following
special text in the function description comment:
@deprecated@ - Marks the function as deprecated (not recommended
for new development and scheduled for removal)
@since version@ - Marks the function as new in the specified version.
@private@ - Marks the function as private (same as starting the
function name with an underscore)
[1]: https://www.msweet.org/codedoc
### Variables
Variables with a global scope are capitalized, e.g., `ThisVariable`,
`ThatVariable`, `ThisStateVariable`, etc. Globals *must not* be used in the
PDFio library.
Variables with a local scope are lowercase with underscores between words,
e.g., `this_variable`, `that_variable`, etc. Any "local global" variables
shared by functions within a source file are declared static.
Each variable is declared on a separate line and is immediately followed by a
comment block describing the variable:
int ThisVariable; // The current state of this
static int that_variable; // The current state of that
### Types
All type names are lowercase with underscores between words and `_t` appended
to the end of the name, e.g., `pdfio_this_type_t`, `pdfio_that_type_t`, etc.
Type names start with the "pdfio\_" prefix to avoid conflicts with system types.
Private type names start with an underscore, e.g., `_pdfio_this_t`,
`_pdfio_that_t`, etc.
Each type has a comment block immediately after the typedef:
typedef int pdfio_this_type_t; // This type is for foobar options.
### Structures
All structure names are lowercase with underscores between words and `_s`
appended to the end of the name, e.g., `pdfio_this_s`, `pdfio_that_s`, etc.
Structure names start with the "pdfio\_" prefix to avoid conflicts with system
types. Private structure names start with an underscore, e.g., `_pdfio_this_s`,
`_pdfio_that_s`, etc.
Each structure has a comment block immediately after the struct and each member
is documented similar to the variable naming policy above:
struct pdfio_this_struct_s // This structure is for foobar options.
{
int this_member; // Current state for this
int that_member; // Current state for that
};
One common design pattern is to define a private structure with a public
typedef, for example:
// In public header
typedef struct _pdfio_foo_s pdfio_foo_t // Foo object
// In private header
struct _pdfio_foo_s // Foo object
{
int this_member; // Current state for this
int that_member; // Current state for that
};
### Constants
All constant names are uppercase with underscores between words, e.g.,
`PDFIO_THIS_CONSTANT`, `PDFIO_THAT_CONSTANT`, etc. Constants begin with the
"PDFIO\_" prefix to avoid conflicts with system constants. Private constants
start with an underscore, e.g., `_PDFIO_THIS_CONSTANT`,
`_PDFIO_THAT_CONSTANT`, etc.
Typed enumerations should be used whenever possible to allow for type checking
by the compiler. The constants for typed enumerations must match the type name
in uppercase, for example a `pdfio_foo_e` enumeration has constant names
starting with `PDFIO_FOO_`.
Comment blocks immediately follow each constant:
typedef enum pdfio_style_e // Style enumerations
{
PDFIO_STYLE_THIS, // This style
PDFIO_STYLE_THAT // That style
} pdfio_style_t;
Shell Script Guidelines
-----------------------
All shell scripts in PDFio must conform to the [POSIX shell][POSIX-SHELL]
command language and should restrict their dependence on non-POSIX utility
commands.
[POSIX-SHELL]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18
Makefile Guidelines
-------------------
PDFio uses a single [POSIX makefile][POSIX-MAKE] to build it. GNU make
extensions MUST NOT be used.
[POSIX-MAKE]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html
The following variables are defined in the makefile:
- `AR`; the static library archiver command,
- `ARFLAGS`; options for the static library archiver,
- `CC`; the C compiler command,
- `CFLAGS`; options for the C compiler,
- `CODESIGN_IDENTITY`: the code signing identity,
- `CPPFLAGS`; options for the C preprocessor,
- `DESTDIR`/`DSTROOT`: the destination root directory when installing.
- `DSO`; the shared library building command,
- `DSOFLAGS`; options for the shared library building command,
- `LDFLAGS`; options for the linker,
- `LIBPDFIO`: the name of the primary (shared or static) library
- `LIBPDFIO_STATIC`: the name of the secondary (static) library
- `LIBS`; libraries for all programs,
- `OPTIM`; common compiler optimization options,
- `prefix`; the installation prefix directory,
- `RANLIB`; the static library indexing command,
- `SHELL`; the sh (POSIX shell) command,
- `VERSION`: the library version number.
The following standard targets are defined in the makefile:
- `all`; creates the static library and unit test program.
- `clean`; removes all target programs libraries, documentation files, and
object files,
- `install`; installs all distribution files in their corresponding locations.
- `test`; runs the unit test program, building it as needed.

18
EXAMPLES.md Normal file
View File

@ -0,0 +1,18 @@
PDFio Examples
==============
The "examples" subdirectory contains example code showing how to do different
things with PDFio.
code128.c
---------
This example shows how to embed and use a barcode font.
md2pdf.c
--------
This example shows how to generate pages with multiple fonts, embedded images,
and headers and footers.

17
FAQ.md
View File

@ -1,17 +0,0 @@
Frequently Asked Questions
==========================
Why Don't You Support Writing a PDF File with Encryption?
---------------------------------------------------------
PDF encryption offers very little protection:
- PDF encryption keys are reused and derived from the user password (padded
with a standard base string) and the object numbers in the file.
- RC4 encryption (40- and 128-bit) was broken years ago.
- AES encryption (128- and 256-bit) is better, but PDF uses Cipher Block
Chaining (CBC) which enables attacks that allow the original encryption key
to be recovered.
In addition, PDF usage controls (no print, no copy, etc.) are tied to this
encryption, making them trivial to bypass.

190
Makefile
View File

@ -1,190 +0,0 @@
#
# Makefile for PDFio.
#
# Copyright © 2021 by Michael R Sweet.
#
# Licensed under Apache License v2.0. See the file "LICENSE" for more
# information.
#
# POSIX makefile
.POSIX:
# Variables...
AR = ar
ARFLAGS = cr
CC = cc
CFLAGS =
CODESIGN_IDENTITY = Developer ID
COMMONFLAGS = -Os -g
CPPFLAGS = '-DPDFIO_VERSION="$(VERSION)"'
DESTDIR = $(DSTROOT)
DSO = cc
DSOFLAGS =
DSONAME =
LDFLAGS =
LIBS = -lm -lz
RANLIB = ranlib
VERSION = 1.0b1
prefix = /usr/local
# Base rules
.SUFFIXES: .c .h .o
.c.o:
$(CC) $(CFLAGS) $(CPPFLAGS) $(COMMONFLAGS) -c $<
# Files
PUBHEADERS = \
pdfio.h \
pdfio-content.h
PUBOBJS = \
pdfio-array.o \
pdfio-common.o \
pdfio-content.o \
pdfio-dict.o \
pdfio-file.o \
pdfio-object.o \
pdfio-page.o \
pdfio-stream.o \
pdfio-string.o \
pdfio-token.o \
pdfio-value.o
LIBOBJS = \
$(PUBOBJS) \
ttf.o
OBJS = \
$(LIBOBJS) \
testpdfio.o
TARGETS = \
$(DSONAME) \
libpdfio.a \
testpdfio
# Make everything
all: $(TARGETS)
all-shared:
if test `uname` = Darwin; then \
$(MAKE) DSONAME="libpdfio.1.dylib" -$(MAKEFLAGS) all; \
else
$(MAKE) DSONAME="libpdfio.so.1" -$(MAKEFLAGS) all; \
fi
debug:
$(MAKE) -$(MAKEFLAGS) COMMONFLAGS="-g -fsanitize=address -DDEBUG=1" clean all
# Clean everything
clean:
rm -f $(TARGETS) $(OBJS)
# Install everything
install: $(TARGETS)
-mkdir -p $(DESTDIR)$(prefix)/include
cp $(PUBHEADERS) $(DESTDIR)$(prefix)/include
-mkdir -p $(DESTDIR)$(prefix)/lib
cp libpdfio.a $(DESTDIR)$(prefix)/lib
$(RANLIB) $(DESTDIR)$(prefix)/lib/libpdfio.a
if test "x$(DSONAME)" = xlibpdfio.so.1; then \
cp $(DSONAME) $(DESTDIR)$(prefix)/lib; \
ln -sf libpdfio.so.1 $(DESTDIR)$(prefix)/lib/libpdfio.so; \
elif test "x$(DSONAME)" = xlibpdfio.1.dylib; then \
cp $(DSONAME) $(DESTDIR)$(prefix)/lib; \
codesign -s "$(CODESIGN_IDENTITY)" -o runtime --timestamp $(DESTDIR)$(prefix)/lib/libpdfio.1.dylib; \
ln -sf libpdfio.1.dylib $(DESTDIR)$(prefix)/lib/libpdfio.dylib; \
fi
-mkdir -p $(DESTDIR)$(prefix)/lib/pkgconfig
echo 'prefix="$(prefix)"' >$(DESTDIR)$(prefix)/lib/pkgconfig/pdfio.pc
echo 'Version: $(VERSION)' >>$(DESTDIR)$(prefix)/lib/pkgconfig/pdfio.pc
cat pdfio.pc.in >>$(DESTDIR)$(prefix)/lib/pkgconfig/pdfio.pc
-mkdir -p $(DESTDIR)$(prefix)/share/doc/pdfio
cp doc/pdfio.html doc/pdfio-512.png LICENSE NOTICE $(DESTDIR)$(prefix)/share/doc/pdfio
-mkdir -p $(DESTDIR)$(prefix)/share/man/man3
cp doc/pdfio.3 $(DESTDIR)$(prefix)/share/man/man3
install-shared:
if test `uname` = Darwin; then \
$(MAKE) DSONAME="libpdfio.1.dylib" -$(MAKEFLAGS) install; \
else
$(MAKE) DSONAME="libpdfio.so.1" -$(MAKEFLAGS) install; \
fi
# Test everything
test: testpdfio
./testpdfio
# pdfio library
libpdfio.a: $(LIBOBJS)
$(AR) $(ARFLAGS) $@ $(LIBOBJS)
$(RANLIB) $@
libpdfio.so.1: $(LIBOBJS)
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -shared -o $@ -Wl,soname,$@ $(LIBOBJS) $(LIBS)
libpdfio.1.dylib: $(LIBOBJS)
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -dynamiclib -o $@ -install_name $(prefix)/lib/$@ -current_version $(VERSION) -compatibility_version 1.0 $(LIBOBJS) $(LIBS)
# pdfio1.def (Windows DLL exports file...)
#
# I'd love to use __declspec(dllexport) but MS puts it before the function
# declaration instead of after like everyone else, and it breaks Codedoc and
# other tools I rely on...
pdfio1.def: $(LIBOBJS) Makefile
echo Generating $@...
echo "LIBRARY pdfio1" >$@
echo "VERSION 1.0" >>$@
echo "EXPORTS" >>$@
(nm $(LIBOBJS) 2>/dev/null | grep "T _" | awk '{print $$3}' | \
grep -v '^_ttf' | grep -v '^__' | sed -e '1,$$s/^_//'; \
echo pdfioAdobeRGBGamma; echo pdfioAdobeRGBMatrix; \
echo pdfioAdobeRGBWhitePoint; \
echo pdfioDisplayP3Gamma; echo pdfioDisplayP3Matrix; \
echo pdfioDisplayP3WhitePoint; \
echo pdfioSRGBGamma; echo pdfioSRGBMatrix; \
echo pdfioSRGBWhitePoint; \
echo _pdfioTokenInit; \
echo _pdfioValueDebug; echo _pdfioValueRead) | sort >>$@
# pdfio test program
testpdfio: testpdfio.o libpdfio.a
$(CC) $(LDFLAGS) $(COMMONFLAGS) -o $@ testpdfio.o libpdfio.a $(LIBS)
# Dependencies
$(OBJS): pdfio.h Makefile
$(LIBOBJS): pdfio-private.h
pdfio-content.o: pdfio-content.h ttf.h
ttf.o: ttf.h
# Make documentation using Codedoc <https://www.msweet.org/codedoc>
DOCFLAGS = \
--author "Michael R Sweet" \
--copyright "Copyright (c) 2021 by Michael R Sweet" \
--docversion $(VERSION)
.PHONY: doc
doc:
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" $(PUBHEADERS) $(PUBOBJS:.o=.c) --body doc/pdfio.md --coverimage doc/pdfio-512.png pdfio.xml >doc/pdfio.html
codedoc $(DOCFLAGS) --title "pdf read/write library" --man pdfio --section 3 --body doc/pdfio.md pdfio.xml >doc/pdfio.3
rm -f pdfio.xml
# Analyze code with the Clang static analyzer <https://clang-analyzer.llvm.org>
clang:
clang $(CPPFLAGS) --analyze $(OBJS:.o=.c) 2>clang.log
rm -rf $(OBJS:.o=.plist)
test -s clang.log && (echo "$(GHA_ERROR)Clang detected issues."; echo ""; cat clang.log; exit 1) || exit 0
# Analyze code using Cppcheck <http://cppcheck.sourceforge.net>
cppcheck:
cppcheck $(CPPFLAGS) --template=gcc --addon=cert.py --suppressions-list=.cppcheck $(OBJS:.o=.c) 2>cppcheck.log
test -s cppcheck.log && (echo "$(GHA_ERROR)Cppcheck detected issues."; echo ""; cat cppcheck.log; exit 1) || exit 0

281
Makefile.in Normal file
View File

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

2
NOTICE
View File

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

View File

@ -3,18 +3,15 @@ pdfio - PDF Read/Write Library
![Version](https://img.shields.io/github/v/release/michaelrsweet/pdfio?include_prereleases)
![Apache 2.0](https://img.shields.io/github/license/michaelrsweet/pdfio)
![Build](https://github.com/michaelrsweet/pdfio/workflows/Build/badge.svg)
[![Build Status](https://img.shields.io/github/workflow/status/michaelrsweet/pdfio/Build)](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)
[![LGTM Grade](https://img.shields.io/lgtm/grade/cpp/github/michaelrsweet/pdfio)](https://lgtm.com/projects/g/michaelrsweet/pdfio/context:cpp)
[![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/michaelrsweet/pdfio)](https://lgtm.com/projects/g/michaelrsweet/pdfio/)
PDFio is a simple C library for reading and writing PDF files. The primary
goals of PDFio are:
- Read and write any version of PDF file
- Provide access to pages, objects, and streams within a PDF file
- Support reading encrypted PDF files
- Support writing PDF files with digital signatures
- Support reading and writing of encrypted PDF files
- Extract or embed useful metadata (author, creator, page information, etc.)
- "Filter" PDF files, for example to extract a range of pages or to embed fonts
that are missing from a PDF
@ -31,7 +28,7 @@ PDFio requires the following to build the software:
- A C99 compiler such as Clang, GCC, or MS Visual C
- A POSIX-compliant `make` program
- ZLIB (<https://www.zlib.net>) 1.0 or higher
- ZLIB (<https://www.zlib.net>) 1.1 or higher
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
@ -39,15 +36,27 @@ IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
Documentation
-------------
See the man page (`pdfio.3`), frequently ask questions (`FAQ.md`), and full HTML
documentation (`pdfio.html`) for information on using PDFio.
See the man page (`pdfio.3`) and full HTML documentation (`pdfio.html`) for
information on using PDFio.
Installing pdfio
Installing PDFio
----------------
PDFio comes with a portable makefile that will work on any POSIX-compliant
system with ZLIB installed. To make it, run:
PDFio uses a configure script on Unix systems to generate a makefile:
./configure
If you want a shared library, run:
./configure --enable-shared
The default installation location is "/usr/local". Pass the `--prefix` option
to make to install it to another location:
./configure --prefix=/some/other/directory
Once configured, run the following to make the library:
make all
@ -57,53 +66,15 @@ To test it, run:
To install it, run:
make install
If you want a shared library, run:
make all-shared
make install-shared
The default installation location is "/usr/local". Pass the `prefix` variable
to make to install it to another location:
make install prefix=/some/other/directory
The makefile installs the pdfio header to "${prefix}/include", the library to
"${prefix}/lib", the `pkg-config` file to "${prefix}/lib/pkgconfig", the man
page to "${prefix}/share/man/man3", and the documentation to
"${prefix}/share/doc/pdfio".
The makefile supports the following variables that can be specified in the make
command or as environment variables:
- `AR`: the library archiver (default "ar")
- `ARFLAGS`: options for the library archiver (default "cr")
- `CC`: the C compiler (default "cc")
- `CFLAGS`: options for the C compiler (default "")
- `CODESIGN_IDENTITY`: the identity to use when code signing the shared library
on macOS (default "Developer ID")
- `COMMONFLAGS`: options for the C compiler and linker (typically architecture
and optimization options, default is "-Os -g")
- `CPPFLAGS`: options for the C preprocessor (default "")
- `DESTDIR` and `DSTROOT`: specifies a root directory when installing
(default is "", specify only one)
- `DSOFLAGS`: options for the C compiler when linking the shared library
(default "")
- `LDFLAGS`: options for the C compiler when linking the test programs
(default "")
- `LIBS`: library options when linking the test programs (default "-lz")
- `RANLIB`: program that generates a table-of-contents in a library
(default "ranlib")
- `prefix`: specifies the installation directory (default "/usr/local")
sudo make install
Visual Studio Project
---------------------
The Visual Studio solution ("pdfio.sln") is provided for Windows developers and
generates both a static library and DLL. You can also use NuGet to install the
`pdfio_native` package.
generates the PDFIO1 DLL. You can also use NuGet to install the `pdfio_native`
package.
Xcode Project
@ -114,15 +85,11 @@ generates a static library that will be installed under "/usr/local" with:
sudo xcodebuild install
You can reproduce this with the makefile using:
sudo make 'COMMONFLAGS="-Os -mmacosx-version-min=10.14 -arch x86_64 -arch arm64"' install
Legal Stuff
-----------
PDFio is Copyright © 2021 by Michael R Sweet.
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

132
SECURITY.md Normal file
View File

@ -0,0 +1,132 @@
Security Policy
===============
This file describes how security issues are reported and handled, and what the
expectations are for security issues reported to this project.
Reporting a Security Bug
------------------------
For the purposes of this project, a security bug is a software defect that
allows a *local or remote user* to gain unauthorized access or privileges on the
host computer or to cause the software to crash. Such defects should be
reported to the project security advisory page at
<https://github.com/michaelrsweet/pdfio/security/advisories>.
Alternately, security bugs can be reported to "security AT msweet.org" using the
PGP public key below. Expect a response within 5 business days. Any proposed
embargo date should be at least 30 days and no more than 90 days in the future.
> *Note:* If you've found a software defect that allows a *program* to gain
> unauthorized access or privileges on the host computer or causes the program
> to crash, that defect should be reported as an ordinary project issue at
> <https://github.com/michaelrsweet/pdfio/issues>.
Responsible Disclosure
----------------------
With *responsible disclosure*, a security issue (and its fix) is disclosed only
after a mutually-agreed period of time (the "embargo date"). The issue and fix
are shared amongst and reviewed by the key stakeholders (Linux distributions,
OS vendors, etc.) and the CERT/CC. Fixes are released to the public on the
agreed-upon date.
> Responsible disclosure applies only to production releases. A security
> vulnerability that only affects unreleased code can be fixed immediately
> without coordination. Vendors *should not* package and release unstable
> snapshots, beta releases, or release candidates of this software.
Supported Versions
------------------
All production releases of this software are subject to this security policy. A
production release is tagged and given a semantic version number of the form:
MAJOR.MINOR.PATCH
where "MAJOR" is an integer starting at 1 and "MINOR" and "PATCH" are integers
starting at 0. A feature release has a "PATCH" value of 0, for example:
1.0.0
1.1.0
2.0.0
Beta releases and release candidates are *not* production releases and use
semantic version numbers of the form:
MAJOR.MINORbNUMBER
MAJOR.MINORrcNUMBER
where "MAJOR" and "MINOR" identify the new feature release version number and
"NUMBER" identifies a beta or release candidate number starting at 1, for
example:
1.0b1
1.0b2
1.0rc1
PGP Public Key
--------------
The following PGP public key can be used for signing security messages.
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
mQINBF6L0RgBEAC8FTqc/1Al+pWW+ULE0OB2qdbiA2NBjEm0X0WhvpjkqihS1Oih
ij3fzFxKJ+DgutQyDb4QFD8tCFL0f0rtNL1Iz8TtiAJjvlhL4kG5cdq5HYEchO10
qFeZ1DqvnHXB4pbKouEQ7Q/FqB1PG+m6y2q1ntgW+VPKm/nFUWBCmhTQicY3FOEG
q9r90enc8vhQGOX4p01KR0+izI/g+97pWgMMj5N4zHuXV/GrPhlVgo3Wn1OfEuX4
9vmv7GX4G17Me3E3LOo0c6fmPHJsrRG5oifLpvEJXVZW/RhJR3/pKMPSI5gW8Sal
lKAkNeV7aZG3U0DCiIVL6E4FrqXP4PPj1KBixtxOHqzQW8EJwuqbszNN3vp9w6jM
GvGtl8w5Qrw/BwnGC6Dmw+Qv04p9JRY2lygzZYcKuwZbLzBdC2CYy7P2shoKiymX
ARv+i+bUl6OmtDe2aYaqRkNDgJkpuVInBlMHwOyLP6fN2o7ETXQZ+0a1vQsgjmD+
Mngkc44HRnzsIJ3Ga4WwW8ggnAwUzJ/DgJFYOSbRUF/djBT4/EFoU+/kjXRqq8/d
c8HjZtz2L27njmMw68/bYmY1TliLp50PXGzJA/KeY90stwKtTI0ufwAyi9i9BaYq
cGbdq5jnfSNMDdKW2kLCNTQeUWSSytMTsdU0Av3Jrv5KQF8x5GaXcpCOTwARAQAB
tExNaWNoYWVsIFN3ZWV0IChzZWN1cml0eUBtc3dlZXQub3JnKSAoU2VjdXJpdHkg
UEdQIEtleSkgPHNlY3VyaXR5QG1zd2VldC5vcmc+iQJUBBMBCgA+FiEEOElfSXYU
h91AF0sBpZiItz2feQIFAl6L0RgCGwMFCQeGH4AFCwkIBwMFFQoJCAsFFgIDAQAC
HgECF4AACgkQpZiItz2feQIhjhAAqZHuQJkPBsAKUvJtPiyunpR6JENTUIDxnVXG
nue+Zev+B7PzQ7C4CAx7vXwuWTt/BXoyQFKRUrm+YGiBTvLYQ8fPqudDnycSaf/A
n01Ushdlhyg1wmCBGHTgt29IkEZphNj6BebRd675RTOSD5y14jrqUb+gxRNuNDa5
ZiZBlBE4A8TV6nvlCyLP5oXyTvKQRFCh4dEiL5ZvpoxnhNvJpSe1ohL8iJ9aeAd5
JdakOKi8MmidRPYC5IldXwduW7VC7dtqSiPqT5aSN0GJ8nIhSpn/ZkOEAPHAtxxa
0VgjltXwUDktu74MUUghdg2vC1df2Z+PqHLsGEqOmxoBIJYXroIqSEpO3Ma7hz0r
Xg1AWHMR/xxiLXLxgaZRvTp7AlaNjbqww8JDG8g+nDIeGsgIwWN/6uPczledvDQa
HtlMfN97i+rt6sCu13UMZHpBKOGg7eAGRhgpOwpUqmlW1b+ojRHGkmZ8oJSE7sFT
gzSGNkmfVgA1ILl0mi8OBVZ4jlUg6EgVsiPlzolH92iscK7g50PdjzpQe0m3gmcL
dpOmSL8Fti05dPfamJzIvJd28kMZ6yMnACKj9rq/VpfgYBLK8dbNUjEOQ2oq7PyR
Ye/LE1OmAJwfZQkyQNI8yAFXoRJ8u3/bRb3SPvGGWquGBDKHv2K1XiCW65uyLe5B
RNJWmme5Ag0EXovRGAEQAJZMFeIMt/ocLskrp89ZyBTTiavFKn9+QW7C2Mb36A73
J2g9vRFBSRizb+t8lSzP/T1GbKS0cEmfEpQppWImTbOMV6ZgxrM0IUy1Yd7Kyc0K
oNMZvykRYwVMzxB5hiQ88kCLfqTNCveIvu1xcB9pWkf+cuDmGCxA3I+yc3Eh/SOP
urDsHObt7fyEmJpSxCXlMFHRCuWyGXhMNvhR186t9mANW0PyxKJ8efr+2Vhm1+pA
Vk9JESac/lREvx9PVFmlPdqgqRkQ0TQB5+ROo9Wy77cxQr5+rvSZZff630I1YgZf
Ph6xOV1/q6vJ3RBNA2nPSTjPeeWQ7pTn7PZGJwCjIUjhMbO+EJVKUJNOAEg033mG
tLfbFUYdhA/dRgFuKz90loCMfsnf3e4o/TFydSHUuwBUtOWkL1BBWEbk95M/Zr00
w5fD9knas1u5Lc4ogXzTFPnvJ6hM1RAFJEd+FYzJZIvzwrIx4Ag1DOKViVBpeLTu
HWj+xckEgvxEBglplALzfSIJ0CLQSNL8iMFbzCnPeUoQfPkqu37KHrB9syAA06Tb
qw1Ax0qBqKInGIgBd0w6dFLF3s04xVcPAXWyJ0w4I7h2bs+aD6YwwK6xxCtXxtN5
Q1LQM8s3tKNXER3mZ8zfwgwjsdLVwhXhysFi6Dlkvk/Vrbn1QDfJnzq+F9LsGRGb
ABEBAAGJAjwEGAEKACYWIQQ4SV9JdhSH3UAXSwGlmIi3PZ95AgUCXovRGAIbDAUJ
B4YfgAAKCRClmIi3PZ95AhDZD/40fShzDS/smZZL0oXN4GgZ62FrXWBdLjontkXo
d8hDh1wJZwqsLVbtO2Gu0CPeH9GclQ3bYsR19sGMM4FDgjMu57O/TU6GZl2Ywcjh
ayhRTHyAq/BKZn71AM0N7LS8MdNTaLbTbzEu5oGbAmOVv5f0SUnQoGxbeF8ih5bo
hR3ZcORujWMgnymL3+cerNyIDQAtfMAUTfpVcwem4CvquA9Wjtur8YN1t+N7I3o2
eMTNSyNUL9Yx3NxbyJ0yrrMvASo+ZVRaPW5+ET9Iqd68ILSY04Gnar3URJssggX8
+cuyEbP9bAG8qYqcr2aSC2dW84mL/RnZGR//1dfS0Ugk6Osj0LSF5i+mz0CbIjYQ
PKgLlgpycuGZBC5kG3RWWfanM0HxPDx10a7vEWA1A5Q+csx4yi3CW/giC1zAdhUO
cJ1l4Uj/oxpGeLN7BnT/2NzU/px2fpbaG+xU4HlwjzFM2cIOUIohHFhdvFZbFIIA
mePqTBnEB3HtXYRTgMoYDXLWhlOXjyVnMR45WDfvEA3KqbAz6sNMtaOJ6rHBWnR1
1YbpvDWUeaGSLXBoGyo3RgTrN9jON8lE/oUxFobnEdfZGD+uwIniylc5rw3+VkBU
+QGZDfgPgxjSmKsWq1cK6rNfBacGYrdyqf90VemEsvR8r0Ump0RPzBMlAAq0Xkup
WkiKlA==
=0GzT
-----END PGP PUBLIC KEY BLOCK-----
```

BIN
afl-input/PDFBOX-1010-0.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1018-0.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1023-2.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1029-0.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1036-0.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1036-2.pdf Normal file

Binary file not shown.

View File

@ -0,0 +1,55 @@
%PDF-1.3
1 0 obj<</Type/Catalog/Pages 5 0 R>>
endobj
3 0 obj<</ModDate(D:20110505091515-05'00')/CreationDate(2011/05/05 09:15)/Creator(PaperPort 11.0)/Producer(PaperPort 11.0)/Subject()/Author()/Keywords()/Title()>>
endobj
4 0 obj<</Type/Page/MediaBox[0 0 622.0799 756]/Parent 5 0 R/CropBox[0 0 622.0799 756]/Contents 7 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/XObject<</Z_Im0 6 0 R>>>>>>
endobj
5 0 obj<</Count 1/Type/Pages/Kids[ 4 0 R]>>
endobj
6 0 obj<</Type/XObject/Subtype/Image/Name/XImg/Width 1728/Height 2100/BitsPerComponent 1/ColorSpace/DeviceGray/Intent//Filter[/CCITTFaxDecode]/DecodeParms[<</Colors 1/Columns 1728/Rows 2100/K -1>>]/Length 81592>>stream
endstream
endobj
7 0 obj<</Length 72>>stream
q
622.07996 0 0 756 0 0 cm
0 g
[]0 d 1 w 10 M 0 i 0 J 0 j
/Z_Im0 Do
Q
endstream
endobj
xref
0 8
0000000002 65535 f
0000000010 00000 n
0000000000 00000 f
0000000054 00000 n
0000000224 00000 n
0000000412 00000 n
0000000463 00000 n
0000082294 00000 n
trailer
<</Size 8/Info 3 0 R/Root 1 0 R/ID[<c48c2a5922382dc456a05f8e3ccbb9f8><94a076a2f82a754598b70200e827ac8b>]>>
startxref
82414
%%EOF
%PaperPortPDFversion3 0 obj<</ModDate(D:20110505091515-05'00')/CreationDate(2011/05/05 09:15)/Creator(PaperPort 11.0)/Producer(PaperPort 11.0)/Subject()/Author()/Keywords()/Title()>>
endobj
5 0 obj<</Count 2/Type/Pages/Kids[ 4 0 R 8 0 R]>>
endobj
8 0 obj<</Type/Page/MediaBox[0 0 622.0799 757.4399]/Parent 5 0 R/CropBox[0 0 622.0799 757.4399]/Contents 10 0 R/Resources<</ProcSet[/PDF/Text/ImageB/ImageC/ImageI]/XObject<</Z_Im0 9 0 R>>>>>>
endobj
9 0 obj<</Type/XObject/Subtype/Image/Name/XImg/Width 1728/Height 2104/BitsPerComponent 1/ColorSpace/DeviceGray/Intent//Filter[/CCITTFaxDecode]/DecodeParms[<</Colors 1/Columns 1728/Rows 2104/K -1>>]/Length 78404>>stream
endstream
endobj
10 0 obj<</Length 78>>stream
q
622.07996 0 0 757.44001 0 0 cm
0 g
[]0 d 1 w 10 M 0 i 0 J 0 j
/Z_Im0 Do
Q
endstream

BIN
afl-input/PDFBOX-1039-0.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1047-0.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1048-1.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1065-0.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1065-1.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1067-1.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1068-1.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1074-1.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1074-3.pdf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
afl-input/PDFBOX-1094-3.pdf Normal file

Binary file not shown.

Binary file not shown.

BIN
afl-input/PDFBOX-1094-4.pdf Normal file

Binary file not shown.

BIN
afl-input/PDFBOX-1095-2.pdf Normal file

Binary file not shown.

1466
afl-pdf.dict Normal file

File diff suppressed because it is too large Load Diff

1774
config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

1907
config.sub vendored Executable file

File diff suppressed because it is too large Load Diff

5608
configure vendored Executable file

File diff suppressed because it is too large Load Diff

299
configure.ac Normal file
View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
doc/pdfio-epub.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

71
examples/Makefile Normal file
View File

@ -0,0 +1,71 @@
#
# Makefile for PDFio examples.
#
# Copyright © 2024-2025 by Michael R Sweet.
#
# Licensed under Apache License v2.0. See the file "LICENSE" for more
# information.
#
# POSIX makefile
.POSIX:
# Common options
CFLAGS = -g $(CPPFLAGS)
#CFLAGS = -g -fsanitize=address $(CPPFLAGS)
CPPFLAGS = -I.. $(shell PKG_CONFIG_PATH="..:$(PKG_CONFIG_PATH)" pkg-config pdfio --cflags)
LIBS = -L.. $(shell PKG_CONFIG_PATH="..:$(PKG_CONFIG_PATH)" pkg-config pdfio --libs)
# Targets
TARGETS = \
code128 \
image2pdf \
md2pdf \
pdf2text \
pdfioinfo \
pdfiomerge
# Make everything
all: $(TARGETS)
# Clean everything
clean:
rm -f $(TARGETS)
# code128
code128: code128.c
$(CC) $(CFLAGS) -o $@ code128.c $(LIBS)
# image2pdf
image2pdf: image2pdf.c
$(CC) $(CFLAGS) -o $@ image2pdf.c $(LIBS)
# md2pdf
md2pdf: md2pdf.c mmd.c mmd.h
$(CC) $(CFLAGS) -o $@ md2pdf.c mmd.c $(LIBS)
# pdfio text extraction (demo, doesn't handle a lot of things yet)
pdf2text: pdf2text.c
$(CC) $(CFLAGS) -o $@ pdf2text.c $(LIBS)
# pdfioinfo
pdfioinfo: pdfioinfo.c
$(CC) $(CFLAGS) -o $@ pdfioinfo.c $(LIBS)
# pdfiomerge
pdfiomerge: pdfiomerge.c
$(CC) $(CFLAGS) -o $@ pdfiomerge.c $(LIBS)
# Common dependencies...
$(TARGETS): Makefile ../pdfio.h ../pdfio-content.h

BIN
examples/Roboto-Bold.ttf Normal file

Binary file not shown.

BIN
examples/Roboto-Italic.ttf Normal file

Binary file not shown.

View File

@ -0,0 +1,93 @@
Copyright 2011 The Roboto Project Authors (https://github.com/googlefonts/roboto-classic)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

BIN
examples/Roboto-Regular.ttf Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,343 @@
Copyright 2003 Grandzebu, All Rights Reserved
http://grandzebu.net/informatique/codbar-en/code128.htm
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

208
examples/code128.c Normal file
View File

@ -0,0 +1,208 @@
//
// Code 128 barcode example for PDFio.
//
// Copyright © 2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// Usage:
//
// ./code128 "BARCODE" ["TEXT"] >FILENAME.pdf
//
#include <pdfio.h>
#include <pdfio-content.h>
//
// 'make_code128()' - Make a Code 128 barcode string.
//
// This function produces a Code B (printable ASCII) representation of the
// source string and doesn't try to optimize using Code C. Non-printable and
// extended characters are ignored in the source string.
//
static char * // O - Output string
make_code128(char *dst, // I - Destination buffer
const char *src, // I - Source string
size_t dstsize) // I - Size of destination buffer
{
char *dstptr, // Pointer into destination buffer
*dstend; // End of destination buffer
int sum; // Weighted sum
static const char *code128_chars = // Code 128 characters
" !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~\303"
"\304\305\306\307\310\311\312";
static const char code128_fnc_3 = '\304';
// FNC 3
static const char code128_fnc_2 = '\305';
// FNC 2
static const char code128_shift_b = '\306';
// Shift B (for lowercase)
static const char code128_code_c = '\307';
// Code C
static const char code128_code_b = '\310';
// Code B
static const char code128_fnc_4 = '\311';
// FNC 4
static const char code128_fnc_1 = '\312';
// FNC 1
static const char code128_start_code_a = '\313';
// Start code A
static const char code128_start_code_b = '\314';
// Start code B
static const char code128_start_code_c = '\315';
// Start code C
static const char code128_stop = '\316';
// Stop pattern
// Start a Code B barcode...
dstptr = dst;
dstend = dst + dstsize - 3;
*dstptr++ = code128_start_code_b;
sum = code128_start_code_b - 100;
while (*src && dstptr < dstend)
{
if (*src >= ' ' && *src < 0x7f)
{
sum += (dstptr - dst) * (*src - ' ');
*dstptr++ = *src;
}
src ++;
}
// Add the weighted sum modulo 103
*dstptr++ = code128_chars[sum % 103];
// Add the stop pattern and return...
*dstptr++ = code128_stop;
*dstptr = '\0';
return (dst);
}
//
// 'output_cb()' - Write PDF data to the standard output...
//
static ssize_t // O - Number of bytes written
output_cb(void *output_cbdata, // I - Callback data (not used)
const void *buffer, // I - Buffer to write
size_t bytes) // I - Number of bytes to write
{
(void)output_cbdata;
return ((ssize_t)fwrite(buffer, 1, bytes, stdout));
}
//
// 'main()' - Produce a single-page barcode file.
//
int // O - Exit status
main(int argc, // I - Number of command-line arguments
char *argv[]) // I - Command-line arguments
{
const char *barcode, // Barcode to show
*text; // Text to display under barcode
pdfio_file_t *pdf; // Output PDF file
pdfio_obj_t *barcode_font; // Barcode font object
pdfio_obj_t *text_font = NULL; // Text font object
pdfio_dict_t *page_dict; // Page dictionary
pdfio_rect_t media_box; // Media/CropBox for page
pdfio_stream_t *page_st; // Page stream
char barcode_temp[256]; // Barcode buffer
double barcode_height = 36.0, // Height of barcode
barcode_width, // Width of barcode
text_height = 0.0, // Height of text
text_width = 0.0; // Width of text
// Get the barcode and optional text from the command-line...
if (argc < 2 || argc > 3)
{
fputs("Usage: code128 \"BARCODE\" [\"TEXT\"] >FILENAME.pdf\n", stderr);
return (1);
}
barcode = argv[1];
text = argv[2];
// Output a PDF file to the standard output...
#ifdef _WIN32
setmode(1, O_BINARY); // Force binary output on Windows
#endif // _WIN32
if ((pdf = pdfioFileCreateOutput(output_cb, /*output_cbdata*/NULL, /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
return (1);
// Load fonts...
barcode_font = pdfioFileCreateFontObjFromFile(pdf, "code128.ttf", /*unicode*/false);
if (text)
text_font = pdfioFileCreateFontObjFromBase(pdf, "Helvetica");
// Generate Code128 characters for the desired barcode...
if (!(barcode[0] & 0x80))
barcode = make_code128(barcode_temp, barcode, sizeof(barcode_temp));
// Compute sizes of the text...
barcode_width = pdfioContentTextMeasure(barcode_font, barcode, barcode_height);
if (text && text_font)
{
text_height = 9.0;
text_width = pdfioContentTextMeasure(text_font, text, text_height);
}
// Compute the size of the PDF page...
media_box.x1 = 0.0;
media_box.y1 = 0.0;
media_box.x2 = (barcode_width > text_width ? barcode_width : text_width) + 18.0;
media_box.y2 = barcode_height + text_height + 18.0;
// Start a page for the barcode...
page_dict = pdfioDictCreate(pdf);
pdfioDictSetRect(page_dict, "MediaBox", &media_box);
pdfioDictSetRect(page_dict, "CropBox", &media_box);
pdfioPageDictAddFont(page_dict, "B128", barcode_font);
if (text_font)
pdfioPageDictAddFont(page_dict, "TEXT", text_font);
page_st = pdfioFileCreatePage(pdf, page_dict);
// Draw the page...
pdfioContentSetFillColorGray(page_st, 0.0);
pdfioContentSetTextFont(page_st, "B128", barcode_height);
pdfioContentTextBegin(page_st);
pdfioContentTextMoveTo(page_st, 0.5 * (media_box.x2 - barcode_width), 9.0 + text_height);
pdfioContentTextShow(page_st, /*unicode*/false, barcode);
pdfioContentTextEnd(page_st);
if (text && text_font)
{
pdfioContentSetTextFont(page_st, "TEXT", text_height);
pdfioContentTextBegin(page_st);
pdfioContentTextMoveTo(page_st, 0.5 * (media_box.x2 - text_width), 9.0);
pdfioContentTextShow(page_st, /*unicode*/false, text);
pdfioContentTextEnd(page_st);
}
pdfioStreamClose(page_st);
// Close and return...
pdfioFileClose(pdf);
return (0);
}

BIN
examples/code128.ttf Normal file

Binary file not shown.

148
examples/image2pdf.c Normal file
View File

@ -0,0 +1,148 @@
//
// Image example for PDFio.
//
// Copyright © 2023-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// Usage:
//
// ./image2pdf FILENAME.{jpg,png} FILENAME.pdf ["TEXT"]
//
#include <pdfio.h>
#include <pdfio-content.h>
#include <string.h>
//
// 'create_pdf_image_file()' - Create a PDF file of an image with optional caption.
//
bool // O - True on success, false on failure
create_pdf_image_file(
const char *imagename, // I - Image filename
const char *pdfname, // I - PDF filename
const char *caption) // I - Caption filename
{
pdfio_file_t *pdf; // PDF file
pdfio_obj_t *font; // Caption font
pdfio_obj_t *image; // Image
pdfio_dict_t *dict; // Page dictionary
pdfio_stream_t *page; // Page stream
double width, height; // Width and height of image
double swidth, sheight; // Scaled width and height on page
double tx, ty; // Position on page
// Default the caption...
if (!caption)
{
if ((caption = strrchr(imagename, '/')) != NULL)
caption ++;
else
caption = imagename;
}
// Create the PDF file...
pdf = pdfioFileCreate(pdfname, /*version*/NULL, /*media_box*/NULL,
/*crop_box*/NULL, /*error_cb*/NULL,
/*error_cbdata*/NULL);
if (!pdf)
return (false);
// Create a Courier base font for the caption
font = pdfioFileCreateFontObjFromBase(pdf, "Courier");
if (!font)
{
pdfioFileClose(pdf);
return (false);
}
// Create an image object from the JPEG/PNG image file...
image = pdfioFileCreateImageObjFromFile(pdf, imagename, true);
if (!image)
{
pdfioFileClose(pdf);
return (false);
}
// Create a page dictionary with the font and image...
dict = pdfioDictCreate(pdf);
pdfioPageDictAddFont(dict, "F1", font);
pdfioPageDictAddImage(dict, "IM1", image);
// Create the page and its content stream...
page = pdfioFileCreatePage(pdf, dict);
// Position and scale the image on the page...
width = pdfioImageGetWidth(image);
height = pdfioImageGetHeight(image);
// Default media_box is "universal" 595.28x792 points (8.27x11in or
// 210x279mm). Use margins of 36 points (0.5in or 12.7mm) with another
// 36 points for the caption underneath...
swidth = 595.28 - 72.0;
sheight = swidth * height / width;
if (sheight > (792.0 - 36.0 - 72.0))
{
sheight = 792.0 - 36.0 - 72.0;
swidth = sheight * width / height;
}
tx = 0.5 * (595.28 - swidth);
ty = 0.5 * (792 - 36 - sheight);
pdfioContentDrawImage(page, "IM1", tx, ty + 36.0, swidth, sheight);
// Draw the caption in black...
pdfioContentSetFillColorDeviceGray(page, 0.0);
// Compute the starting point for the text - Courier is monospaced
// with a nominal width of 0.6 times the text height...
tx = 0.5 * (595.28 - 18.0 * 0.6 * strlen(caption));
// Position and draw the caption underneath...
pdfioContentTextBegin(page);
pdfioContentSetTextFont(page, "F1", 18.0);
pdfioContentTextMoveTo(page, tx, ty);
pdfioContentTextShow(page, /*unicode*/false, caption);
pdfioContentTextEnd(page);
// Close the page stream and the PDF file...
pdfioStreamClose(page);
pdfioFileClose(pdf);
return (true);
}
//
// 'main()' - Produce a single-page file from an image.
//
int // O - Exit status
main(int argc, // I - Number of command-line arguments
char *argv[]) // I - Command-line arguments
{
const char *imagefile, // Image filename
*pdffile, // PDF filename
*caption; // Caption text
// Get the image file, PDF file, and optional caption text from the command-line...
if (argc < 3 || argc > 4)
{
fputs("Usage: image2pdf FILENAME.{jpg,png} FILENAME.pdf [\"TEXT\"]\n", stderr);
return (1);
}
imagefile = argv[1];
pdffile = argv[2];
caption = argv[3];
return (create_pdf_image_file(imagefile, pdffile, caption) ? 0 : 1);
}

1865
examples/md2pdf.c Normal file

File diff suppressed because it is too large Load Diff

84
examples/md2pdf.md Normal file
View File

@ -0,0 +1,84 @@
---
title: Markdown to PDF Converter Test File
...
Markdown to PDF Converter Test File
===================================
The `md2pdf` program is organized into three source files: `md2pdf.c` which
contains the code to format the markdown content and `mmd.h` and `mmd.c` (from
the [Miniature Markdown Library][MMD] project) which load the markdown content.
[MMD]: https://www.msweet.org/mmd/
This is a test file for `md2pdf`. Here is a bullet list:
- Embed base and TrueType fonts,
- Format text with embedded JPEG and PNG images and check boxes, with support
for wrapping, alignment in table cells, leader text (as used for lists), and
variable line height,
- Add headers and footers, and
- Add hyperlinks and document platform.
And here is an ordered list:
1. Embed base and TrueType fonts,
2. Format text with embedded JPEG and PNG images and check boxes, with support
for wrapping, alignment in table cells, leader text (as used for lists), and
variable line height,
3. Add headers and footers, and
4. Add hyperlinks and document platform.
Code Blocks
-----------
```
0 1 2 3 4 5 6 7 8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
```
Images
------
PDFio book cover image:
![PDFio](../doc/pdfio-epub.png)
Tables
------
Table with leading/trailing pipes:
| Heading 1 | Heading 2 | Heading 3 |
| --------- | --------- | --------- |
| Cell 1,1 | Cell 1,2 | Cell 1,3 |
| Cell 2,1 | Cell 2,2 | Cell 2,3 |
| Cell 3,1 | Cell 3,2 | Cell 3,3 |
Table without leading/trailing pipes:
Heading 1 | Heading 2 | Heading 3
--------- | --------- | ---------
Cell 1,1 | Cell 1,2 | Cell 1,3
Cell 2,1 | Cell 2,2 | Cell 2,3
Cell 3,1 | Cell 3,2 | Cell 3,3
Table with alignment:
Left Alignment | Center Alignment | Right Alignment
:-------- | :-------: | --------:
Cell 1,1 | Cell 1,2 | 1
Cell 2,1 | Cell 2,2 | 12
Cell 3,1 | Cell 3,2 | 123
Table in block quote:
> Heading 1 | Heading 2 | Heading 3
> --------- | --------- | ---------
> Cell 1,1 | Cell 1,2 | Cell 1,3
> Cell 2,1 | Cell 2,2 | Cell 2,3
> Cell 3,1 | Cell 3,2 | Cell 3,3

2381
examples/mmd.c Normal file

File diff suppressed because it is too large Load Diff

112
examples/mmd.h Normal file
View File

@ -0,0 +1,112 @@
//
// Header file for miniature markdown library.
//
// https://www.msweet.org/mmd
//
// Copyright © 2017-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
#ifndef MMD_H
# define MMD_H
# include <stdio.h>
# include <stdbool.h>
# ifdef __cplusplus
extern "C" {
# endif // __cplusplus
//
// Constants...
//
enum mmd_option_e
{
MMD_OPTION_NONE = 0x00, // No markdown extensions
MMD_OPTION_METADATA = 0x01, // Jekyll metadata extension
MMD_OPTION_TABLES = 0x02, // Github table extension
MMD_OPTION_TASKS = 0x04, // Github task item extension (check boxes)
MMD_OPTION_ALL = 0x07 // All supported markdown extensions
};
typedef unsigned mmd_option_t;
typedef enum mmd_type_e
{
MMD_TYPE_NONE = -1,
MMD_TYPE_DOCUMENT, // The document root
MMD_TYPE_METADATA, // Document metadata
MMD_TYPE_BLOCK_QUOTE, // <blockquote>
MMD_TYPE_ORDERED_LIST, // <ol>
MMD_TYPE_UNORDERED_LIST, // <ul>
MMD_TYPE_LIST_ITEM, // <li>
MMD_TYPE_TABLE, // <table>
MMD_TYPE_TABLE_HEADER, // <thead>
MMD_TYPE_TABLE_BODY, // <tbody>
MMD_TYPE_TABLE_ROW, // <tr>
MMD_TYPE_HEADING_1 = 10, // <h1>
MMD_TYPE_HEADING_2, // <h2>
MMD_TYPE_HEADING_3, // <h3>
MMD_TYPE_HEADING_4, // <h4>
MMD_TYPE_HEADING_5, // <h5>
MMD_TYPE_HEADING_6, // <h6>
MMD_TYPE_PARAGRAPH, // <p>
MMD_TYPE_CODE_BLOCK, // <pre><code>
MMD_TYPE_THEMATIC_BREAK, // <hr />
MMD_TYPE_TABLE_HEADER_CELL, // <th>
MMD_TYPE_TABLE_BODY_CELL_LEFT, // <td align="left">
MMD_TYPE_TABLE_BODY_CELL_CENTER, // <td align="center">
MMD_TYPE_TABLE_BODY_CELL_RIGHT, // <td align="right">
MMD_TYPE_NORMAL_TEXT = 100, // Normal text
MMD_TYPE_EMPHASIZED_TEXT, // <em>text</em>
MMD_TYPE_STRONG_TEXT, // <strong>text</strong>
MMD_TYPE_STRUCK_TEXT, // <del>text</del>
MMD_TYPE_LINKED_TEXT, // <a href="link">text</a>
MMD_TYPE_CODE_TEXT, // <code>text</code>
MMD_TYPE_IMAGE, // <img src="link" />
MMD_TYPE_HARD_BREAK, // <br />
MMD_TYPE_SOFT_BREAK, // <wbr />
MMD_TYPE_METADATA_TEXT, // name: value
MMD_TYPE_CHECKBOX // [ ] or [x]
} mmd_type_t;
//
// Types...
//
typedef struct _mmd_s mmd_t; // Markdown node
typedef size_t (*mmd_iocb_t)(void *cbdata, char *buffer, size_t bytes);
// mmdLoadIO callback function
//
// Functions...
//
extern char *mmdCopyAllText(mmd_t *node);
extern void mmdFree(mmd_t *node);
extern const char *mmdGetExtra(mmd_t *node);
extern mmd_t *mmdGetFirstChild(mmd_t *node);
extern mmd_t *mmdGetLastChild(mmd_t *node);
extern const char *mmdGetMetadata(mmd_t *doc, const char *keyword);
extern mmd_t *mmdGetNextSibling(mmd_t *node);
extern mmd_option_t mmdGetOptions(void);
extern mmd_t *mmdGetParent(mmd_t *node);
extern mmd_t *mmdGetPrevSibling(mmd_t *node);
extern const char *mmdGetText(mmd_t *node);
extern mmd_type_t mmdGetType(mmd_t *node);
extern const char *mmdGetURL(mmd_t *node);
extern bool mmdGetWhitespace(mmd_t *node);
extern bool mmdIsBlock(mmd_t *node);
extern mmd_t *mmdLoad(mmd_t *root, const char *filename);
extern mmd_t *mmdLoadFile(mmd_t *root, FILE *fp);
extern mmd_t *mmdLoadIO(mmd_t *root, mmd_iocb_t cb, void *cbdata);
extern mmd_t *mmdLoadString(mmd_t *root, const char *s);
extern void mmdSetOptions(mmd_option_t options);
# ifdef __cplusplus
}
# endif // __cplusplus
#endif // !MMD_H

1420
examples/pdf2text.c Normal file

File diff suppressed because it is too large Load Diff

162
examples/pdfioinfo.c Normal file
View File

@ -0,0 +1,162 @@
//
// PDF metadata example for PDFio.
//
// Copyright © 2023-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// Usage:
//
// ./pdfioinfo FILENAME.pdf
//
#include <pdfio.h>
#include <time.h>
#include <math.h>
//
// 'main()' - Open a PDF file and show its metadata.
//
int // O - Exit status
main(int argc, // I - Number of command-line arguments
char *argv[]) // Command-line arguments
{
const char *filename; // PDF filename
pdfio_file_t *pdf; // PDF file
pdfio_dict_t *catalog; // Catalog dictionary
const char *author, // Author name
*creator, // Creator name
*producer, // Producer name
*title; // Title
time_t creation_date, // Creation date
modification_date; // Modification date
struct tm *creation_tm, // Creation date/time information
*modification_tm; // Modification date/time information
char creation_text[256], // Creation date/time as a string
modification_text[256], // Modification date/time human fmt string
range_text[255]; // Page range text
size_t num_pages; // PDF number of pages
bool has_acroform; // Does the file have an AcroForm?
pdfio_obj_t *page; // Object
pdfio_dict_t *page_dict; // Object dictionary
size_t cur, // Current page index
prev; // Previous page index
pdfio_rect_t cur_box, // Current MediaBox
prev_box; // Previous MediaBox
// Get the filename from the command-line...
if (argc != 2)
{
fputs("Usage: ./pdfioinfo FILENAME.pdf\n", stderr);
return (1);
}
filename = argv[1];
// Open the PDF file with the default callbacks...
pdf = pdfioFileOpen(filename, /*password_cb*/NULL,
/*password_cbdata*/NULL, /*error_cb*/NULL,
/*error_cbdata*/NULL);
if (pdf == NULL)
return (1);
// Get the title, author, etc...
catalog = pdfioFileGetCatalog(pdf);
author = pdfioFileGetAuthor(pdf);
creator = pdfioFileGetCreator(pdf);
has_acroform = pdfioDictGetObj(catalog, "AcroForm") != NULL ? true : false;
num_pages = pdfioFileGetNumPages(pdf);
producer = pdfioFileGetProducer(pdf);
title = pdfioFileGetTitle(pdf);
// Get the creation date and convert to a string...
if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0)
{
creation_tm = localtime(&creation_date);
strftime(creation_text, sizeof(creation_text), "%c", creation_tm);
}
else
{
snprintf(creation_text, sizeof(creation_text), "-- not set --");
}
// Get the modification date and convert to a string...
if ((modification_date = pdfioFileGetModificationDate(pdf)) > 0)
{
modification_tm = localtime(&modification_date);
strftime(modification_text, sizeof(modification_text), "%c", modification_tm);
}
else
{
snprintf(modification_text, sizeof(modification_text), "-- not set --");
}
// Print file information to stdout...
printf("%s:\n", filename);
printf(" Title: %s\n", title ? title : "-- not set --");
printf(" Author: %s\n", author ? author : "-- not set --");
printf(" Creator: %s\n", creator ? creator : "-- not set --");
printf(" Producer: %s\n", producer ? producer : "-- not set --");
printf(" Created On: %s\n", creation_text);
printf(" Modified On: %s\n", modification_text);
printf(" Version: %s\n", pdfioFileGetVersion(pdf));
printf(" AcroForm: %s\n", has_acroform ? "Yes" : "No");
printf(" Number of Pages: %u\n", (unsigned)num_pages);
// Report the MediaBox for all of the pages
prev_box.x1 = prev_box.x2 = prev_box.y1 = prev_box.y2 = 0.0;
for (cur = 0, prev = 0; cur < num_pages; cur ++)
{
// Find the MediaBox for this page in the page tree...
for (page = pdfioFileGetPage(pdf, cur);
page != NULL;
page = pdfioDictGetObj(page_dict, "Parent"))
{
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
page_dict = pdfioObjGetDict(page);
if (pdfioDictGetRect(page_dict, "MediaBox", &cur_box))
break;
}
// If this MediaBox is different from the previous one, show the range of
// pages that have that size...
if (cur == 0 ||
fabs(cur_box.x1 - prev_box.x1) > 0.01 ||
fabs(cur_box.y1 - prev_box.y1) > 0.01 ||
fabs(cur_box.x2 - prev_box.x2) > 0.01 ||
fabs(cur_box.y2 - prev_box.y2) > 0.01)
{
if (cur > prev)
{
snprintf(range_text, sizeof(range_text), "Pages %u-%u",
(unsigned)(prev + 1), (unsigned)cur);
printf("%16s: [%g %g %g %g]\n", range_text,
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
}
// Start a new series of pages with the new size...
prev = cur;
prev_box = cur_box;
}
}
// Show the last range as needed...
if (cur > prev)
{
snprintf(range_text, sizeof(range_text), "Pages %u-%u",
(unsigned)(prev + 1), (unsigned)cur);
printf("%16s: [%g %g %g %g]\n", range_text,
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
}
// Close the PDF file...
pdfioFileClose(pdf);
return (0);
}

146
examples/pdfiomerge.c Normal file
View File

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

232
install-sh Executable file
View File

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

View File

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

View File

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

570
pdfio-aes.c Normal file
View File

@ -0,0 +1,570 @@
//
// AES functions for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// AES code is adapted from the "tiny-AES-c" project
// (<https://github.com/kokke/tiny-AES-c>)
//
#include "pdfio-private.h"
//
// Local types...
//
typedef uint8_t state_t[4][4]; // 4x4 AES state table @private@
//
// Local globals...
//
static const uint8_t sbox[256] = // S-box lookup table
{
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};
static const uint8_t rsbox[256] = // Reverse S-box lookup table
{
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};
// The round constant word array, Rcon[i], contains the values given by
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = // Round constants
{
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
};
//
// Local functions...
//
static void add_round_key(size_t round, state_t *state, const uint8_t *round_key);
static void sub_bytes(state_t *state);
static void shift_rows(state_t *state);
static uint8_t xtime(uint8_t x);
static void mix_columns(state_t *state);
static uint8_t multiply(uint8_t x, uint8_t y);
static void inv_mix_columns(state_t *state);
static void inv_sub_bytes(state_t *state);
static void inv_shift_rows(state_t *state);
static void cipher(state_t *state, const _pdfio_aes_t *ctx);
static void inv_cipher(state_t *state, const _pdfio_aes_t *ctx);
static void xor_with_iv(uint8_t *buf, const uint8_t *Iv);
//
// '_pdfioCryptoAESInit()' - Initialize an AES context.
//
void
_pdfioCryptoAESInit(
_pdfio_aes_t *ctx, // I - AES context
const uint8_t *key, // I - Key
size_t keylen, // I - Length of key (must be 16 or 32)
const uint8_t *iv) // I - 16-byte initialization vector
{
size_t i; // Looping var
uint8_t *rkptr0, // Previous round_key values
*rkptr, // Current round_key values
*rkend, // End of round_key values
tempa[4]; // Used for the column/row operations
size_t nwords = keylen / 4; // Number of 32-bit words in key
// Clear context
memset(ctx, 0, sizeof(_pdfio_aes_t));
ctx->round_size = keylen / 4 + 6;
// The first round key is the key itself.
memcpy(ctx->round_key, key, keylen);
// 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 ++)
{
if ((i % nwords) == 0)
{
// Shifts word left once - [a0,a1,a2,a3] becomes [a1,a2,a3,a0], then
// apply the S-box to each of the four bytes to produce an output word.
tempa[0] = sbox[rkptr[-3]] ^ Rcon[i / nwords];
tempa[1] = sbox[rkptr[-2]];
tempa[2] = sbox[rkptr[-1]];
tempa[3] = sbox[rkptr[-4]];
}
else if (keylen == 32 && (i % nwords) == 4)
{
// Apply the S-box to each of the four bytes to produce an output word.
tempa[0] = sbox[rkptr[-4]];
tempa[1] = sbox[rkptr[-3]];
tempa[2] = sbox[rkptr[-2]];
tempa[3] = sbox[rkptr[-1]];
}
else
{
// Use unshifted values without S-box...
tempa[0] = rkptr[-4];
tempa[1] = rkptr[-3];
tempa[2] = rkptr[-2];
tempa[3] = rkptr[-1];
}
// TODO: Optimize to incorporate this into previous steps
*rkptr++ = *rkptr0++ ^ tempa[0];
*rkptr++ = *rkptr0++ ^ tempa[1];
*rkptr++ = *rkptr0++ ^ tempa[2];
*rkptr++ = *rkptr0++ ^ tempa[3];
}
// Copy the initialization vector...
if (iv)
memcpy(ctx->iv, iv, sizeof(ctx->iv));
}
//
// '_pdfioCryptoAESDecrypt()' - Decrypt a block of bytes with AES.
//
// "inbuffer" and "outbuffer" can point to the same memory. Length must be a
// multiple of 16 bytes (excess is not decrypted).
//
size_t // O - Number of bytes in output buffer
_pdfioCryptoAESDecrypt(
_pdfio_aes_t *ctx, // I - AES context
uint8_t *outbuffer, // I - Output buffer
const uint8_t *inbuffer, // I - Input buffer
size_t len) // I - Number of bytes to decrypt
{
uint8_t next_iv[16]; // Next IV value
size_t outbytes = 0; // Output bytes
if (inbuffer != outbuffer)
{
// Not the most efficient, but we can optimize later - the sample AES code
// manipulates the data directly in memory and doesn't support separate
// input and output buffers...
memcpy(outbuffer, inbuffer, len);
}
while (len > 15)
{
memcpy(next_iv, outbuffer, 16);
inv_cipher((state_t *)outbuffer, ctx);
xor_with_iv(outbuffer, ctx->iv);
memcpy(ctx->iv, next_iv, 16);
outbuffer += 16;
len -= 16;
outbytes += 16;
}
return (outbytes);
}
//
// '_pdfioCryptoAESEncrypt()' - Encrypt a block of bytes with AES.
//
// "inbuffer" and "outbuffer" can point to the same memory. "outbuffer" must
// be a multiple of 16 bytes.
//
size_t // O - Number of bytes in output buffer
_pdfioCryptoAESEncrypt(
_pdfio_aes_t *ctx, // I - AES context
uint8_t *outbuffer, // I - Output buffer
const uint8_t *inbuffer, // I - Input buffer
size_t len) // I - Number of bytes to decrypt
{
uint8_t *iv = ctx->iv; // Current IV for CBC
size_t outbytes = 0; // Output bytes
if (len == 0)
return (0);
if (inbuffer != outbuffer)
{
// Not the most efficient, but we can optimize later - the sample AES code
// manipulates the data directly in memory and doesn't support separate
// input and output buffers...
memcpy(outbuffer, inbuffer, len);
}
while (len > 15)
{
xor_with_iv(outbuffer, iv);
cipher((state_t*)outbuffer, ctx);
iv = outbuffer;
outbuffer += 16;
len -= 16;
outbytes += 16;
}
if (len > 0)
{
// Pad the final buffer with (16 - len)...
memset(outbuffer + len, (int)(16 - len), 16 - len);
xor_with_iv(outbuffer, iv);
cipher((state_t*)outbuffer, ctx);
iv = outbuffer;
outbytes += 16;
}
/* store Iv in ctx for next call */
memcpy(ctx->iv, iv, 16);
return (outbytes);
}
//
// 'add_round_key()' - Add the round key to state.
//
// The round key is added to the state by an XOR function.
//
static void
add_round_key(size_t round, // I - Which round
state_t *state, // I - Current state
const uint8_t *round_key) // I - Key
{
unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state
for (round_key += round * 16, i = 16; i > 0; i --, sptr ++, round_key ++)
*sptr ^= *round_key;
}
//
// 'sub_bytes()' - Substitute the values in the state matrix with values in an S-box.
//
static void
sub_bytes(state_t *state) // I - Current state
{
unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state
for (i = 16; i > 0; i --, sptr ++)
*sptr = sbox[*sptr];
}
//
// 'shift_rows()' - Shift the rows in the state to the left.
//
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
//
static void
shift_rows(state_t *state) // I - Current state
{
uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t temp; // Temporary value
// Rotate first row 1 columns to left
temp = sptr[1];
sptr[1] = sptr[5];
sptr[5] = sptr[9];
sptr[9] = sptr[13];
sptr[13] = temp;
// Rotate second row 2 columns to left
temp = sptr[2];
sptr[2] = sptr[10];
sptr[10] = temp;
temp = sptr[6];
sptr[6] = sptr[14];
sptr[14] = temp;
// Rotate third row 3 columns to left
temp = sptr[3];
sptr[3] = sptr[15];
sptr[15] = sptr[11];
sptr[11] = sptr[7];
sptr[7] = temp;
}
//
// 'xtime()' - Compute the AES xtime function.
//
static uint8_t // O - xtime(x)
xtime(uint8_t x) // I - Column value
{
return ((uint8_t)((x << 1) ^ ((x >> 7) * 0x1b)));
}
//
// 'mix_columns()' - Mix the columns of the state matrix.
//
static void
mix_columns(state_t *state) // I - Current state
{
unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t Tmp, Tm, t; // Temporary values
for (i = 4; i > 0; i --, sptr += 4)
{
t = sptr[0];
Tmp = sptr[0] ^ sptr[1] ^ sptr[2] ^ sptr[3];
Tm = sptr[0] ^ sptr[1];
Tm = xtime(Tm);
sptr[0] ^= Tm ^ Tmp;
Tm = sptr[1] ^ sptr[2];
Tm = xtime(Tm);
sptr[1] ^= Tm ^ Tmp;
Tm = sptr[2] ^ sptr[3];
Tm = xtime(Tm);
sptr[2] ^= Tm ^ Tmp;
Tm = sptr[3] ^ t;
Tm = xtime(Tm);
sptr[3] ^= Tm ^ Tmp;
}
}
//
// 'multiply()' - Multiply numbers in the field GF(2^8)
//
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34
//
static uint8_t multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
//
// 'mix_columns()' - Mix the columns of the state matrix.
//
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
//
static void
inv_mix_columns(state_t *state) // I - Current state
{
unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t a, b, c, d; // Temporary values
for (i = 4; i > 0; i --)
{
a = sptr[0];
b = sptr[1];
c = sptr[2];
d = sptr[3];
*sptr++ = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^ multiply(c, 0x0d) ^ multiply(d, 0x09);
*sptr++ = multiply(a, 0x09) ^ multiply(b, 0x0e) ^ multiply(c, 0x0b) ^ multiply(d, 0x0d);
*sptr++ = multiply(a, 0x0d) ^ multiply(b, 0x09) ^ multiply(c, 0x0e) ^ multiply(d, 0x0b);
*sptr++ = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^ multiply(c, 0x09) ^ multiply(d, 0x0e);
}
}
//
// 'sub_bytes()' - Substitute the values in the state matrix with values in an S-box.
//
static void
inv_sub_bytes(state_t *state) // I - Current state
{
unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state
for (i = 16; i > 0; i --, sptr ++)
*sptr = rsbox[*sptr];
}
//
// 'inv_shift_rows()' - Shift the rows in the state to the right.
//
static void
inv_shift_rows(state_t *state) // I - Current state
{
uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t temp; // Temporary value
// Rotate first row 1 columns to right
temp = sptr[13];
sptr[13] = sptr[9];
sptr[9] = sptr[5];
sptr[5] = sptr[1];
sptr[1] = temp;
// Rotate second row 2 columns to right
temp = sptr[2];
sptr[2] = sptr[10];
sptr[10] = temp;
temp = sptr[6];
sptr[6] = sptr[14];
sptr[14] = temp;
// Rotate third row 3 columns to right
temp = sptr[3];
sptr[3] = sptr[7];
sptr[7] = sptr[11];
sptr[11] = sptr[15];
sptr[15] = temp;
}
//
// 'cipher()' - Encrypt the PlainText.
//
static void
cipher(state_t *state, // I - Current state
const _pdfio_aes_t *ctx) // I - AES context
{
size_t round = 0; // Current round
// Add the First round key to the state before starting the rounds.
add_round_key(0, state, ctx->round_key);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without mix_columns()
for (round = 1; round < ctx->round_size; round ++)
{
sub_bytes(state);
shift_rows(state);
mix_columns(state);
add_round_key(round, state, ctx->round_key);
}
// Add round key to last round
sub_bytes(state);
shift_rows(state);
add_round_key(ctx->round_size, state, ctx->round_key);
}
//
// 'inv_cipher()' - Decrypt the CipherText.
//
static void
inv_cipher(state_t *state, // I - Current state
const _pdfio_aes_t *ctx) // I - AES context
{
size_t round; // Current round
// Add the First round key to the state before starting the rounds.
add_round_key(ctx->round_size, state, ctx->round_key);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without InvMixColumn()
for (round = ctx->round_size - 1; ; round --)
{
inv_shift_rows(state);
inv_sub_bytes(state);
add_round_key(round, state, ctx->round_key);
if (round == 0)
break;
inv_mix_columns(state);
}
}
//
// 'xor_with_iv()' - XOR a block with the initialization vector.
//
static void
xor_with_iv(uint8_t *buf, // I - Block
const uint8_t *Iv) // I - Initialization vector
{
// 16-byte block...
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
}

View File

@ -1,16 +1,12 @@
//
// PDF array functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -331,6 +327,30 @@ pdfioArrayCreate(pdfio_file_t *pdf) // I - PDF file
}
//
// '_pdfioArrayDecrypt()' - Decrypt values in an array.
//
bool // O - `true` on success, `false` on error
_pdfioArrayDecrypt(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object
pdfio_array_t *a, // I - Array
size_t depth) // I - Depth
{
size_t i; // Looping var
_pdfio_value_t *v; // Current value
for (i = a->num_values, v = a->values; i > 0; i --, v ++)
{
if (!_pdfioValueDecrypt(pdf, obj, v, depth))
return (false);
}
return (true);
}
//
// '_pdfioArrayDebug()' - Print the contents of an array.
//
@ -343,6 +363,9 @@ _pdfioArrayDebug(pdfio_array_t *a, // I - Array
_pdfio_value_t *v; // Current value
if (!a)
return;
putc('[', fp);
for (i = a->num_values, v = a->values; i > 0; i --, v ++)
_pdfioValueDebug(v, fp);
@ -357,9 +380,16 @@ _pdfioArrayDebug(pdfio_array_t *a, // I - Array
void
_pdfioArrayDelete(pdfio_array_t *a) // I - Array
{
if (a)
free(a->values);
size_t i; // Looping var
for (i = 0; i < a->num_values; i ++)
{
if (a->values[i].type == PDFIO_VALTYPE_BINARY)
free(a->values[i].value.binary.data);
}
free(a->values);
free(a);
}
@ -389,17 +419,26 @@ pdfioArrayGetBinary(
size_t n, // I - Index
size_t *length) // O - Length of string
{
if (!a || n >= a->num_values || a->values[n].type != PDFIO_VALTYPE_BINARY || !length)
if (!a || n >= a->num_values || (a->values[n].type != PDFIO_VALTYPE_BINARY && a->values[n].type != PDFIO_VALTYPE_STRING))
{
if (length)
*length = 0;
return (NULL);
}
else if (a->values[n].type == PDFIO_VALTYPE_BINARY)
{
if (length)
*length = a->values[n].value.binary.datalen;
return (a->values[n].value.binary.data);
}
else
{
*length = a->values[n].value.binary.datalen;
return (a->values[n].value.binary.data);
if (length)
*length = strlen(a->values[n].value.string);
return ((unsigned char *)a->values[n].value.string);
}
}
@ -558,7 +597,9 @@ _pdfioArrayGetValue(pdfio_array_t *a, // I - Array
pdfio_array_t * // O - New array
_pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
_pdfio_token_t *tb) // I - Token buffer/stack
pdfio_obj_t *obj, // I - Object, if any
_pdfio_token_t *tb, // I - Token buffer/stack
size_t depth) // I - Depth of array
{
pdfio_array_t *array; // New array
char token[8192]; // Token from file
@ -568,7 +609,8 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
PDFIO_DEBUG("_pdfioArrayRead(pdf=%p, tb=%p)\n", pdf, tb);
// Create an array...
array = pdfioArrayCreate(pdf);
if ((array = pdfioArrayCreate(pdf)) == NULL)
return (NULL);
// Read until we get "]" to end the array...
while (_pdfioTokenGet(tb, token, sizeof(token)))
@ -581,7 +623,7 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
// Push the token and decode the value...
_pdfioTokenPush(tb, token);
if (!_pdfioValueRead(pdf, tb, &value))
if (!_pdfioValueRead(pdf, obj, tb, &value, depth))
break;
// PDFIO_DEBUG("_pdfioArrayRead(%p): Appending ", (void *)array);
@ -595,12 +637,35 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
}
//
// 'pdfioArrayRemove()' - Remove an array entry.
//
bool // O - `true` on success, `false` otherwise
pdfioArrayRemove(pdfio_array_t *a, // I - Array
size_t n) // I - Index
{
if (!a || n >= a->num_values)
return (false);
if (a->values[n].type == PDFIO_VALTYPE_BINARY)
free(a->values[n].value.binary.data);
a->num_values --;
if (n < a->num_values)
memmove(a->values + n, a->values + n + 1, (a->num_values - n) * sizeof(_pdfio_value_t));
return (true);
}
//
// '_pdfioArrayWrite()' - Write an array to a PDF file.
//
bool // O - `true` on success, `false` otherwise
_pdfioArrayWrite(pdfio_array_t *a) // I - Array
_pdfioArrayWrite(pdfio_array_t *a, // I - Array
pdfio_obj_t *obj) // I - Object, if any
{
pdfio_file_t *pdf = a->pdf; // PDF file
size_t i; // Looping var
@ -614,7 +679,7 @@ _pdfioArrayWrite(pdfio_array_t *a) // I - Array
// Write each value...
for (i = a->num_values, v = a->values; i > 0; i --, v ++)
{
if (!_pdfioValueWrite(pdf, v, NULL))
if (!_pdfioValueWrite(pdf, obj, v, NULL))
return (false);
}

308
pdfio-base-font-widths.h Normal file
View File

@ -0,0 +1,308 @@
//
// PDF base font widths for PDFio.
//
// Copyright © 2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
#ifndef PDFIO_BASE_FONT_WIDTHS_H
# define PDFIO_BASE_FONT_WIDTHS_H 1
static short courier_bold_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0,
600, 0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0, 600, 0,
0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0, 600, 600,
0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600
};
static short courier_boldoblique_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0,
600, 0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0, 600, 0,
0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0, 600, 600,
0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600
};
static short courier_oblique_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0,
600, 0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0, 600, 0,
0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0, 600, 600,
0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600
};
static short courier_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0,
600, 0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0, 600, 0,
0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 0, 600, 600,
0, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600
};
static short helvetica_bold_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278,
556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611,
975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778,
667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556,
333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611,
611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 0,
556, 0, 278, 556, 500, 1000, 556, 556, 333, 1000, 667, 333, 1000, 0, 611, 0,
0, 278, 278, 500, 500, 350, 556, 1000, 333, 1000, 556, 333, 944, 0, 500, 667,
0, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 584, 737, 333,
606, 584, 351, 351, 333, 611, 556, 278, 333, 351, 365, 556, 869, 869, 869, 611,
722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278,
722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611,
556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278,
611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556
};
static short helvetica_boldoblique_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
278, 333, 474, 556, 556, 889, 722, 238, 333, 333, 389, 584, 278, 333, 278, 278,
556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333, 584, 584, 584, 611,
975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556, 722, 611, 833, 722, 778,
667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 333, 278, 333, 584, 556,
333, 556, 611, 556, 611, 556, 333, 611, 611, 278, 278, 556, 278, 889, 611, 611,
611, 611, 389, 556, 333, 611, 556, 778, 556, 556, 500, 389, 280, 389, 584, 0,
556, 0, 278, 556, 500, 1000, 556, 556, 333, 1000, 667, 333, 1000, 0, 611, 0,
0, 278, 278, 500, 500, 350, 556, 1000, 333, 1000, 556, 333, 944, 0, 500, 667,
0, 333, 556, 556, 556, 556, 280, 556, 333, 737, 370, 556, 584, 584, 737, 333,
606, 584, 444, 444, 333, 611, 556, 278, 333, 444, 365, 556, 1055, 1055, 1055, 611,
722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278,
722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611,
556, 556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278,
611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611, 556
};
static short helvetica_oblique_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278,
556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556,
1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778,
667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556,
333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556,
556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 0,
556, 0, 222, 556, 333, 1000, 556, 556, 333, 1000, 667, 333, 1000, 0, 611, 0,
0, 222, 222, 333, 333, 350, 556, 1000, 333, 1000, 500, 333, 944, 0, 500, 667,
0, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 584, 737, 333,
606, 584, 390, 390, 333, 556, 537, 278, 333, 390, 365, 556, 947, 947, 947, 611,
667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278,
722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611,
556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278,
556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556, 500
};
static short helvetica_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
278, 278, 355, 556, 556, 889, 667, 191, 333, 333, 389, 584, 278, 333, 278, 278,
556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278, 584, 584, 584, 556,
1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500, 667, 556, 833, 722, 778,
667, 778, 722, 667, 611, 722, 667, 944, 667, 667, 611, 278, 278, 278, 469, 556,
333, 556, 556, 500, 556, 556, 278, 556, 556, 222, 222, 500, 222, 833, 556, 556,
556, 556, 333, 500, 278, 556, 500, 722, 500, 500, 500, 334, 260, 334, 584, 0,
556, 0, 222, 556, 333, 1000, 556, 556, 333, 1000, 667, 333, 1000, 0, 611, 0,
0, 222, 221, 333, 333, 350, 556, 1000, 333, 1000, 500, 333, 944, 0, 500, 667,
0, 333, 556, 556, 556, 556, 260, 556, 333, 737, 370, 556, 584, 584, 737, 333,
606, 584, 351, 351, 333, 556, 537, 278, 333, 351, 365, 556, 869, 869, 869, 611,
667, 667, 667, 667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278,
722, 722, 778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 666, 666, 611,
556, 556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278,
556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 555, 500
};
static short symbol_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
250, 333, 713, 500, 549, 833, 778, 439, 333, 333, 500, 549, 250, 549, 250, 278,
500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 549, 549, 549, 444,
549, 722, 667, 722, 612, 611, 763, 603, 722, 333, 631, 722, 686, 889, 722, 722,
768, 741, 556, 592, 611, 690, 439, 768, 645, 795, 611, 333, 863, 333, 658, 500,
500, 631, 549, 549, 494, 439, 521, 411, 603, 329, 603, 549, 549, 576, 521, 549,
549, 521, 549, 603, 439, 576, 713, 686, 493, 686, 494, 480, 200, 480, 549, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
762, 620, 247, 549, 167, 713, 500, 753, 753, 753, 753, 1042, 987, 603, 987, 603,
400, 549, 411, 549, 549, 713, 494, 460, 549, 549, 549, 549, 1000, 603, 1000, 658,
823, 686, 795, 987, 768, 768, 823, 768, 768, 713, 713, 713, 713, 713, 713, 713,
768, 713, 790, 790, 890, 823, 549, 250, 713, 603, 603, 1042, 987, 603, 987, 603,
494, 329, 790, 790, 786, 713, 384, 384, 384, 384, 384, 384, 494, 494, 494, 494,
0, 329, 274, 686, 686, 686, 384, 384, 384, 384, 384, 384, 494, 494, 494, 0
};
static short times_bold_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
250, 333, 555, 500, 500, 1000, 833, 278, 333, 333, 500, 570, 250, 333, 250, 278,
500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500,
930, 722, 667, 722, 722, 667, 611, 778, 778, 389, 500, 778, 667, 944, 722, 778,
611, 778, 722, 556, 667, 722, 722, 1000, 722, 722, 667, 333, 278, 333, 581, 500,
333, 500, 556, 444, 556, 444, 333, 500, 556, 278, 333, 556, 278, 833, 556, 500,
556, 556, 444, 389, 333, 556, 500, 722, 500, 500, 444, 394, 220, 394, 520, 0,
500, 0, 333, 500, 500, 1000, 500, 500, 333, 1000, 556, 333, 1000, 0, 667, 0,
0, 333, 333, 500, 500, 350, 500, 1000, 333, 1000, 389, 333, 722, 0, 444, 722,
0, 333, 500, 500, 500, 500, 220, 500, 333, 747, 300, 500, 570, 570, 747, 333,
400, 570, 300, 300, 333, 556, 540, 250, 333, 300, 330, 500, 750, 750, 750, 500,
722, 722, 722, 722, 722, 722, 1000, 722, 667, 667, 667, 667, 389, 389, 389, 389,
722, 722, 778, 778, 778, 778, 778, 570, 778, 722, 722, 722, 722, 722, 611, 556,
500, 500, 500, 500, 500, 500, 722, 444, 444, 444, 444, 444, 278, 278, 278, 278,
500, 556, 500, 500, 500, 500, 500, 570, 500, 556, 556, 556, 556, 500, 556, 500
};
static short times_bolditalic_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
250, 389, 555, 500, 500, 833, 778, 278, 333, 333, 500, 570, 250, 333, 250, 278,
500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 570, 570, 570, 500,
832, 667, 667, 667, 722, 667, 667, 722, 778, 389, 500, 667, 611, 889, 722, 722,
611, 722, 667, 556, 611, 722, 667, 889, 667, 611, 611, 333, 278, 333, 570, 500,
333, 500, 500, 444, 500, 444, 333, 500, 556, 278, 278, 500, 278, 778, 556, 500,
500, 500, 389, 389, 278, 556, 444, 667, 500, 444, 389, 348, 220, 348, 570, 0,
500, 0, 333, 500, 500, 1000, 500, 500, 333, 1000, 556, 333, 944, 0, 611, 0,
0, 333, 333, 500, 500, 350, 500, 1000, 333, 1000, 389, 333, 722, 0, 389, 611,
0, 389, 500, 500, 500, 500, 220, 500, 333, 747, 266, 500, 606, 606, 747, 333,
400, 570, 300, 300, 333, 576, 500, 250, 333, 300, 300, 500, 750, 750, 750, 500,
667, 667, 667, 667, 667, 667, 944, 667, 667, 667, 667, 667, 389, 389, 389, 389,
722, 722, 722, 722, 722, 722, 722, 570, 722, 722, 722, 722, 722, 611, 611, 500,
500, 500, 500, 500, 500, 500, 722, 444, 444, 444, 444, 444, 278, 278, 278, 278,
500, 556, 500, 500, 500, 500, 500, 570, 500, 556, 556, 556, 556, 444, 500, 444
};
static short times_italic_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
250, 333, 420, 500, 500, 833, 778, 214, 333, 333, 500, 675, 250, 333, 250, 278,
500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333, 675, 675, 675, 500,
920, 611, 611, 667, 722, 611, 611, 722, 722, 333, 444, 667, 556, 833, 667, 722,
611, 722, 611, 500, 556, 722, 611, 833, 611, 556, 556, 389, 278, 389, 422, 500,
333, 500, 500, 444, 500, 444, 278, 500, 500, 278, 278, 444, 278, 722, 500, 500,
500, 500, 389, 389, 278, 500, 444, 667, 444, 444, 389, 400, 275, 400, 541, 0,
500, 0, 333, 500, 556, 889, 500, 500, 333, 1000, 500, 333, 944, 0, 556, 0,
0, 333, 333, 556, 556, 350, 500, 889, 333, 980, 389, 333, 667, 0, 389, 556,
0, 389, 500, 500, 500, 500, 275, 500, 333, 760, 276, 500, 675, 675, 760, 333,
400, 675, 300, 300, 333, 500, 523, 250, 333, 300, 310, 500, 750, 750, 750, 500,
611, 611, 611, 611, 611, 611, 889, 667, 611, 611, 611, 611, 333, 333, 333, 333,
722, 667, 722, 722, 722, 722, 722, 675, 722, 722, 722, 722, 722, 556, 611, 500,
500, 500, 500, 500, 500, 500, 667, 444, 444, 444, 444, 444, 278, 278, 278, 278,
500, 500, 500, 500, 500, 500, 500, 675, 500, 500, 500, 500, 500, 444, 500, 444
};
static short times_roman_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
250, 333, 408, 500, 500, 833, 778, 180, 333, 333, 500, 564, 250, 333, 250, 278,
500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 564, 564, 564, 444,
921, 722, 667, 667, 722, 611, 556, 722, 722, 333, 389, 722, 611, 889, 722, 722,
556, 722, 667, 556, 611, 722, 722, 944, 722, 722, 611, 333, 278, 333, 469, 500,
333, 444, 500, 444, 500, 444, 333, 500, 500, 278, 278, 500, 278, 778, 500, 500,
500, 500, 333, 389, 278, 500, 500, 722, 500, 500, 444, 480, 200, 480, 541, 0,
500, 0, 333, 500, 444, 1000, 500, 500, 333, 1000, 556, 333, 889, 0, 611, 0,
0, 333, 333, 444, 444, 350, 500, 1000, 333, 980, 389, 333, 722, 0, 444, 722,
0, 333, 500, 500, 500, 500, 200, 500, 333, 760, 276, 500, 564, 564, 760, 333,
400, 564, 300, 300, 333, 500, 453, 250, 333, 300, 310, 500, 750, 750, 750, 444,
722, 722, 722, 722, 722, 722, 889, 667, 611, 611, 611, 611, 333, 333, 333, 333,
722, 722, 722, 722, 722, 722, 722, 564, 722, 722, 722, 722, 722, 722, 556, 500,
444, 444, 444, 444, 444, 444, 667, 444, 444, 444, 444, 444, 278, 278, 278, 278,
500, 500, 500, 500, 500, 500, 500, 564, 500, 500, 500, 500, 500, 500, 500, 500
};
static short zapfdingbats_widths[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
278, 974, 961, 974, 980, 719, 789, 790, 791, 690, 960, 939, 549, 855, 911, 933,
911, 945, 974, 755, 846, 762, 761, 571, 677, 763, 760, 759, 754, 494, 552, 537,
577, 692, 786, 788, 788, 790, 793, 794, 816, 823, 789, 841, 823, 833, 816, 831,
923, 744, 723, 749, 790, 792, 695, 776, 768, 792, 759, 707, 708, 682, 701, 826,
815, 789, 789, 707, 687, 696, 689, 786, 787, 713, 791, 785, 791, 873, 761, 762,
762, 759, 759, 892, 892, 788, 784, 438, 138, 277, 415, 392, 392, 668, 668, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 732, 544, 544, 910, 667, 760, 760, 776, 595, 694, 626, 788, 788, 788, 788,
788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788,
788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788, 788,
788, 788, 788, 788, 894, 838, 1016, 458, 748, 924, 748, 918, 927, 928, 928, 834,
873, 828, 924, 924, 917, 930, 931, 463, 883, 836, 836, 867, 867, 696, 696, 874,
0, 874, 760, 946, 771, 865, 771, 888, 967, 888, 831, 873, 927, 970, 918, 0
};
#endif // !PDFIO_BASE_FONT_WIDTHS_H

View File

@ -1,16 +1,12 @@
//
// Common support functions for pdfio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -38,6 +34,8 @@ _pdfioFileConsume(pdfio_file_t *pdf, // I - PDF file
else if (_pdfioFileSeek(pdf, (off_t)bytes, SEEK_CUR) < 0)
return (false);
PDFIO_DEBUG("_pdfioFileConsume: pos=%ld\n", (long)(pdf->bufpos + pdf->bufptr - pdf->buffer));
return (true);
}
@ -100,7 +98,7 @@ _pdfioFileFlush(pdfio_file_t *pdf) // I - PDF file
if (!write_buffer(pdf, pdf->buffer, (size_t)(pdf->bufptr - pdf->buffer)))
return (false);
pdf->bufpos += pdf->bufptr - pdf->buffer;
pdf->bufpos += (off_t)(pdf->bufptr - pdf->buffer);
}
pdf->bufptr = pdf->buffer;
@ -143,7 +141,7 @@ _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
*bufend = buffer + bufsize - 1; // Pointer to end of buffer
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend);
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p, offset=%lu\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend, (unsigned long)(pdf->bufpos + (pdf->bufptr - pdf->buffer)));
while (!eol)
{
@ -218,7 +216,7 @@ _pdfioFilePeek(pdfio_file_t *pdf, // I - PDF file
PDFIO_DEBUG("_pdfioFilePeek: Sliding buffer, total=%ld\n", (long)total);
memmove(pdf->buffer, pdf->bufptr, total);
pdf->bufpos += pdf->bufptr - pdf->buffer;
pdf->bufpos += (off_t)(pdf->bufptr - pdf->buffer);
pdf->bufptr = pdf->buffer;
pdf->bufend = pdf->buffer + total;
@ -263,7 +261,7 @@ _pdfioFilePrintf(pdfio_file_t *pdf, // I - PDF file
// Format the string...
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
_pdfio_vsnprintf(pdf, buffer, sizeof(buffer), format, ap);
va_end(ap);
// Write it...
@ -319,16 +317,21 @@ _pdfioFileRead(pdfio_file_t *pdf, // I - PDF file
// Advance current position in file as needed...
if (pdf->bufend)
{
pdf->bufpos += pdf->bufend - pdf->buffer;
pdf->bufpos += (off_t)(pdf->bufend - pdf->buffer);
pdf->bufptr = pdf->bufend = NULL;
}
// Read directly from the file...
if ((rbytes = read_buffer(pdf, bufptr, bytes)) > 0)
{
pdf->bufpos += rbytes;
pdf->bufpos += (off_t)rbytes;
continue;
}
else if (rbytes < 0 && (errno == EINTR || errno == EAGAIN))
{
rbytes = 0;
continue;
}
else
break;
}
@ -353,30 +356,35 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
off_t offset, // I - Offset
int whence) // I - Offset base
{
PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d)\n", pdf, (long)offset, whence);
PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d) pdf->bufpos=%lu\n", pdf, (long)offset, whence, (unsigned long)(pdf ? pdf->bufpos : 0));
// Adjust offset for relative seeks...
if (whence == SEEK_CUR)
{
offset += pdf->bufpos;
offset += pdf->bufpos + (off_t)(pdf->bufptr - pdf->buffer);
whence = SEEK_SET;
}
if (pdf->mode == _PDFIO_MODE_READ)
{
// Reading, see if we already have the data we need...
if (whence != SEEK_END && offset >= pdf->bufpos && offset < (pdf->bufpos + pdf->bufend - pdf->buffer))
if (whence != SEEK_END && offset >= pdf->bufpos && pdf->bufend && offset < (off_t)(pdf->bufpos + pdf->bufend - pdf->buffer))
{
// Yes, seek within existing buffer...
pdf->bufptr = pdf->buffer + offset - pdf->bufpos;
pdf->bufptr = pdf->buffer + (offset - pdf->bufpos);
PDFIO_DEBUG("_pdfioFileSeek: Seek within buffer, bufpos=%ld.\n", (long)pdf->bufpos);
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p, bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufend);
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p(<%02X%02X...>), bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufptr[0] & 255, pdf->bufptr[1] & 255, pdf->bufend);
return (offset);
}
// No, reset the read buffer
pdf->bufptr = pdf->bufend = NULL;
}
else if (pdf->output_cb)
{
_pdfioFileError(pdf, "Unable to seek within output stream.");
return (-1);
}
else
{
// Writing, make sure we write any buffered data...
@ -390,13 +398,16 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
}
// Seek within the file...
if ((offset = lseek(pdf->fd, offset, whence)) < 0)
if ((offset = lseek(pdf->fd, offset, whence)) < 0 && whence == SEEK_END && errno == EINVAL)
offset = lseek(pdf->fd, 0, SEEK_SET);
if (offset < 0)
{
_pdfioFileError(pdf, "Unable to seek within file - %s", strerror(errno));
return (-1);
}
PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld.\n", (long)pdf->bufpos);
PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld, offset=%lu.\n", (long)pdf->bufpos, (unsigned long)offset);
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p, bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufend);
pdf->bufpos = offset;
@ -515,7 +526,7 @@ read_buffer(pdfio_file_t *pdf, // I - PDF file
return (rbytes);
}
//
// 'write_buffer()' - Write a buffer to a PDF file.
//
@ -530,25 +541,37 @@ write_buffer(pdfio_file_t *pdf, // I - PDF file
ssize_t wbytes; // Bytes written...
// Write to the file...
while (bytes > 0)
if (pdf->output_cb)
{
while ((wbytes = write(pdf->fd, bufptr, bytes)) < 0)
// Write to a stream...
if ((pdf->output_cb)(pdf->output_ctx, buffer, bytes) < 0)
{
// Stop if we have an error that shouldn't be retried...
if (errno != EINTR && errno != EAGAIN)
break;
}
if (wbytes < 0)
{
// Hard error...
_pdfioFileError(pdf, "Unable to write to file - %s", strerror(errno));
_pdfioFileError(pdf, "Unable to write to output callback.");
return (false);
}
}
else
{
// Write to the file...
while (bytes > 0)
{
while ((wbytes = write(pdf->fd, bufptr, bytes)) < 0)
{
// Stop if we have an error that shouldn't be retried...
if (errno != EINTR && errno != EAGAIN)
break;
}
bufptr += wbytes;
bytes -= (size_t)wbytes;
if (wbytes < 0)
{
// Hard error...
_pdfioFileError(pdf, "Unable to write to file - %s", strerror(errno));
return (false);
}
bufptr += wbytes;
bytes -= (size_t)wbytes;
}
}
return (true);

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
//
// Public content header file for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2023 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -9,18 +9,7 @@
#ifndef PDFIO_CONTENT_H
# define PDFIO_CONTENT_H
//
// Include necessary headers...
//
# include "pdfio.h"
//
// C++ magic...
//
# ifdef __cplusplus
extern "C" {
# endif // __cplusplus
@ -91,12 +80,13 @@ extern bool pdfioContentPathClose(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentPathCurve(pdfio_stream_t *st, double x1, double y1, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC;
extern bool pdfioContentPathCurve13(pdfio_stream_t *st, double x1, double y1, double x3, double y3) _PDFIO_PUBLIC;
extern bool pdfioContentPathCurve23(pdfio_stream_t *st, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC;
extern bool pdfioContentPathEnd(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentPathLineTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC;
extern bool pdfioContentPathMoveTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC;
extern bool pdfioContentPathRect(pdfio_stream_t *st, double x, double y, double width, double height) _PDFIO_PUBLIC;
extern bool pdfioContentRestore(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentSave(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentSetDashPattern(pdfio_stream_t *st, int phase, int on, int off) _PDFIO_PUBLIC;
extern bool pdfioContentSetDashPattern(pdfio_stream_t *st, double phase, double on, double off) _PDFIO_PUBLIC;
extern bool pdfioContentSetFillColorDeviceCMYK(pdfio_stream_t *st, double c, double m, double y, double k) _PDFIO_PUBLIC;
extern bool pdfioContentSetFillColorDeviceGray(pdfio_stream_t *st, double g) _PDFIO_PUBLIC;
extern bool pdfioContentSetFillColorDeviceRGB(pdfio_stream_t *st, double r, double g, double b) _PDFIO_PUBLIC;
@ -125,16 +115,21 @@ extern bool pdfioContentSetTextXScaling(pdfio_stream_t *st, double percent) _PD
extern bool pdfioContentStroke(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentTextBegin(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentTextEnd(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern double pdfioContentTextMeasure(pdfio_obj_t *font, const char *s, double size) _PDFIO_PUBLIC;
extern bool pdfioContentTextMoveLine(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
extern bool pdfioContentTextMoveTo(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
extern bool pdfioContentTextNewLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentTextNewLineShow(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *s) _PDFIO_PUBLIC;
extern bool pdfioContentTextNewLineShowf(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *format, ...) _PDFIO_PUBLIC;
extern bool pdfioContentTextNextLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentTextShow(pdfio_stream_t *st, bool unicode, const char *s) _PDFIO_PUBLIC;
extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4);
extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC;
extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, size_t num_fragments, const double *offsets, const char * const *fragments) _PDFIO_PUBLIC;
// Resource helpers...
extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateICCObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t datalen, size_t num_colors) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateICCObjFromFile(pdfio_file_t *pdf, const char *filename, size_t num_colors) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateImageObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t width, size_t height, size_t num_colors, pdfio_array_t *color_data, bool alpha, bool interpolate) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateImageObjFromFile(pdfio_file_t *pdf, const char *filename, bool interpolate) _PDFIO_PUBLIC;
@ -150,10 +145,6 @@ extern bool pdfioPageDictAddFont(pdfio_dict_t *dict, const char *name, pdfio_ob
extern bool pdfioPageDictAddImage(pdfio_dict_t *dict, const char *name, pdfio_obj_t *obj) _PDFIO_PUBLIC;
//
// C++ magic...
//
# ifdef __cplusplus
}
# endif // __cplusplus

1120
pdfio-crypto.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,12 @@
//
// PDF dictionary functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -21,6 +17,49 @@
static int compare_pairs(_pdfio_pair_t *a, _pdfio_pair_t *b);
//
// 'pdfioDictClear()' - Remove a key/value pair from a dictionary.
//
bool // O - `true` if cleared, `false` otherwise
pdfioDictClear(pdfio_dict_t *dict, // I - Dictionary
const char *key) // I - Key
{
size_t idx; // Index into pairs
_pdfio_pair_t *pair, // Current pair
pkey; // Search key
PDFIO_DEBUG("pdfioDictClear(dict=%p, key=\"%s\")\n", dict, key);
if (!dict || !key)
return (false);
// See if the key is already set...
if (dict->num_pairs > 0)
{
pkey.key = key;
if ((pair = (_pdfio_pair_t *)bsearch(&pkey, dict->pairs, dict->num_pairs, sizeof(_pdfio_pair_t), (int (*)(const void *, const void *))compare_pairs)) != NULL)
{
// Yes, remove it...
if (pair->value.type == PDFIO_VALTYPE_BINARY)
free(pair->value.value.binary.data);
idx = (size_t)(pair - dict->pairs);
dict->num_pairs --;
if (idx < dict->num_pairs)
memmove(pair, pair + 1, (dict->num_pairs - idx) * sizeof(_pdfio_pair_t));
return (true);
}
}
return (false);
}
//
// 'pdfioDictCopy()' - Copy a dictionary to a PDF file.
//
@ -126,6 +165,30 @@ pdfioDictCreate(pdfio_file_t *pdf) // I - PDF file
}
//
// '_pdfioDictDecrypt()' - Decrypt the values in a dictionary.
//
bool // O - `true` on success, `false` on error
_pdfioDictDecrypt(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object
pdfio_dict_t *dict, // I - Dictionary
size_t depth) // I - Depth
{
size_t i; // Looping var
_pdfio_pair_t *pair; // Current pair
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{
if (strcmp(pair->key, "ID") && !_pdfioValueDecrypt(pdf, obj, &pair->value, depth + 1))
return (false);
}
return (true);
}
//
// '_pdfioDictDebug()' - Dump a dictionary to stderr.
//
@ -138,6 +201,9 @@ _pdfioDictDebug(pdfio_dict_t *dict, // I - Dictionary
_pdfio_pair_t *pair; // Current pair
if (!dict)
return;
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{
fprintf(fp, "/%s", pair->key);
@ -154,7 +220,18 @@ void
_pdfioDictDelete(pdfio_dict_t *dict) // I - Dictionary
{
if (dict)
{
size_t i; // Looping var
_pdfio_pair_t *pair; // Current pair
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{
if (pair->value.type == PDFIO_VALTYPE_BINARY)
free(pair->value.value.binary.data);
}
free(dict->pairs);
}
free(dict);
}
@ -198,6 +275,11 @@ pdfioDictGetBinary(pdfio_dict_t *dict, // I - Dictionary
*length = value->value.binary.datalen;
return (value->value.binary.data);
}
else if (value && value->type == PDFIO_VALTYPE_STRING)
{
*length = strlen(value->value.string);
return ((unsigned char *)value->value.string);
}
else
{
*length = 0;
@ -260,6 +342,18 @@ pdfioDictGetDict(pdfio_dict_t *dict, // I - Dictionary
}
//
// 'pdfioDictGetKey()' - Get the key for the specified pair.
//
const char * // O - Key for specified pair
pdfioDictGetKey(pdfio_dict_t *dict, // I - Dictionary
size_t n) // I - Pair index (`0`-based)
{
return ((dict && n < dict->num_pairs) ? dict->pairs[n].key : NULL);
}
//
// 'pdfioDictGetName()' - Get a key name value from a dictionary.
//
@ -278,6 +372,17 @@ pdfioDictGetName(pdfio_dict_t *dict, // I - Dictionary
}
//
// 'pdfioDictGetNumPairs()' - Get the number of key/value pairs in a dictionary.
//
size_t // O - Number of pairs
pdfioDictGetNumPairs(pdfio_dict_t *dict)// I - Dictionary
{
return (dict ? dict->num_pairs : 0);
}
//
// 'pdfioDictGetNumber()' - Get a key number value from a dictionary.
//
@ -354,9 +459,151 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
if (value && value->type == PDFIO_VALTYPE_STRING)
{
return (value->value.string);
}
else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096)
{
// Convert binary string to regular string...
char temp[4096], // Temporary string
*tempptr; // Pointer into temporary string
unsigned char *dataptr; // Pointer into the data string
if (!(value->value.binary.datalen & 1) && !memcmp(value->value.binary.data, "\377\376", 2))
{
// Copy UTF-16 BE
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[0] << 8) | dataptr[1];
if (ch >= 0xd800 && ch <= 0xdbff && remaining > 3)
{
// Multi-word UTF-16 char...
int lch; // Lower bits
lch = (dataptr[2] << 8) | dataptr[3];
if (lch < 0xdc00 || lch >= 0xdfff)
break;
ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
dataptr += 2;
remaining -= 2;
}
else if (ch >= 0xfffe)
{
continue;
}
if (ch < 128)
{
// ASCII
*tempptr++ = (char)ch;
}
else if (ch < 4096)
{
// 2-byte UTF-8
*tempptr++ = (char)(0xc0 | (ch >> 6));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else if (ch < 65536)
{
// 3-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 12));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else
{
// 4-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 18));
*tempptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
}
*tempptr = '\0';
}
else if (!(value->value.binary.datalen & 1) && !memcmp(value->value.binary.data, "\376\377", 2))
{
// Copy UTF-16 LE
int ch; // Unicode character
size_t remaining; // Remaining bytes
for (dataptr = value->value.binary.data + 2, remaining = value->value.binary.datalen - 2, tempptr = temp; remaining > 1 && tempptr < (temp + sizeof(temp) - 5); dataptr += 2, remaining -= 2)
{
ch = (dataptr[1] << 8) | dataptr[0];
if (ch >= 0xd800 && ch <= 0xdbff && remaining > 3)
{
// Multi-word UTF-16 char...
int lch; // Lower bits
lch = (dataptr[3] << 8) | dataptr[2];
if (lch < 0xdc00 || lch >= 0xdfff)
break;
ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
dataptr += 2;
remaining -= 2;
}
else if (ch >= 0xfffe)
{
continue;
}
if (ch < 128)
{
// ASCII
*tempptr++ = (char)ch;
}
else if (ch < 4096)
{
// 2-byte UTF-8
*tempptr++ = (char)(0xc0 | (ch >> 6));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else if (ch < 65536)
{
// 3-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 12));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else
{
// 4-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 18));
*tempptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
}
*tempptr = '\0';
}
else
{
// Copy as-is...
memcpy(temp, value->value.binary.data, value->value.binary.datalen);
temp[value->value.binary.datalen] = '\0';
}
free(value->value.binary.data);
value->type = PDFIO_VALTYPE_STRING;
value->value.string = pdfioStringCreate(dict->pdf, temp);
return (value->value.string);
}
else
{
return (NULL);
}
}
@ -412,6 +659,47 @@ _pdfioDictGetValue(pdfio_dict_t *dict, // I - Dictionary
}
//
// 'pdfioDictIterateKeys()' - Iterate the keys in a dictionary.
//
// This function iterates the keys in a dictionary, calling the supplied
// function "cb":
//
// ```
// bool
// my_dict_cb(pdfio_dict_t *dict, const char *key, void *cb_data)
// {
// ... "key" contains the dictionary key ...
// ... return true to continue or false to stop ...
// }
// ```
//
// The iteration continues as long as the callback returns `true` or all keys
// have been iterated.
//
void
pdfioDictIterateKeys(
pdfio_dict_t *dict, // I - Dictionary
pdfio_dict_cb_t cb, // I - Callback function
void *cb_data) // I - Callback data
{
size_t i; // Looping var
_pdfio_pair_t *pair; // Current pair
// Range check input...
if (!dict || !cb)
return;
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{
if (!(cb)(dict, pair->key, cb_data))
break;
}
}
//
// '_pdfioDictRead()' - Read a dictionary from a PDF file.
//
@ -420,14 +708,16 @@ _pdfioDictGetValue(pdfio_dict_t *dict, // I - Dictionary
pdfio_dict_t * // O - New dictionary
_pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
_pdfio_token_t *tb) // I - Token buffer/stack
pdfio_obj_t *obj, // I - Object, if any
_pdfio_token_t *tb, // I - Token buffer/stack
size_t depth) // I - Depth of dictionary
{
pdfio_dict_t *dict; // New dictionary
char key[256]; // Dictionary key
_pdfio_value_t value; // Dictionary value
PDFIO_DEBUG("_pdfioDictRead(pdf=%p)\n", pdf);
PDFIO_DEBUG("_pdfioDictRead(pdf=%p, obj=%p, tb=%p, depth=%lu)\n", pdf, obj, tb, (unsigned long)depth);
// Create a dictionary and start reading...
if ((dict = pdfioDictCreate(pdf)) == NULL)
@ -439,6 +729,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
if (!strcmp(key, ">>"))
{
// End of dictionary...
PDFIO_DEBUG("_pdfioDictRead: Returning dictionary value...\n");
return (dict);
}
else if (key[0] != '/')
@ -446,18 +737,25 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
_pdfioFileError(pdf, "Invalid dictionary contents.");
break;
}
else if (_pdfioDictGetValue(dict, key + 1))
{
_pdfioFileError(pdf, "Duplicate dictionary key '%s'.", key + 1);
return (NULL);
}
// Then get the next value...
if (!_pdfioValueRead(pdf, tb, &value))
PDFIO_DEBUG("_pdfioDictRead: Reading value for '%s'.\n", key + 1);
if (!_pdfioValueRead(pdf, obj, tb, &value, depth))
{
_pdfioFileError(pdf, "Missing value for dictionary key.");
_pdfioFileError(pdf, "Missing value for dictionary key '%s'.", key + 1);
break;
}
if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value))
break;
// PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key);
PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key);
}
// Dictionary is invalid - pdfioFileClose will free the memory, return NULL
@ -653,7 +951,7 @@ pdfioDictSetNull(pdfio_dict_t *dict, // I - Dictionary
bool // O - `true` on success, `false` on failure
pdfioDictSetNumber(pdfio_dict_t *dict, // I - Dictionary
const char *key, // I - Key
double value) // I - Value
double value) // I - Value
{
_pdfio_value_t temp; // New value
@ -802,6 +1100,8 @@ _pdfioDictSetValue(
{
// Yes, replace the value...
PDFIO_DEBUG("_pdfioDictSetValue: Replacing existing value.\n");
if (pair->value.type == PDFIO_VALTYPE_BINARY)
free(pair->value.value.binary.data);
pair->value = *value;
return (true);
}
@ -835,9 +1135,9 @@ _pdfioDictSetValue(
#ifdef DEBUG
PDFIO_DEBUG("_pdfioDictSetValue(%p): %lu pairs\n", (void *)dict, (unsigned long)dict->num_pairs);
PDFIO_DEBUG("_pdfioDictSetValue(%p): ", (void *)dict);
PDFIO_DEBUG_DICT(dict);
PDFIO_DEBUG("\n");
// PDFIO_DEBUG("_pdfioDictSetValue(%p): ", (void *)dict);
// PDFIO_DEBUG_DICT(dict);
// PDFIO_DEBUG("\n");
#endif // DEBUG
return (true);
@ -850,6 +1150,7 @@ _pdfioDictSetValue(
bool // O - `true` on success, `false` on failure
_pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary
pdfio_obj_t *obj, // I - Object, if any
off_t *length) // I - Offset to length value
{
pdfio_file_t *pdf = dict->pdf; // PDF file
@ -867,7 +1168,7 @@ _pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary
// Write all of the key/value pairs...
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{
if (!_pdfioFilePrintf(pdf, "/%s", pair->key))
if (!_pdfioFilePrintf(pdf, "%N", pair->key))
return (false);
if (length && !strcmp(pair->key, "Length") && pair->value.type == PDFIO_VALTYPE_NUMBER && pair->value.value.number <= 0.0)
@ -877,7 +1178,7 @@ _pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary
if (!_pdfioFilePuts(pdf, " 9999999999"))
return (false);
}
else if (!_pdfioValueWrite(pdf, &pair->value, NULL))
else if (!_pdfioValueWrite(pdf, obj, &pair->value, NULL))
return (false);
}

File diff suppressed because it is too large Load Diff

392
pdfio-md5.c Normal file
View File

@ -0,0 +1,392 @@
//
// MD5 functions for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 1999 Aladdin Enterprises. All rights reserved.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// L. Peter Deutsch
// ghost@aladdin.com
//
#include "pdfio-private.h"
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321.
It is derived directly from the text of the RFC and not from the
reference implementation.
The original and principal author of md5.c is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
1999-05-03 lpd Original version.
*/
#define T1 0xd76aa478
#define T2 0xe8c7b756
#define T3 0x242070db
#define T4 0xc1bdceee
#define T5 0xf57c0faf
#define T6 0x4787c62a
#define T7 0xa8304613
#define T8 0xfd469501
#define T9 0x698098d8
#define T10 0x8b44f7af
#define T11 0xffff5bb1
#define T12 0x895cd7be
#define T13 0x6b901122
#define T14 0xfd987193
#define T15 0xa679438e
#define T16 0x49b40821
#define T17 0xf61e2562
#define T18 0xc040b340
#define T19 0x265e5a51
#define T20 0xe9b6c7aa
#define T21 0xd62f105d
#define T22 0x02441453
#define T23 0xd8a1e681
#define T24 0xe7d3fbc8
#define T25 0x21e1cde6
#define T26 0xc33707d6
#define T27 0xf4d50d87
#define T28 0x455a14ed
#define T29 0xa9e3e905
#define T30 0xfcefa3f8
#define T31 0x676f02d9
#define T32 0x8d2a4c8a
#define T33 0xfffa3942
#define T34 0x8771f681
#define T35 0x6d9d6122
#define T36 0xfde5380c
#define T37 0xa4beea44
#define T38 0x4bdecfa9
#define T39 0xf6bb4b60
#define T40 0xbebfbc70
#define T41 0x289b7ec6
#define T42 0xeaa127fa
#define T43 0xd4ef3085
#define T44 0x04881d05
#define T45 0xd9d4d039
#define T46 0xe6db99e5
#define T47 0x1fa27cf8
#define T48 0xc4ac5665
#define T49 0xf4292244
#define T50 0x432aff97
#define T51 0xab9423a7
#define T52 0xfc93a039
#define T53 0x655b59c3
#define T54 0x8f0ccc92
#define T55 0xffeff47d
#define T56 0x85845dd1
#define T57 0x6fa87e4f
#define T58 0xfe2ce6e0
#define T59 0xa3014314
#define T60 0x4e0811a1
#define T61 0xf7537e82
#define T62 0xbd3af235
#define T63 0x2ad7d2bb
#define T64 0xeb86d391
//
// Use the unoptimized (big-endian) implementation if we don't know the
// endian-ness of the platform.
//
#ifdef __BYTE_ORDER__
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define ARCH_IS_BIG_ENDIAN 0 // Use little endian optimized version
# else
# define ARCH_IS_BIG_ENDIAN 1 // Use generic version
# endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#elif !defined(ARCH_IS_BIG_ENDIAN)
# define ARCH_IS_BIG_ENDIAN 1 // Use generic version
#endif // !ARCH_IS_BIG_ENDIAN
//
// 'md5_process()' - Hash a block of data.
//
static void
md5_process(_pdfio_md5_t *pms, // I - MD5 state
const uint8_t *data/*[64]*/)// I - Data
{
uint32_t a = pms->abcd[0], // First word of state
b = pms->abcd[1], // Second word of state
c = pms->abcd[2], // Third word of state
d = pms->abcd[3]; // Fourth word of state
uint32_t t; // Temporary state
#if ARCH_IS_BIG_ENDIAN
// On big-endian machines, we must arrange the bytes in the right
// order. (This also works on machines of unknown byte order.)
uint32_t X[16]; // Little-endian representation
const uint8_t *xp; // Pointer into data
int i; // Looping var
for (i = 0, xp = data; i < 16; i ++, xp += 4)
X[i] = xp[0] + (unsigned)(xp[1] << 8) + (unsigned)(xp[2] << 16) + (unsigned)(xp[3] << 24);
#else /* !ARCH_IS_BIG_ENDIAN */
// On little-endian machines, we can process properly aligned data without copying it.
uint32_t xbuf[16]; // Aligned buffer
const uint32_t *X; // Pointer to little-endian representation
if (!((data - (const uint8_t *)0) & 3))
{
// data is properly aligned, use it directly...
X = (const uint32_t *)data;
}
else
{
// data is not aligned, copy to the aligned buffer...
memcpy(xbuf, data, 64);
X = xbuf;
}
#endif // ARCH_IS_BIG_ENDIAN
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
// Round 1.
// Let [abcd k s i] denote the operation
// a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s).
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define SET(a, b, c, d, k, s, Ti) t = a + F(b,c,d) + X[k] + Ti; a = ROTATE_LEFT(t, s) + b
// Do the following 16 operations.
SET(a, b, c, d, 0, 7, T1);
SET(d, a, b, c, 1, 12, T2);
SET(c, d, a, b, 2, 17, T3);
SET(b, c, d, a, 3, 22, T4);
SET(a, b, c, d, 4, 7, T5);
SET(d, a, b, c, 5, 12, T6);
SET(c, d, a, b, 6, 17, T7);
SET(b, c, d, a, 7, 22, T8);
SET(a, b, c, d, 8, 7, T9);
SET(d, a, b, c, 9, 12, T10);
SET(c, d, a, b, 10, 17, T11);
SET(b, c, d, a, 11, 22, T12);
SET(a, b, c, d, 12, 7, T13);
SET(d, a, b, c, 13, 12, T14);
SET(c, d, a, b, 14, 17, T15);
SET(b, c, d, a, 15, 22, T16);
#undef SET
// Round 2.
// Let [abcd k s i] denote the operation
// a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s).
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti) t = a + G(b,c,d) + X[k] + Ti; a = ROTATE_LEFT(t, s) + b
// Do the following 16 operations.
SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18);
SET(c, d, a, b, 11, 14, T19);
SET(b, c, d, a, 0, 20, T20);
SET(a, b, c, d, 5, 5, T21);
SET(d, a, b, c, 10, 9, T22);
SET(c, d, a, b, 15, 14, T23);
SET(b, c, d, a, 4, 20, T24);
SET(a, b, c, d, 9, 5, T25);
SET(d, a, b, c, 14, 9, T26);
SET(c, d, a, b, 3, 14, T27);
SET(b, c, d, a, 8, 20, T28);
SET(a, b, c, d, 13, 5, T29);
SET(d, a, b, c, 2, 9, T30);
SET(c, d, a, b, 7, 14, T31);
SET(b, c, d, a, 12, 20, T32);
#undef SET
// Round 3.
// Let [abcd k s t] denote the operation
// a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s).
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti) t = a + H(b,c,d) + X[k] + Ti; a = ROTATE_LEFT(t, s) + b
// Do the following 16 operations.
SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34);
SET(c, d, a, b, 11, 16, T35);
SET(b, c, d, a, 14, 23, T36);
SET(a, b, c, d, 1, 4, T37);
SET(d, a, b, c, 4, 11, T38);
SET(c, d, a, b, 7, 16, T39);
SET(b, c, d, a, 10, 23, T40);
SET(a, b, c, d, 13, 4, T41);
SET(d, a, b, c, 0, 11, T42);
SET(c, d, a, b, 3, 16, T43);
SET(b, c, d, a, 6, 23, T44);
SET(a, b, c, d, 9, 4, T45);
SET(d, a, b, c, 12, 11, T46);
SET(c, d, a, b, 15, 16, T47);
SET(b, c, d, a, 2, 23, T48);
#undef SET
// Round 4.
// Let [abcd k s t] denote the operation
// a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s).
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti) t = a + I(b,c,d) + X[k] + Ti; a = ROTATE_LEFT(t, s) + b
// Do the following 16 operations.
SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50);
SET(c, d, a, b, 14, 15, T51);
SET(b, c, d, a, 5, 21, T52);
SET(a, b, c, d, 12, 6, T53);
SET(d, a, b, c, 3, 10, T54);
SET(c, d, a, b, 10, 15, T55);
SET(b, c, d, a, 1, 21, T56);
SET(a, b, c, d, 8, 6, T57);
SET(d, a, b, c, 15, 10, T58);
SET(c, d, a, b, 6, 15, T59);
SET(b, c, d, a, 13, 21, T60);
SET(a, b, c, d, 4, 6, T61);
SET(d, a, b, c, 11, 10, T62);
SET(c, d, a, b, 2, 15, T63);
SET(b, c, d, a, 9, 21, T64);
#undef SET
// Then perform the following additions. (That is increment each of the four
// registers by the value it had before this block was started.)
pms->abcd[0] += a;
pms->abcd[1] += b;
pms->abcd[2] += c;
pms->abcd[3] += d;
}
//
// '_pdfioCryptoMD5Init()' - Initialize an MD5 hash.
//
void
_pdfioCryptoMD5Init(_pdfio_md5_t *pms) // I - MD5 state
{
pms->count[0] = pms->count[1] = 0;
pms->abcd[0] = 0x67452301;
pms->abcd[1] = 0xefcdab89;
pms->abcd[2] = 0x98badcfe;
pms->abcd[3] = 0x10325476;
}
//
// '_pdfioCryptoMD5Append()' - Append bytes to the MD5 hash.
//
void
_pdfioCryptoMD5Append(
_pdfio_md5_t *pms, // I - MD5 state
const uint8_t *data, // I - Data to add
size_t nbytes) // I - Number of bytes
{
const uint8_t *p = data; // Pointer into data
size_t left = nbytes; // Remaining bytes
size_t offset = (pms->count[0] >> 3) & 63;
// Offset into state
uint32_t nbits = (uint32_t)(nbytes << 3);
// Number of bits to add
if (nbytes == 0)
return;
// Update the message length.
pms->count[1] += (unsigned)(nbytes >> 29);
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1] ++;
// Process an initial partial block.
if (offset)
{
size_t copy = ((offset + nbytes) > 64 ? 64 - offset : nbytes);
// Number of bytes to copy
memcpy(pms->buf + offset, p, copy);
if ((offset + copy) < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
}
// Process full blocks.
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
// Copy a final partial block.
if (left)
memcpy(pms->buf, p, left);
}
//
// '_pdfioCryptoMD5Finish()' - Finalize the MD5 hash.
//
void
_pdfioCryptoMD5Finish(
_pdfio_md5_t *pms, // I - MD5 state
uint8_t digest[16]) // O - Digest value
{
int i; // Looping var
uint8_t data[8]; // Digest length data
static const uint8_t pad[64] = // Padding bytes
{
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// Save the length before padding.
for (i = 0; i < 8; ++i)
data[i] = (uint8_t)(pms->count[i >> 2] >> ((i & 3) << 3));
// Pad to 56 bytes mod 64.
_pdfioCryptoMD5Append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
// Append the length.
_pdfioCryptoMD5Append(pms, data, 8);
// Copy the digest from the state...
for (i = 0; i < 16; ++i)
digest[i] = (uint8_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}

View File

@ -1,26 +1,15 @@
//
// PDF object functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
//
// Local functions...
//
static bool write_obj_header(pdfio_obj_t *obj);
//
// 'pdfioObjClose()' - Close an object, writing any data as needed to the PDF
// file.
@ -33,14 +22,20 @@ pdfioObjClose(pdfio_obj_t *obj) // I - Object
if (!obj)
return (false);
// Clear the current object pointer...
obj->pdf->current_obj = NULL;
if (obj->pdf->mode != _PDFIO_MODE_WRITE)
return (true); // Nothing to do when reading
{
// Nothing to do when reading
return (true);
}
// Write what remains for the object...
if (!obj->offset)
{
// Write the object value
if (!write_obj_header(obj))
if (!_pdfioObjWriteHeader(obj))
return (false);
// Write the "endobj" line...
@ -96,6 +91,9 @@ pdfioObjCopy(pdfio_file_t *pdf, // I - PDF file
if (!_pdfioValueCopy(pdf, &dstobj->value, srcobj->pdf, &srcobj->value))
return (NULL);
if (dstobj->value.type == PDFIO_VALTYPE_DICT)
pdfioDictClear(dstobj->value.value.dict, "Length");
if (srcobj->stream_offset)
{
// Copy stream data...
@ -143,6 +141,9 @@ pdfioObjCreateStream(
pdfio_obj_t *obj, // I - Object
pdfio_filter_t filter) // I - Type of compression to apply
{
pdfio_obj_t *length_obj = NULL; // Length object, if any
// Range check input
if (!obj || obj->pdf->mode != _PDFIO_MODE_WRITE || obj->value.type != PDFIO_VALTYPE_DICT)
return (NULL);
@ -159,24 +160,45 @@ pdfioObjCreateStream(
return (NULL);
}
if (obj->pdf->current_obj)
{
_pdfioFileError(obj->pdf, "Another object (%u) is already open.", (unsigned)obj->pdf->current_obj->number);
return (NULL);
}
// Write the header...
if (!_pdfioDictGetValue(obj->value.value.dict, "Length"))
{
// Need a Length key for the stream, add a placeholder that we can fill in
// later...
pdfioDictSetNumber(obj->value.value.dict, "Length", 0.0);
if (obj->pdf->output_cb)
{
// Streaming via an output callback, so add a placeholder length object
_pdfio_value_t length_value; // Length value
length_value.type = PDFIO_VALTYPE_NUMBER;
length_value.value.number = 0.0f;
length_obj = _pdfioFileCreateObj(obj->pdf, obj->pdf, &length_value);
pdfioDictSetObj(obj->value.value.dict, "Length", length_obj);
}
else
{
// Need a Length key for the stream, add a placeholder that we can fill in
// later...
pdfioDictSetNumber(obj->value.value.dict, "Length", 0.0);
}
}
if (!write_obj_header(obj))
if (!_pdfioObjWriteHeader(obj))
return (NULL);
if (!_pdfioFilePuts(obj->pdf, "stream\n"))
return (NULL);
obj->stream_offset = _pdfioFileTell(obj->pdf);
obj->stream_offset = _pdfioFileTell(obj->pdf);
obj->pdf->current_obj = obj;
// Return the new stream...
return (_pdfioStreamCreate(obj, filter));
return (_pdfioStreamCreate(obj, length_obj, 0, filter));
}
@ -188,8 +210,13 @@ void
_pdfioObjDelete(pdfio_obj_t *obj) // I - Object
{
if (obj)
{
pdfioStreamClose(obj->stream);
if (obj->datafree)
(obj->datafree)(obj->data);
}
free(obj);
}
@ -234,6 +261,17 @@ pdfioObjGetDict(pdfio_obj_t *obj) // I - Object
}
//
// '_pdfioObjGetExtension()' - Get the extension pointer for an object.
//
void * // O - Extension data
_pdfioObjGetExtension(pdfio_obj_t *obj) // I - Object
{
return (obj->data);
}
//
// 'pdfioObjGetGeneration()' - Get the object's generation number.
//
@ -269,7 +307,8 @@ pdfioObjGetLength(pdfio_obj_t *obj) // I - Object
if ((lenobj = pdfioDictGetObj(obj->value.value.dict, "Length")) == NULL)
{
_pdfioFileError(obj->pdf, "Unable to get length of stream.");
if (!_pdfioDictGetValue(obj->value.value.dict, "Length"))
_pdfioFileError(obj->pdf, "Unable to get length of stream.");
return (0);
}
@ -288,6 +327,26 @@ pdfioObjGetLength(pdfio_obj_t *obj) // I - Object
}
//
// 'pdfioObjGetName()' - Get the name value associated with an object.
//
const char * // O - Dictionary or `NULL` on error
pdfioObjGetName(pdfio_obj_t *obj) // I - Object
{
if (!obj)
return (NULL);
if (obj->value.type == PDFIO_VALTYPE_NONE)
_pdfioObjLoad(obj);
if (obj->value.type == PDFIO_VALTYPE_NAME)
return (obj->value.value.name);
else
return (NULL);
}
//
// 'pdfioObjGetNumber()' - Get the object's number.
//
@ -302,8 +361,21 @@ pdfioObjGetNumber(pdfio_obj_t *obj) // I - Object
//
// 'pdfioObjGetSubtype()' - Get an object's subtype.
//
// This function returns an object's PDF subtype name, if any. Common subtype
// names include:
//
// - "CIDFontType0": A CID Type0 font
// - "CIDFontType2": A CID TrueType font
// - "Image": An image or image mask
// - "Form": A fillable form
// - "OpenType": An OpenType font
// - "Type0": A composite font
// - "Type1": A PostScript Type1 font
// - "Type3": A PDF Type3 font
// - "TrueType": A TrueType font
//
const char * // O - Object subtype
const char * // O - Object subtype name or `NULL` for none
pdfioObjGetSubtype(pdfio_obj_t *obj) // I - Object
{
pdfio_dict_t *dict; // Object dictionary
@ -319,8 +391,21 @@ pdfioObjGetSubtype(pdfio_obj_t *obj) // I - Object
//
// 'pdfioObjGetType()' - Get an object's type.
//
// This function returns an object's PDF type name, if any. Common type names
// include:
//
// - "CMap": A character map for composite fonts
// - "Font": An embedded font (@link pdfioObjGetSubtype@ will tell you the
// font format)
// - "FontDescriptor": A font descriptor
// - "Page": A (visible) page
// - "Pages": A page tree node
// - "Template": An invisible template page
// - "XObject": An image, image mask, or form (@link pdfioObjGetSubtype@ will
// tell you which)
//
const char * // O - Object type
const char * // O - Object type name or `NULL` for none
pdfioObjGetType(pdfio_obj_t *obj) // I - Object
{
pdfio_dict_t *dict; // Object dictionary
@ -387,12 +472,15 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
}
ptr += 3;
while (*ptr && isspace(*ptr & 255))
ptr ++;
_pdfioFileConsume(obj->pdf, (size_t)(ptr - line));
// Then grab the object value...
_pdfioTokenInit(&tb, obj->pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, obj->pdf);
if (!_pdfioValueRead(obj->pdf, &tb, &obj->value))
if (!_pdfioValueRead(obj->pdf, obj, &tb, &obj->value, 0))
{
_pdfioFileError(obj->pdf, "Unable to read value for object %lu.", (unsigned long)obj->number);
return (false);
@ -405,15 +493,29 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
return (false);
}
PDFIO_DEBUG("_pdfioObjLoad: tb.bufptr=%p, tb.bufend=%p, tb.bufptr[0]=0x%02x, tb.bufptr[1]=0x%02x\n", tb.bufptr, tb.bufend, tb.bufptr[0], tb.bufptr[1]);
_pdfioTokenFlush(&tb);
if (!strcmp(line, "stream"))
{
// Yes, save its location...
// Yes, this is an embedded stream so save its location...
obj->stream_offset = _pdfioFileTell(obj->pdf);
PDFIO_DEBUG("_pdfioObjLoad: stream_offset=%lu.\n", (unsigned long)obj->stream_offset);
}
// Decrypt as needed...
if (obj->pdf->encryption)
{
PDFIO_DEBUG("_pdfioObjLoad: Decrypting value...\n");
if (!_pdfioValueDecrypt(obj->pdf, obj, &obj->value, 0))
{
PDFIO_DEBUG("_pdfioObjLoad: Failed to decrypt.\n");
return (false);
}
}
PDFIO_DEBUG("_pdfioObjLoad: ");
PDFIO_DEBUG_VALUE(&obj->value);
PDFIO_DEBUG("\n");
@ -434,6 +536,12 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
if (!obj)
return (NULL);
if (obj->pdf->current_obj)
{
_pdfioFileError(obj->pdf, "Another object (%u) is already open.", (unsigned)obj->pdf->current_obj->number);
return (NULL);
}
// Make sure we've loaded the object dictionary...
if (!obj->value.type)
{
@ -446,23 +554,40 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
return (NULL);
// Open the stream...
obj->pdf->current_obj = obj;
return (_pdfioStreamOpen(obj, decode));
}
//
// 'write_obj_header()' - Write the object header...
// '_pdfioObjSetExtension()' - Set extension data for an object.
//
static bool // O - `true` on success, `false` on failure
write_obj_header(pdfio_obj_t *obj) // I - Object
void
_pdfioObjSetExtension(
pdfio_obj_t *obj, // I - Object
void *data, // I - Data
_pdfio_extfree_t datafree) // I - Free function
{
obj->data = data;
obj->datafree = datafree;
}
//
// '_pdfioObjWriteHeader()' - Write the object header...
//
bool // O - `true` on success, `false` on failure
_pdfioObjWriteHeader(pdfio_obj_t *obj) // I - Object
{
obj->offset = _pdfioFileTell(obj->pdf);
if (!_pdfioFilePrintf(obj->pdf, "%lu %u obj\n", (unsigned long)obj->number, obj->generation))
return (false);
if (!_pdfioValueWrite(obj->pdf, &obj->value, &obj->length_offset))
if (!_pdfioValueWrite(obj->pdf, obj, &obj->value, &obj->length_offset))
return (false);
return (_pdfioFilePuts(obj->pdf, "\n"));

View File

@ -1,17 +1,20 @@
//
// PDF page functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2022 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
#include "pdfio-private.h"
//
// Include necessary headers...
// Local functions...
//
#include "pdfio-private.h"
static _pdfio_value_t *get_contents(pdfio_obj_t *page);
//
@ -47,3 +50,74 @@ pdfioPageCopy(pdfio_file_t *pdf, // I - PDF file
else
return (_pdfioFileAddPage(pdf, dstpage));
}
//
// 'pdfioPageGetNumStreams()' - Get the number of content streams for a page object.
//
size_t // O - Number of streams
pdfioPageGetNumStreams(
pdfio_obj_t *page) // I - Page object
{
_pdfio_value_t *contents = get_contents(page);
// Contents value
if (!contents)
return (0);
else if (contents->type == PDFIO_VALTYPE_ARRAY)
return (pdfioArrayGetSize(contents->value.array));
else
return (1);
}
//
// 'pdfioPageOpenStream()' - Open a content stream for a page.
//
pdfio_stream_t * // O - Stream
pdfioPageOpenStream(
pdfio_obj_t *page, // I - Page object
size_t n, // I - Stream index (0-based)
bool decode) // I - `true` to decode/decompress stream
{
_pdfio_value_t *contents = get_contents(page);
// Contents value
if (!contents)
return (NULL);
else if (contents->type == PDFIO_VALTYPE_ARRAY && n < pdfioArrayGetSize(contents->value.array))
return (pdfioObjOpenStream(pdfioArrayGetObj(contents->value.array, n), decode));
else if (n)
return (NULL);
else
return (pdfioObjOpenStream(pdfioFileFindObj(page->pdf, contents->value.indirect.number), decode));
}
//
// 'get_contents()' - Get a page's Contents value.
//
static _pdfio_value_t * // O - Value or NULL on error
get_contents(pdfio_obj_t *page) // I - Page object
{
// Range check input...
if (!page)
return (NULL);
// Load the page object as needed...
if (page->value.type == PDFIO_VALTYPE_NONE)
{
if (!_pdfioObjLoad(page))
return (NULL);
}
if (page->value.type != PDFIO_VALTYPE_DICT)
return (NULL);
return (_pdfioDictGetValue(page->value.value.dict, "Contents"));
}

View File

@ -1,7 +1,7 @@
//
// Private header file for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -9,40 +9,23 @@
#ifndef PDFIO_PRIVATE_H
# define PDFIO_PRIVATE_H
//
// Include necessary headers...
//
# ifdef _WIN32
/*
* Disable bogus VS warnings/errors...
*/
# define _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS // Disable bogus VS warnings/errors...
# endif // _WIN32
# include "pdfio.h"
# include <stdarg.h>
# include <stdint.h>
# include <string.h>
# include <ctype.h>
# include <errno.h>
# include <inttypes.h>
# include <fcntl.h>
# include <locale.h>
# ifdef _WIN32
# include <io.h>
# include <direct.h>
/*
* Microsoft renames the POSIX functions to _name, and introduces
* a broken compatibility layer using the original names. As a result,
* random crashes can occur when, for example, strdup() allocates memory
* from a different heap than used by malloc() and free().
*
* To avoid moronic problems like this, we #define the POSIX function
* names to the corresponding non-standard Microsoft names.
*/
# define access _access
# include <windows.h> // GetTempPathA
# define access _access // Map standard POSIX/C99 names
# define close _close
# define fileno _fileno
# define lseek _lseek
@ -55,25 +38,20 @@
# define unlink _unlink
# define vsnprintf _vsnprintf
# define write _write
/*
* Map various parameters for POSIX...
*/
# define F_OK 00
# define W_OK 02
# define R_OK 04
# define O_RDONLY _O_RDONLY
# ifndef F_OK
# define F_OK 00 // POSIX parameters/flags
# define W_OK 02
# define R_OK 04
# endif // !F_OK
# define O_RDONLY _O_RDONLY // Map standard POSIX open flags
# define O_WRONLY _O_WRONLY
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# define O_BINARY _O_BINARY
# else // !_WIN32
# include <unistd.h>
# define O_BINARY 0
# define O_BINARY 0 // Map Windows-specific open flag
# endif // _WIN32
# include <string.h>
# include <ctype.h>
# include <zlib.h>
@ -115,6 +93,11 @@
// Types and constants...
//
# define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values
typedef void (*_pdfio_extfree_t)(void *);
// Extension data free function
typedef enum _pdfio_mode_e // Read/write mode
{
_PDFIO_MODE_READ, // Read a PDF file
@ -124,7 +107,7 @@ typedef enum _pdfio_mode_e // Read/write mode
typedef enum _pdfio_predictor_e // PNG predictor constants
{
_PDFIO_PREDICTOR_NONE = 1, // No predictor (default)
_PDFIO_PREDICTOR_TIFF2 = 2, // TIFF2 predictor (???)
_PDFIO_PREDICTOR_TIFF2 = 2, // TIFF predictor 2 (difference from left neighbor)
_PDFIO_PREDICTOR_PNG_NONE = 10, // PNG None predictor (same as `_PDFIO_PREDICTOR_NONE`)
_PDFIO_PREDICTOR_PNG_SUB = 11, // PNG Sub predictor
_PDFIO_PREDICTOR_PNG_UP = 12, // PNG Up predictor
@ -174,6 +157,44 @@ typedef struct _pdfio_value_s // Value structure
} value; // Value union
} _pdfio_value_t;
typedef struct _pdfio_aes_s // AES encryption state
{
size_t round_size; // Size of round key
uint8_t round_key[240], // Round key
iv[16]; // Initialization vector
} _pdfio_aes_t;
typedef struct _pdfio_md5_s // MD5 hash state
{
uint32_t count[2]; // Message length in bits, lsw first
uint32_t abcd[4]; // Digest buffer
uint8_t buf[64]; // Accumulate block
} _pdfio_md5_t;
typedef struct _pdfio_rc4_s // RC4 encryption state
{
uint8_t sbox[256]; // S boxes for encryption
uint8_t i, j; // Current indices into S boxes
} _pdfio_rc4_t;
typedef struct _pdfio_sha265_s // SHA-256 hash state
{
uint32_t Intermediate_Hash[8]; // Message Digest
uint32_t Length_High; // Message length in bits
uint32_t Length_Low; // Message length in bits
int Message_Block_Index; // Message_Block array index
uint8_t Message_Block[64]; // 512-bit message blocks
int Computed; // Is the hash computed?
int Corrupted; // Cumulative corruption code
} _pdfio_sha256_t;
typedef union _pdfio_crypto_ctx_u // Cryptographic contexts
{
_pdfio_aes_t aes; // AES-128/256 context
_pdfio_rc4_t rc4; // RC4-40/128 context
} _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);
struct _pdfio_array_s
{
pdfio_file_t *pdf; // PDF file
@ -206,24 +227,37 @@ typedef struct _pdfio_objmap_s // PDF object map
struct _pdfio_file_s // PDF file structure
{
char *filename; // Filename
struct lconv *loc; // Locale data
char *version; // Version number
pdfio_rect_t media_box, // Default MediaBox value
crop_box; // Default CropBox value
_pdfio_mode_t mode; // Read/write mode
pdfio_output_cb_t output_cb; // Output callback
void *output_ctx; // Context for output callback
pdfio_error_cb_t error_cb; // Error callback
void *error_data; // Data for error callback
pdfio_encryption_t encryption; // Encryption mode
pdfio_permission_t permissions; // Access permissions (encrypted PDF files)
uint8_t file_key[16], // File encryption key
owner_key[32], // Owner encryption key
user_key[32], // User encryption key
password[32]; // Padded password
size_t file_keylen, // Length of file encryption key
owner_keylen, // Length of owner encryption key
user_keylen; // Length of user encryption key
// Active file data
int fd; // File descriptor
char buffer[8192], // Read/write buffer
*bufptr, // Pointer into buffer
*bufend; // End of buffer
off_t bufpos; // Position in file for start of buffer
pdfio_dict_t *trailer; // Trailer dictionary
pdfio_obj_t *root; // Root object/dictionary
pdfio_obj_t *info; // Information object
pdfio_obj_t *pages_root; // Root pages object
pdfio_obj_t *encrypt; // Encryption object/dictionary
pdfio_dict_t *trailer_dict; // Trailer dictionary
pdfio_obj_t *root_obj; // Root object/dictionary
pdfio_obj_t *info_obj; // Information object
pdfio_obj_t *pages_obj; // Root pages object
pdfio_obj_t *encrypt_obj; // De/Encryption object/dictionary
pdfio_obj_t *cp1252_obj, // CP1252 font encoding object
*unicode_obj; // Unicode font encoding object
pdfio_array_t *id_array; // ID array
@ -236,8 +270,10 @@ struct _pdfio_file_s // PDF file structure
alloc_dicts; // Allocated dictionaries
pdfio_dict_t **dicts; // Dictionaries
size_t num_objs, // Number of objects
alloc_objs; // Allocated objects
pdfio_obj_t **objs; // Objects
alloc_objs, // Allocated objects
last_obj; // Last object added
pdfio_obj_t **objs, // Objects
*current_obj; // Current object being written/read
size_t num_objmaps, // Number of object maps
alloc_objmaps; // Allocated object maps
_pdfio_objmap_t *objmaps; // Object maps
@ -260,12 +296,15 @@ struct _pdfio_obj_s // Object
size_t stream_length; // Length of stream, if any
_pdfio_value_t value; // Dictionary/number/etc. value
pdfio_stream_t *stream; // Open stream, if any
void *data; // Extension data, if any
_pdfio_extfree_t datafree; // Free callback for extension data
};
struct _pdfio_stream_s // Stream
{
pdfio_file_t *pdf; // PDF file
pdfio_obj_t *obj; // Object
pdfio_obj_t *length_obj; // Length object, if any
pdfio_filter_t filter; // Compression/decompression filter
size_t remaining; // Remaining bytes in stream
char buffer[8192], // Read/write buffer
@ -274,10 +313,13 @@ struct _pdfio_stream_s // Stream
z_stream flate; // Flate filter state
_pdfio_predictor_t predictor; // Predictor function, if any
size_t pbpixel, // Size of a pixel in bytes
pbsize; // Predictor buffer size, if any
unsigned char cbuffer[4096], // Compressed data buffer
pbsize, // Predictor buffer size, if any
cbsize; // Compressed data buffer size
unsigned char *cbuffer, // Compressed data buffer
*prbuffer, // Raw buffer (previous line), as needed
*psbuffer; // PNG filter buffer, as needed
_pdfio_crypto_cb_t crypto_cb; // Encryption/descryption callback, if any
_pdfio_crypto_ctx_t crypto_ctx; // Cryptographic context
};
@ -285,31 +327,54 @@ struct _pdfio_stream_s // Stream
// Functions...
//
extern size_t _pdfio_strlcpy(char *dst, const char *src, size_t dstsize) _PDFIO_INTERNAL;
extern double _pdfio_strtod(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
extern ssize_t _pdfio_vsnprintf(pdfio_file_t *pdf, char *buffer, size_t bufsize, const char *format, va_list ap) _PDFIO_INTERNAL;
extern bool _pdfioArrayDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_array_t *a, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL;
extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, _pdfio_token_t *ts) _PDFIO_INTERNAL;
extern bool _pdfioArrayWrite(pdfio_array_t *a) _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 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 _pdfioCryptoAESEncrypt(_pdfio_aes_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
extern bool _pdfioCryptoLock(pdfio_file_t *pdf, pdfio_permission_t permissions, pdfio_encryption_t encryption, const char *owner_password, const char *user_password) _PDFIO_INTERNAL;
extern void _pdfioCryptoMakeRandom(uint8_t *buffer, size_t bytes) _PDFIO_INTERNAL;
extern _pdfio_crypto_cb_t _pdfioCryptoMakeReader(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL;
extern _pdfio_crypto_cb_t _pdfioCryptoMakeWriter(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL;
extern void _pdfioCryptoMD5Append(_pdfio_md5_t *pms, const uint8_t *data, size_t nbytes) _PDFIO_INTERNAL;
extern void _pdfioCryptoMD5Finish(_pdfio_md5_t *pms, uint8_t digest[16]) _PDFIO_INTERNAL;
extern void _pdfioCryptoMD5Init(_pdfio_md5_t *pms) _PDFIO_INTERNAL;
extern void _pdfioCryptoRC4Init(_pdfio_rc4_t *ctx, const uint8_t *key, size_t keylen) _PDFIO_INTERNAL;
extern size_t _pdfioCryptoRC4Crypt(_pdfio_rc4_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Append(_pdfio_sha256_t *, const uint8_t *bytes, size_t bytecount) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Init(_pdfio_sha256_t *ctx) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Digest) _PDFIO_INTERNAL;
extern bool _pdfioCryptoUnlock(pdfio_file_t *pdf, pdfio_password_cb_t password_cb, void *password_data) _PDFIO_INTERNAL;
extern bool _pdfioDictDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_dict_t *dict, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
extern pdfio_dict_t *_pdfioDictRead(pdfio_file_t *pdf, _pdfio_token_t *ts) _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 _pdfioDictWrite(pdfio_dict_t *dict, off_t *length) _PDFIO_INTERNAL;
extern bool _pdfioDictWrite(pdfio_dict_t *dict, pdfio_obj_t *obj, 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 _pdfioFileAddPage(pdfio_file_t *pdf, pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern bool _pdfioFileConsume(pdfio_file_t *pdf, size_t bytes) _PDFIO_INTERNAL;
extern pdfio_obj_t *_pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_file_t *srcpdf, _pdfio_value_t *value) _PDFIO_INTERNAL;
extern bool _pdfioFileDefaultError(pdfio_file_t *pdf, const char *message, void *data) _PDFIO_INTERNAL;
extern bool _pdfioFileError(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_INTERNAL;
extern bool _pdfioFileError(pdfio_file_t *pdf, const char *format, ...) _PDFIO_INTERNAL;
extern pdfio_obj_t *_pdfioFileFindMappedObj(pdfio_file_t *pdf, pdfio_file_t *src_pdf, size_t src_number) _PDFIO_INTERNAL;
extern bool _pdfioFileFlush(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 ssize_t _pdfioFilePeek(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL;
extern bool _pdfioFilePrintf(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_INTERNAL;
extern bool _pdfioFilePrintf(pdfio_file_t *pdf, const char *format, ...) _PDFIO_INTERNAL;
extern bool _pdfioFilePuts(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
extern ssize_t _pdfioFileRead(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL;
extern off_t _pdfioFileSeek(pdfio_file_t *pdf, off_t offset, int whence) _PDFIO_INTERNAL;
@ -317,9 +382,12 @@ extern off_t _pdfioFileTell(pdfio_file_t *pdf) _PDFIO_INTERNAL;
extern bool _pdfioFileWrite(pdfio_file_t *pdf, const void *buffer, size_t bytes) _PDFIO_INTERNAL;
extern void _pdfioObjDelete(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern void *_pdfioObjGetExtension(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern bool _pdfioObjLoad(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern void _pdfioObjSetExtension(pdfio_obj_t *obj, void *data, _pdfio_extfree_t datafree) _PDFIO_INTERNAL;
extern bool _pdfioObjWriteHeader(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_filter_t compression) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, size_t cbsize, pdfio_filter_t compression) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
extern bool _pdfioStringIsAllocated(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
@ -332,9 +400,11 @@ extern void _pdfioTokenPush(_pdfio_token_t *tb, const char *token) _PDFIO_INTER
extern bool _pdfioTokenRead(_pdfio_token_t *tb, char *buffer, size_t bufsize);
extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vdst, pdfio_file_t *pdfsrc, _pdfio_value_t *vsrc) _PDFIO_INTERNAL;
extern bool _pdfioValueDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioValueDelete(_pdfio_value_t *v) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, _pdfio_token_t *ts, _pdfio_value_t *v) _PDFIO_INTERNAL;
extern bool _pdfioValueWrite(pdfio_file_t *pdf, _pdfio_value_t *v, off_t *length) _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;
#endif // !PDFIO_PRIVATE_H

113
pdfio-rc4.c Normal file
View File

@ -0,0 +1,113 @@
//
// RC4 functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
//
// Original code by Tim Martin
// Copyright © 1999 by Carnegie Mellon University, All Rights Reserved
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose and without fee is hereby granted,
// provided that the above copyright notice appear in all copies and that
// both that copyright notice and this permission notice appear in
// supporting documentation, and that the name of Carnegie Mellon
// University not be used in advertising or publicity pertaining to
// distribution of the software without specific, written prior
// permission.
//
// CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
// THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
// ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
// OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#include "pdfio-private.h"
//
// '_pdfioCryptoRC4Init()' - Initialize an RC4 context with the specified key.
//
void
_pdfioCryptoRC4Init(
_pdfio_rc4_t *ctx, // IO - Context
const uint8_t *key, // I - Key
size_t keylen) // I - Length of key
{
size_t i; // Looping var
uint8_t j, // S box counter
tmp; // Temporary variable
// Fill in linearly s0=0, s1=1, ...
for (i = 0; i < 256; i ++)
ctx->sbox[i] = (uint8_t)i;
for (i = 0, j = 0; i < 256; i ++)
{
// j = (j + Si + Ki) mod 256
j += ctx->sbox[i] + key[i % keylen];
// Swap Si and Sj...
tmp = ctx->sbox[i];
ctx->sbox[i] = ctx->sbox[j];
ctx->sbox[j] = tmp;
}
// Initialize counters to 0 and return...
ctx->i = 0;
ctx->j = 0;
}
//
// '_pdfioCryptoRC4Crypt()' - De/encrypt the given buffer.
//
// "inbuffer" and "outbuffer" can point to the same memory.
//
size_t // O - Number of output bytes
_pdfioCryptoRC4Crypt(
_pdfio_rc4_t *ctx, // I - Context
uint8_t *outbuffer, // I - Output buffer
const uint8_t *inbuffer, // I - Input buffer
size_t len) // I - Size of buffers
{
uint8_t tmp, // Swap variable
i, j, // Looping vars
t; // Current S box
size_t outbytes = len; // Number of output bytes
// Loop through the entire buffer...
i = ctx->i;
j = ctx->j;
while (len > 0)
{
// Get the next S box indices...
i ++;
j += ctx->sbox[i];
// Swap Si and Sj...
tmp = ctx->sbox[i];
ctx->sbox[i] = ctx->sbox[j];
ctx->sbox[j] = tmp;
// Get the S box index for this byte...
t = ctx->sbox[i] + ctx->sbox[j];
// Encrypt using the S box...
*outbuffer++ = *inbuffer++ ^ ctx->sbox[t];
len --;
}
// Copy current S box indices back to context...
ctx->i = i;
ctx->j = j;
return (outbytes);
}

480
pdfio-sha256.c Normal file
View File

@ -0,0 +1,480 @@
//
// SHA-256 functions for PDFio.
//
// Copyright © 2021-2023 by Michael R Sweet.
// Copyright © 2011 IETF Trust and the persons identified as authors of the
// code. All rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// conditions are met:
//
// - Redistributions of source code must retain the above
// copyright notice, this list of conditions and
// the following disclaimer.
//
// - 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.
//
// - Neither the name of Internet Society, IETF or IETF Trust, nor
// the names of specific contributors, may be used to endorse or
// promote products derived from this software without specific
// prior written permission.
//
// 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 OWNER 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.
//
/*
* Description:
* This file implements the Secure Hash Algorithms SHA-224 and
* SHA-256 as defined in the U.S. National Institute of Standards
* and Technology Federal Information Processing Standards
* Publication (FIPS PUB) 180-3 published in October 2008
* and formerly defined in its predecessors, FIPS PUB 180-1
* and FIP PUB 180-2.
*
* A combined document showing all algorithms is available at
* http://csrc.nist.gov/publications/fips/
* fips180-3/fips180-3_final.pdf
*
* The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit
* message digests for a given data stream. It should take about
* 2**n steps to find a message with the same digest as a given
* message and 2**(n/2) to find any two messages with the same
* digest, when n is the digest size in bits. Therefore, this
* algorithm can serve as a means of providing a
* "fingerprint" for a message.
*
* Portability Issues:
* SHA-224 and SHA-256 are defined in terms of 32-bit "words".
* This code uses <stdint.h> (included via "sha.h") to define 32-
* and 8-bit unsigned integer types. If your C compiler does not
* support 32-bit unsigned integers, this code is not
* appropriate.
*
* Caveats:
* SHA-224 and SHA-256 are designed to work with messages less
* than 2^64 bits long. This implementation uses SHA224/256Input()
* to hash the bits that are a multiple of the size of an 8-bit
* octet, and then optionally uses SHA224/256FinalBits()
* to hash the final few bits of the input.
*/
#include "pdfio-private.h"
/* Constants from sha.h */
enum {
SHA256_Message_Block_Size = 64,
SHA256HashSize = 32,
SHA256HashSizeBits = 256
};
enum {
shaSuccess = 0,
shaNull, /* Null pointer parameter */
shaInputTooLong, /* input data too long */
shaStateError, /* called Input after FinalBits or Result */
shaBadParam /* passed a bad parameter */
};
/* Macros from sha-private.h */
#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z))
#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z))
/* Define the SHA shift, rotate left, and rotate right macros */
#define SHA256_SHR(bits,word) ((word) >> (bits))
#define SHA256_ROTL(bits,word) \
(((word) << (bits)) | ((word) >> (32-(bits))))
#define SHA256_ROTR(bits,word) \
(((word) >> (bits)) | ((word) << (32-(bits))))
/* Define the SHA SIGMA and sigma macros */
#define SHA256_SIGMA0(word) \
(SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word))
#define SHA256_SIGMA1(word) \
(SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word))
#define SHA256_sigma0(word) \
(SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word))
#define SHA256_sigma1(word) \
(SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word))
/*
* Add "length" to the length.
* Set Corrupted when overflow has occurred.
*/
static uint32_t addTemp;
#define SHA224_256AddLength(context, length) \
(addTemp = (context)->Length_Low, (context)->Corrupted = \
(((context)->Length_Low += (length)) < addTemp) && \
(++(context)->Length_High == 0) ? shaInputTooLong : \
(context)->Corrupted )
/* Local Function Prototypes */
static int SHA224_256Reset(_pdfio_sha256_t *context, uint32_t *H0);
static void SHA224_256ProcessMessageBlock(_pdfio_sha256_t *context);
static void SHA224_256Finalize(_pdfio_sha256_t *context,
uint8_t Pad_Byte);
static void SHA224_256PadMessage(_pdfio_sha256_t *context,
uint8_t Pad_Byte);
static int SHA224_256ResultN(_pdfio_sha256_t *context,
uint8_t Message_Digest[ ], int HashSize);
/* Initial Hash Values: FIPS 180-3 section 5.3.3 */
static uint32_t SHA256_H0[SHA256HashSize/4] = {
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
};
/*
* _pdfioCryptoSHA256Init
*
* Description:
* This function will initialize the _pdfio_sha256_t in preparation
* for computing a new SHA256 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*/
void _pdfioCryptoSHA256Init(_pdfio_sha256_t *context)
{
SHA224_256Reset(context, SHA256_H0);
}
/*
* _pdfioCryptoSHA256Append
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_array[ ]: [in]
* An array of octets representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array.
*
* Returns:
* sha Error Code.
*/
void
_pdfioCryptoSHA256Append(_pdfio_sha256_t *context, const uint8_t *message_array,
size_t length)
{
if (!length) return;
while (length--) {
context->Message_Block[context->Message_Block_Index++] =
*message_array;
if ((SHA224_256AddLength(context, 8) == shaSuccess) &&
(context->Message_Block_Index == SHA256_Message_Block_Size))
SHA224_256ProcessMessageBlock(context);
message_array++;
}
}
/*
* _pdfioCryptoSHA256Finish
*
* Description:
* This function will return the 256-bit message digest
* into the Message_Digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 31.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest[ ]: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*/
void
_pdfioCryptoSHA256Finish(_pdfio_sha256_t *context,
uint8_t *Message_Digest)
{
SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
}
/*
* SHA224_256Reset
*
* Description:
* This helper function will initialize the _pdfio_sha256_t in
* preparation for computing a new SHA-224 or SHA-256 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
* H0[ ]: [in]
* The initial hash value array to use.
*
* Returns:
* sha Error Code.
*/
static int SHA224_256Reset(_pdfio_sha256_t *context, uint32_t *H0)
{
if (!context) return shaNull;
context->Length_High = context->Length_Low = 0;
context->Message_Block_Index = 0;
context->Intermediate_Hash[0] = H0[0];
context->Intermediate_Hash[1] = H0[1];
context->Intermediate_Hash[2] = H0[2];
context->Intermediate_Hash[3] = H0[3];
context->Intermediate_Hash[4] = H0[4];
context->Intermediate_Hash[5] = H0[5];
context->Intermediate_Hash[6] = H0[6];
context->Intermediate_Hash[7] = H0[7];
context->Computed = 0;
context->Corrupted = shaSuccess;
return shaSuccess;
}
/*
* SHA224_256ProcessMessageBlock
*
* Description:
* This helper function will process the next 512 bits of the
* message stored in the Message_Block array.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the Secure Hash Standard.
*/
static void SHA224_256ProcessMessageBlock(_pdfio_sha256_t *context)
{
/* Constants defined in FIPS 180-3, section 4.2.2 */
static const uint32_t K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
int t, t4; /* Loop counter */
uint32_t temp1, temp2; /* Temporary word value */
uint32_t W[64]; /* Word sequence */
uint32_t A, B, C, D, E, F, G, H; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for (t = t4 = 0; t < 16; t++, t4 += 4)
W[t] = (((uint32_t)context->Message_Block[t4]) << 24) |
(((uint32_t)context->Message_Block[t4 + 1]) << 16) |
(((uint32_t)context->Message_Block[t4 + 2]) << 8) |
(((uint32_t)context->Message_Block[t4 + 3]));
for (t = 16; t < 64; t++)
W[t] = SHA256_sigma1(W[t-2]) + W[t-7] +
SHA256_sigma0(W[t-15]) + W[t-16];
A = context->Intermediate_Hash[0];
B = context->Intermediate_Hash[1];
C = context->Intermediate_Hash[2];
D = context->Intermediate_Hash[3];
E = context->Intermediate_Hash[4];
F = context->Intermediate_Hash[5];
G = context->Intermediate_Hash[6];
H = context->Intermediate_Hash[7];
for (t = 0; t < 64; t++) {
temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t];
temp2 = SHA256_SIGMA0(A) + SHA_Maj(A,B,C);
H = G;
G = F;
F = E;
E = D + temp1;
D = C;
C = B;
B = A;
A = temp1 + temp2;
}
context->Intermediate_Hash[0] += A;
context->Intermediate_Hash[1] += B;
context->Intermediate_Hash[2] += C;
context->Intermediate_Hash[3] += D;
context->Intermediate_Hash[4] += E;
context->Intermediate_Hash[5] += F;
context->Intermediate_Hash[6] += G;
context->Intermediate_Hash[7] += H;
context->Message_Block_Index = 0;
}
/*
* SHA224_256Finalize
*
* Description:
* This helper function finishes off the digest calculations.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* Pad_Byte: [in]
* The last byte to add to the message block before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* sha Error Code.
*/
static void SHA224_256Finalize(_pdfio_sha256_t *context,
uint8_t Pad_Byte)
{
int i;
SHA224_256PadMessage(context, Pad_Byte);
/* message may be sensitive, so clear it out */
for (i = 0; i < SHA256_Message_Block_Size; ++i)
context->Message_Block[i] = 0;
context->Length_High = 0; /* and clear length */
context->Length_Low = 0;
context->Computed = 1;
}
/*
* SHA224_256PadMessage
*
* Description:
* According to the standard, the message must be padded to the next
* even multiple of 512 bits. The first padding bit must be a '1'.
* The last 64 bits represent the length of the original message.
* All bits in between should be 0. This helper function will pad
* the message according to those rules by filling the
* Message_Block array accordingly. When it returns, it can be
* assumed that the message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad.
* Pad_Byte: [in]
* The last byte to add to the message block before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* Nothing.
*/
static void SHA224_256PadMessage(_pdfio_sha256_t *context,
uint8_t Pad_Byte)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index >= (SHA256_Message_Block_Size-8)) {
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < SHA256_Message_Block_Size)
context->Message_Block[context->Message_Block_Index++] = 0;
SHA224_256ProcessMessageBlock(context);
} else
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < (SHA256_Message_Block_Size-8))
context->Message_Block[context->Message_Block_Index++] = 0;
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = (uint8_t)(context->Length_High >> 24);
context->Message_Block[57] = (uint8_t)(context->Length_High >> 16);
context->Message_Block[58] = (uint8_t)(context->Length_High >> 8);
context->Message_Block[59] = (uint8_t)(context->Length_High);
context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24);
context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16);
context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8);
context->Message_Block[63] = (uint8_t)(context->Length_Low);
SHA224_256ProcessMessageBlock(context);
}
/*
* SHA224_256ResultN
*
* Description:
* This helper function will return the 224-bit or 256-bit message
* digest into the Message_Digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 27/31.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest[ ]: [out]
* Where the digest is returned.
* HashSize: [in]
* The size of the hash, either 28 or 32.
*
* Returns:
* sha Error Code.
*/
static int SHA224_256ResultN(_pdfio_sha256_t *context,
uint8_t Message_Digest[ ], int HashSize)
{
int i;
if (!context) return shaNull;
if (!Message_Digest) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (!context->Computed)
SHA224_256Finalize(context, 0x80);
for (i = 0; i < HashSize; ++i)
Message_Digest[i] = (uint8_t)
(context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ));
return shaSuccess;
}

View File

@ -1,16 +1,12 @@
//
// PDF stream functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -54,6 +50,10 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
while ((status = deflate(&st->flate, Z_FINISH)) != Z_STREAM_END)
{
size_t bytes = st->cbsize - st->flate.avail_out,
// Bytes to write
outbytes; // Actual bytes written
if (status < Z_OK && status != Z_BUF_ERROR)
{
_pdfioFileError(st->pdf, "Flate compression failed: %s", zstrerror(status));
@ -61,25 +61,70 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
goto done;
}
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
if (st->crypto_cb)
{
// Encrypt it first...
outbytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, bytes & (size_t)~15);
}
else
{
// No encryption
outbytes = bytes;
}
if (!_pdfioFileWrite(st->pdf, st->cbuffer, outbytes))
{
ret = false;
goto done;
}
st->flate.next_out = (Bytef *)st->cbuffer;
st->flate.avail_out = (uInt)sizeof(st->cbuffer);
if (bytes > outbytes)
{
bytes -= outbytes;
memmove(st->cbuffer, st->cbuffer + outbytes, bytes);
}
else
{
bytes = 0;
}
st->flate.next_out = (Bytef *)st->cbuffer + bytes;
st->flate.avail_out = (uInt)(st->cbsize - bytes);
}
if (st->flate.avail_out < (uInt)sizeof(st->cbuffer))
if (st->flate.avail_out < (uInt)st->cbsize)
{
// Write any residuals...
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
size_t bytes = st->cbsize - st->flate.avail_out;
// Bytes to write
if (st->crypto_cb)
{
// Encrypt it first...
bytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, bytes);
}
if (!_pdfioFileWrite(st->pdf, st->cbuffer, bytes))
{
ret = false;
goto done;
}
}
deflateEnd(&st->flate);
}
else if (st->crypto_cb && st->bufptr > st->buffer)
{
// Encrypt and flush
uint8_t temp[8192]; // Temporary buffer
size_t outbytes; // Output bytes
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, (uint8_t *)st->buffer, (size_t)(st->bufptr - st->buffer));
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
{
ret = false;
goto done;
}
}
// Save the length of this stream...
@ -93,7 +138,12 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
}
// Update the length as needed...
if (st->obj->length_offset)
if (st->length_obj)
{
st->length_obj->value.value.number = (double)st->obj->stream_length;
pdfioObjClose(st->length_obj);
}
else if (st->obj->length_offset)
{
// Seek back to the "/Length 9999999999" we wrote...
if (_pdfioFileSeek(st->pdf, st->obj->length_offset, SEEK_SET) < 0)
@ -120,6 +170,9 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
done:
st->pdf->current_obj = NULL;
free(st->cbuffer);
free(st->prbuffer);
free(st->psbuffer);
free(st);
@ -137,6 +190,8 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
pdfio_stream_t * // O - Stream or `NULL` on error
_pdfioStreamCreate(
pdfio_obj_t *obj, // I - Object
pdfio_obj_t *length_obj, // I - Length object, if any
size_t cbsize, // I - Size of compression buffer
pdfio_filter_t compression) // I - Compression to apply
{
pdfio_stream_t *st; // Stream
@ -149,9 +204,28 @@ _pdfioStreamCreate(
return (NULL);
}
st->pdf = obj->pdf;
st->obj = obj;
st->filter = compression;
st->pdf = obj->pdf;
st->obj = obj;
st->length_obj = length_obj;
st->filter = compression;
st->bufptr = st->buffer;
st->bufend = st->buffer + sizeof(st->buffer);
if (obj->pdf->encryption)
{
uint8_t iv[64]; // Initialization vector
size_t ivlen = sizeof(iv); // Length of initialization vector, if any
if ((st->crypto_cb = _pdfioCryptoMakeWriter(st->pdf, obj, &st->crypto_ctx, iv, &ivlen)) == NULL)
{
// TODO: Add error message?
free(st);
return (NULL);
}
if (ivlen > 0)
_pdfioFileWrite(st->pdf, iv, ivlen);
}
if (compression == PDFIO_FILTER_FLATE)
{
@ -230,8 +304,21 @@ _pdfioStreamCreate(
else
st->predictor = _PDFIO_PREDICTOR_NONE;
if (cbsize == 0)
cbsize = 4096;
st->cbsize = cbsize;
if ((st->cbuffer = malloc(cbsize)) == NULL)
{
_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Flate output buffer: %s", (unsigned long)cbsize, strerror(errno));
free(st->prbuffer);
free(st->psbuffer);
free(st);
return (NULL);
}
st->flate.next_out = (Bytef *)st->cbuffer;
st->flate.avail_out = (uInt)sizeof(st->cbuffer);
st->flate.avail_out = (uInt)cbsize;
if ((status = deflateInit(&(st->flate), 9)) != Z_OK)
{
@ -290,14 +377,23 @@ pdfioStreamConsume(pdfio_stream_t *st, // I - Stream
//
// 'pdfioStreamGetToken()' - Read a single PDF token from a stream.
//
// This function reads a single PDF token from a stream, skipping all whitespace
// and comments. Operator tokens, boolean values, and numbers are returned
// as-is in the provided string buffer. String values start with the opening
// parenthesis ('(') but have all escaping resolved and the terminating
// parenthesis removed. Hexadecimal string values start with the opening angle
// bracket ('<') and have all whitespace and the terminating angle bracket
// removed.
//
bool // O - `true` on success, `false` on EOF
bool // O - `true` on success, `false` on end-of-stream or error
pdfioStreamGetToken(
pdfio_stream_t *st, // I - Stream
char *buffer, // I - String buffer
size_t bufsize) // I - Size of string buffer
{
_pdfio_token_t tb; // Token buffer/stack
bool ret; // Return value
// Range check input...
@ -307,7 +403,10 @@ pdfioStreamGetToken(
// Read using the token engine...
_pdfioTokenInit(&tb, st->pdf, (_pdfio_tconsume_cb_t)pdfioStreamConsume, (_pdfio_tpeek_cb_t)pdfioStreamPeek, st);
return (_pdfioTokenRead(&tb, buffer, bufsize));
ret = _pdfioTokenRead(&tb, buffer, bufsize);
_pdfioTokenFlush(&tb);
return (ret);
}
@ -325,6 +424,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
pdfio_stream_t *st; // Stream
pdfio_dict_t *dict = pdfioObjGetDict(obj);
// Object dictionary
const char *type; // Object type
PDFIO_DEBUG("_pdfioStreamOpen(obj=%p(%u), decode=%s)\n", obj, (unsigned)obj->number, decode ? "true" : "false");
@ -339,16 +439,36 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
st->pdf = obj->pdf;
st->obj = obj;
if ((st->remaining = pdfioObjGetLength(obj)) == 0)
if ((st->remaining = pdfioObjGetLength(obj)) == 0 && !_pdfioDictGetValue(pdfioObjGetDict(obj), "Length"))
{
free(st);
return (NULL);
_pdfioFileError(obj->pdf, "No stream data.");
goto error;
}
if (_pdfioFileSeek(st->pdf, obj->stream_offset, SEEK_SET) != obj->stream_offset)
{
free(st);
return (NULL);
_pdfioFileError(obj->pdf, "Unable to seek to stream data.");
goto error;
}
type = pdfioObjGetType(obj);
if (obj->pdf->encryption && (!type || strcmp(type, "XRef")))
{
uint8_t iv[64]; // Initialization vector
size_t ivlen; // Length of initialization vector, if any
ivlen = (size_t)_pdfioFilePeek(st->pdf, iv, sizeof(iv));
if ((st->crypto_cb = _pdfioCryptoMakeReader(st->pdf, obj, &st->crypto_ctx, iv, &ivlen)) == NULL)
goto error;
PDFIO_DEBUG("_pdfioStreamOpen: ivlen=%d\n", (int)ivlen);
if (ivlen > 0)
_pdfioFileConsume(st->pdf, ivlen);
if (st->pdf->encryption >= PDFIO_ENCRYPTION_AES_128)
st->remaining = (st->remaining + 15) & (size_t)~15;
}
if (decode)
@ -356,16 +476,23 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
// Try to decode/decompress the contents of this object...
const char *filter = pdfioDictGetName(dict, "Filter");
// Filter value
pdfio_array_t *fa = pdfioDictGetArray(dict, "Filter");
// Filter array
if (!filter && fa && pdfioArrayGetSize(fa) == 1)
{
// Support single-valued arrays...
filter = pdfioArrayGetName(fa, 0);
}
if (!filter)
{
// No single filter name, do we have a compound filter?
if (pdfioDictGetArray(dict, "Filter"))
if (fa)
{
// TODO: Implement compound filters...
_pdfioFileError(st->pdf, "Unsupported compound stream filter.");
free(st);
return (NULL);
goto error;
}
// No filter, read as-is...
@ -398,8 +525,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
else if (bpc < 1 || bpc == 3 || (bpc > 4 && bpc < 8) || (bpc > 8 && bpc < 16) || bpc > 16)
{
_pdfioFileError(st->pdf, "Unsupported BitsPerColor value %d.", bpc);
free(st);
return (NULL);
goto error;
}
if (colors == 0)
@ -409,8 +535,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
else if (colors < 0 || colors > 4)
{
_pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors);
free(st);
return (NULL);
goto error;
}
if (columns == 0)
@ -420,15 +545,13 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
else if (columns < 0)
{
_pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns);
free(st);
return (NULL);
goto error;
}
if ((predictor > 2 && predictor < 10) || predictor > 15)
{
_pdfioFileError(st->pdf, "Unsupported Predictor function %d.", predictor);
free(st);
return (NULL);
goto error;
}
else if (predictor > 1)
{
@ -442,47 +565,45 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
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);
free(st->prbuffer);
free(st->psbuffer);
free(st);
return (NULL);
goto error;
}
}
else
{
st->predictor = _PDFIO_PREDICTOR_NONE;
}
if (sizeof(st->cbuffer) > st->remaining)
st->cbsize = 4096;
if ((st->cbuffer = malloc(st->cbsize)) == NULL)
{
_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Flate compression buffer.", (unsigned long)st->cbsize);
goto error;
}
PDFIO_DEBUG("_pdfioStreamOpen: pos=%ld\n", (long)_pdfioFileTell(st->pdf));
if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer));
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->cbsize);
if (rbytes <= 0)
{
_pdfioFileError(st->pdf, "Unable to read bytes for stream.");
free(st->prbuffer);
free(st->psbuffer);
free(st);
return (NULL);
goto error;
}
if (st->crypto_cb)
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;
if (st->cbuffer[0] == 0x0a)
{
st->flate.next_in ++; // Skip newline
st->flate.avail_in --;
}
PDFIO_DEBUG("_pdfioStreamOpen: avail_in=%u, cbuffer=<%02X%02X%02X%02X%02X%02X%02X%02X...>\n", st->flate.avail_in, st->cbuffer[0], st->cbuffer[1], st->cbuffer[2], st->cbuffer[3], st->cbuffer[4], st->cbuffer[5], st->cbuffer[6], st->cbuffer[7]);
if ((status = inflateInit(&(st->flate))) != Z_OK)
{
_pdfioFileError(st->pdf, "Unable to start Flate filter: %s", zstrerror(status));
free(st->prbuffer);
free(st->psbuffer);
free(st);
return (NULL);
goto error;
}
st->remaining -= st->flate.avail_in;
@ -495,9 +616,8 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
else
{
// Something else we don't support
_pdfioFileError(st->pdf, "Unsupported stream filter '/%s'.", filter);
free(st);
return (NULL);
_pdfioFileError(st->pdf, "Unsupported stream filter '%N'.", filter);
goto error;
}
}
else
@ -507,6 +627,16 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
}
return (st);
// If we get here something went wrong...
error:
free(st->cbuffer);
free(st->prbuffer);
free(st->psbuffer);
free(st);
return (NULL);
}
@ -559,6 +689,10 @@ pdfioStreamPeek(pdfio_stream_t *st, // I - Stream
//
// 'pdfioStreamPrintf()' - Write a formatted string to a stream.
//
// This function writes a formatted string to a stream. In addition to the
// standard `printf` format characters, you can use "%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
pdfioStreamPrintf(
@ -576,7 +710,7 @@ pdfioStreamPrintf(
// Format the string...
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
_pdfio_vsnprintf(st->pdf, buffer, sizeof(buffer), format, ap);
va_end(ap);
// Write the string...
@ -585,7 +719,7 @@ pdfioStreamPrintf(
//
// '()' - Write a single character to a stream.
// 'pdfioStreamPutChar()' - Write a single character to a stream.
//
bool // O - `true` on success, `false` on failure
@ -715,8 +849,63 @@ pdfioStreamWrite(
// Write it...
if (st->filter == PDFIO_FILTER_NONE)
{
// No filtering so just write it...
return (_pdfioFileWrite(st->pdf, buffer, bytes));
// No filtering...
if (st->crypto_cb)
{
// Encrypt data before writing...
uint8_t temp[8192]; // Temporary buffer
size_t cbytes, // Current bytes
outbytes; // Output bytes
bufptr = (const unsigned char *)buffer;
while (bytes > 0)
{
if (st->bufptr > st->buffer || bytes < 16)
{
// Write through the stream's buffer...
if ((cbytes = bytes) > (size_t)(st->bufend - st->bufptr))
cbytes = (size_t)(st->bufend - st->bufptr);
memcpy(st->bufptr, bufptr, cbytes);
st->bufptr += cbytes;
if (st->bufptr >= st->bufend)
{
// Encrypt and flush
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, (uint8_t *)st->buffer, sizeof(st->buffer));
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
return (false);
st->bufptr = st->buffer;
}
}
else
{
// Write directly up to sizeof(temp) bytes...
if ((cbytes = bytes) > sizeof(temp))
cbytes = sizeof(temp);
if (cbytes & 15)
{
// AES has a 16-byte block size, so save the last few bytes...
cbytes &= (size_t)~15;
}
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, bufptr, cbytes);
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
return (false);
}
bytes -= cbytes;
bufptr += cbytes;
}
return (true);
}
else
{
// Write unencrypted...
return (_pdfioFileWrite(st->pdf, buffer, bytes));
}
}
pbline = st->pbsize - 1;
@ -842,6 +1031,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
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)
@ -853,8 +1043,13 @@ stream_read(pdfio_stream_t *st, // I - Stream
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)
@ -870,14 +1065,17 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (st->flate.avail_in == 0)
{
// Read more from the file...
if (sizeof(st->cbuffer) > st->remaining)
if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer));
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;
@ -888,7 +1086,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status));
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
@ -923,25 +1121,31 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (st->flate.avail_in == 0)
{
// Read more from the file...
if (sizeof(st->cbuffer) > st->remaining)
if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer));
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: %s", zstrerror(status));
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
else if (status == Z_STREAM_END)
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
break;
}
@ -987,25 +1191,31 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (st->flate.avail_in == 0)
{
// Read more from the file...
if (sizeof(st->cbuffer) > st->remaining)
if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer));
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: %s", zstrerror(status));
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
else if (status == Z_STREAM_END)
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
break;
}
@ -1088,14 +1298,39 @@ stream_write(pdfio_stream_t *st, // I - Stream
while (st->flate.avail_in > 0)
{
if (st->flate.avail_out < (sizeof(st->cbuffer) / 8))
if (st->flate.avail_out < (st->cbsize / 8))
{
// Flush the compression buffer...
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
size_t cbytes = st->cbsize - st->flate.avail_out,
outbytes;
if (st->crypto_cb)
{
// Encrypt it first...
outbytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, cbytes & (size_t)~15);
}
else
{
outbytes = cbytes;
}
// fprintf(stderr, "stream_write: bytes=%u, outbytes=%u\n", (unsigned)bytes, (unsigned)outbytes);
if (!_pdfioFileWrite(st->pdf, st->cbuffer, outbytes))
return (false);
st->flate.next_out = (Bytef *)st->cbuffer;
st->flate.avail_out = sizeof(st->cbuffer);
if (cbytes > outbytes)
{
cbytes -= outbytes;
memmove(st->cbuffer, st->cbuffer + outbytes, cbytes);
}
else
{
cbytes = 0;
}
st->flate.next_out = (Bytef *)st->cbuffer + cbytes;
st->flate.avail_out = (uInt)(st->cbsize - cbytes);
}
// Deflate what we can this time...

View File

@ -1,16 +1,12 @@
//
// PDF dictionary functions for PDFio.
// PDF string functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -18,7 +14,522 @@
// Local functions...
//
static int compare_strings(char **a, char **b);
static size_t find_string(pdfio_file_t *pdf, const char *s, int *rdiff);
//
// '_pdfio_strlcpy()' - Safe string copy.
//
size_t // O - Length of source string
_pdfio_strlcpy(char *dst, // I - Destination string buffer
const char *src, // I - Source string
size_t dstsize) // I - Size of destination
{
size_t srclen; // Length of source string
// Range check input...
if (!dst || !src || dstsize == 0)
{
if (dst)
*dst = '\0';
return (0);
}
// Figure out how much room is needed...
dstsize --;
srclen = strlen(src);
// Copy the appropriate amount...
if (srclen <= dstsize)
{
// Source string will fit...
memmove(dst, src, srclen);
dst[srclen] = '\0';
}
else
{
// Source string too big, copy what we can and clean up the end...
char *ptr = dst + dstsize - 1, // Pointer into string
*end = ptr + 1; // Pointer to end of string
memmove(dst, src, dstsize);
dst[dstsize] = '\0';
// Validate last character in destination buffer...
if (ptr > dst && *ptr & 0x80)
{
while ((*ptr & 0xc0) == 0x80 && ptr > dst)
ptr --;
if ((*ptr & 0xe0) == 0xc0)
{
// Verify 2-byte UTF-8 sequence...
if ((end - ptr) != 2)
*ptr = '\0';
}
else if ((*ptr & 0xf0) == 0xe0)
{
// Verify 3-byte UTF-8 sequence...
if ((end - ptr) != 3)
*ptr = '\0';
}
else if ((*ptr & 0xf8) == 0xf0)
{
// Verify 4-byte UTF-8 sequence...
if ((end - ptr) != 4)
*ptr = '\0';
}
else if (*ptr & 0x80)
{
// Invalid sequence at end...
*ptr = '\0';
}
}
}
return (srclen);
}
//
// '_pdfio_strtod()' - Convert a string to a double value.
//
// This function wraps strtod() to avoid locale issues.
//
double // O - Double value
_pdfio_strtod(pdfio_file_t *pdf, // I - PDF file
const char *s) // I - String
{
char temp[64], // Temporary buffer
*tempptr; // Pointer into temporary buffer
// See if the locale has a special decimal point string...
if (!pdf->loc)
return (strtod(s, NULL));
// Copy leading sign, numbers, period, and then numbers...
tempptr = temp;
temp[sizeof(temp) - 1] = '\0';
while (*s && *s != '.')
{
if (tempptr < (temp + sizeof(temp) - 1))
*tempptr++ = *s++;
else
return (0.0);
}
if (*s == '.')
{
// Convert decimal point to locale equivalent...
size_t declen = strlen(pdf->loc->decimal_point);
// Length of decimal point
s ++;
if (declen <= (sizeof(temp) - (size_t)(tempptr - temp)))
{
memcpy(tempptr, pdf->loc->decimal_point, declen);
tempptr += declen;
}
else
{
return (0.0);
}
}
// Copy any remaining characters...
while (*s)
{
if (tempptr < (temp + sizeof(temp) - 1))
*tempptr++ = *s++;
else
return (0.0);
}
// Nul-terminate the temporary string and convert the string...
*tempptr = '\0';
return (strtod(temp, NULL));
}
//
// '_pdfio_vsnprintf()' - Format a string.
//
// This function emulates vsnprintf() to avoid locale issues.
//
ssize_t // O - Number of bytes
_pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
char *buffer, // I - Output buffer
size_t bufsize, // I - Size of output buffer
const char *format, // I - printf-style format string
va_list ap) // I - Pointer to additional arguments
{
char *bufptr, // Pointer to position in buffer
*bufend, // Pointer to end of buffer
size, // Size character (h, l, L)
type; // Format type character
int width, // Width of field
prec; // Number of characters of precision
char tformat[100], // Temporary format string for snprintf()
*tptr, // Pointer into temporary format
temp[1024], // Buffer for formatted numbers
*tempptr; // Pointer into buffer
char *s; // Pointer to string
ssize_t bytes; // Total number of bytes needed
const char *dec = pdf->loc ? pdf->loc->decimal_point : ".";
// Decimal point string
char *decptr; // Pointer to decimal point
// Loop through the format string, formatting as needed...
bufptr = buffer;
bufend = buffer + bufsize - 1;
bytes = 0;
while (*format)
{
if (*format == '%')
{
// Format character...
tptr = tformat;
*tptr++ = *format++;
if (*format == '%')
{
if (bufptr < bufend)
*bufptr++ = *format;
bytes ++;
format ++;
continue;
}
else if (strchr(" -+#\'", *format))
{
*tptr++ = *format++;
}
if (*format == '*')
{
// Get width from argument...
format ++;
width = va_arg(ap, int);
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
tptr += strlen(tptr);
}
else
{
width = 0;
while (isdigit(*format & 255))
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
width = width * 10 + *format++ - '0';
}
}
if (*format == '.')
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
format ++;
if (*format == '*')
{
// Get precision from argument...
format ++;
prec = va_arg(ap, int);
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
tptr += strlen(tptr);
}
else
{
while (isdigit(*format & 255))
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
format ++;
}
}
}
if (*format == 'l' && format[1] == 'l')
{
size = 'L';
if (tptr < (tformat + sizeof(tformat) - 2))
{
*tptr++ = 'l';
*tptr++ = 'l';
}
format += 2;
}
else if (*format == 'h' || *format == 'l' || *format == 'L')
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
size = *format++;
}
else
{
size = 0;
}
if (!*format)
break;
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
type = *format++;
*tptr = '\0';
switch (type)
{
case 'E' : // Floating point formats
case 'G' :
case 'e' :
case 'f' :
case 'g' :
if ((size_t)(width + 2) > sizeof(temp))
break;
snprintf(temp, sizeof(temp), tformat, va_arg(ap, double));
if ((decptr = strstr(temp, dec)) != NULL)
{
// Convert locale decimal point to "."
PDFIO_DEBUG("_pdfio_vsnprintf: Before \"%s\"\n", temp);
tempptr = decptr + strlen(dec);
if (tempptr > (decptr + 1))
memmove(decptr + 1, tempptr, strlen(tempptr) + 1);
*decptr = '.';
// Strip trailing 0's...
for (tempptr = temp + strlen(temp) - 1; tempptr > temp && *tempptr == '0'; tempptr --)
*tempptr = '\0';
if (*tempptr == '.')
*tempptr = '\0'; // Strip trailing decimal point
PDFIO_DEBUG("_pdfio_vsnprintf: After \"%s\"\n", temp);
}
// Copy to the output buffer
bytes += (int)strlen(temp);
if (bufptr < bufend)
{
_pdfio_strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr);
}
break;
case 'B' : // Integer formats
case 'X' :
case 'b' :
case 'd' :
case 'i' :
case 'o' :
case 'u' :
case 'x' :
if ((size_t)(width + 2) > sizeof(temp))
break;
# ifdef HAVE_LONG_LONG
if (size == 'L')
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long));
else
# endif // HAVE_LONG_LONG
if (size == 'l')
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long));
else
snprintf(temp, sizeof(temp), tformat, va_arg(ap, int));
bytes += (int)strlen(temp);
if (bufptr < bufend)
{
_pdfio_strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr);
}
break;
case 'p' : // Pointer value
if ((size_t)(width + 2) > sizeof(temp))
break;
snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *));
bytes += (int)strlen(temp);
if (bufptr < bufend)
{
_pdfio_strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr);
}
break;
case 'c' : // Character or character array
bytes += width;
if (bufptr < bufend)
{
if (width <= 1)
{
*bufptr++ = (char)va_arg(ap, int);
}
else
{
if ((bufptr + width) > bufend)
width = (int)(bufend - bufptr);
memcpy(bufptr, va_arg(ap, char *), (size_t)width);
bufptr += width;
}
}
break;
case 'S' : // PDF string
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
// PDF strings start with "("...
if (bufptr < bufend)
*bufptr++ = '(';
bytes ++;
// Loop through the literal string...
while (*s)
{
// Escape special characters
if (*s == '\\' || *s == '(' || *s == ')')
{
// Simple escape...
if (bufptr < bufend)
*bufptr++ = '\\';
if (bufptr < bufend)
*bufptr++ = *s;
bytes += 2;
}
else if (*s < ' ')
{
// Octal escape...
snprintf(bufptr, (size_t)(bufend - bufptr + 1), "\\%03o", *s & 255);
bufptr += strlen(bufptr);
bytes += 4;
}
else
{
// Literal character...
if (bufptr < bufend)
*bufptr++ = *s;
bytes ++;
}
s ++;
}
// PDF strings end with ")"...
if (bufptr < bufend)
*bufptr++ = ')';
bytes ++;
break;
case 's' : // Literal string
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
if (width != 0)
{
// Format string to fit inside the specified width...
if ((size_t)(width + 1) > sizeof(temp))
break;
snprintf(temp, sizeof(temp), tformat, s);
s = temp;
}
bytes += strlen(s);
if (bufptr < bufend)
{
_pdfio_strlcpy(bufptr, s, (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr);
}
break;
case 'N' : // Output name string with proper escaping
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
// PDF names start with "/"...
if (bufptr < bufend)
*bufptr++ = '/';
bytes ++;
// Loop through the name string...
while (*s)
{
if (*s < 0x21 || *s > 0x7e || *s == '#')
{
// Output #XX for character...
snprintf(bufptr, (size_t)(bufend - bufptr + 1), "#%02X", *s & 255);
bufptr += strlen(bufptr);
bytes += 3;
}
else
{
// Output literal character...
if (bufptr < bufend)
*bufptr++ = *s;
bytes ++;
}
s ++;
}
break;
case 'n' : // Output number of chars so far
*(va_arg(ap, int *)) = (int)bytes;
break;
}
}
else
{
// Literal character...
bytes ++;
if (bufptr < bufend)
*bufptr++ = *format++;
}
}
// Nul-terminate the string and return the number of characters needed.
*bufptr = '\0';
PDFIO_DEBUG("_pdfio_vsnprintf: Returning %ld \"%s\"\n", (long)bytes, buffer);
return (bytes);
}
//
@ -36,8 +547,9 @@ pdfioStringCreate(
pdfio_file_t *pdf, // I - PDF file
const char *s) // I - Nul-terminated string
{
char *news; // New string
char **match; // Matching string
char *news; // New string
size_t idx; // Index into strings
int diff; // Different
PDFIO_DEBUG("pdfioStringCreate(pdf=%p, s=\"%s\")\n", pdf, s);
@ -47,8 +559,17 @@ pdfioStringCreate(
return (NULL);
// See if the string has already been added...
if (pdf->num_strings > 0 && (match = (char **)bsearch(&s, pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings)) != NULL)
return (*match);
if (pdf->num_strings > 0)
{
idx = find_string(pdf, s, &diff);
if (diff == 0)
return (pdf->strings[idx]);
}
else
{
idx = 0;
diff = -1;
}
// Not already added, so add it...
if ((news = strdup(s)) == NULL)
@ -69,11 +590,17 @@ pdfioStringCreate(
pdf->alloc_strings += 128;
}
// TODO: Change to insertion sort as needed...
pdf->strings[pdf->num_strings ++] = news;
// Insert the string...
if (diff > 0)
idx ++;
if (pdf->num_strings > 1)
qsort(pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings);
PDFIO_DEBUG("pdfioStringCreate: Inserting \"%s\" at %u\n", news, (unsigned)idx);
if (idx < pdf->num_strings)
memmove(pdf->strings + idx + 1, pdf->strings + idx, (pdf->num_strings - idx) * sizeof(char *));
pdf->strings[idx] = news;
pdf->num_strings ++;
PDFIO_DEBUG("pdfioStringCreate: %lu strings\n", (unsigned long)pdf->num_strings);
@ -124,17 +651,67 @@ _pdfioStringIsAllocated(
pdfio_file_t *pdf, // I - PDF file
const char *s) // I - String
{
return (pdf->num_strings > 0 && bsearch(&s, pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings) != NULL);
int diff; // Difference
if (pdf->num_strings == 0)
return (false);
find_string(pdf, s, &diff);
return (diff == 0);
}
//
// 'compare_strings()' - Compare two strings.
// 'find_string()' - Find an element in the array.
//
static int // O - Result of comparison
compare_strings(char **a, // I - First string
char **b) // I - Second string
static size_t // O - Index of match
find_string(pdfio_file_t *pdf, // I - PDF file
const char *s, // I - String to find
int *rdiff) // O - Difference of match
{
return (strcmp(*a, *b));
size_t left, // Left side of search
right, // Right side of search
current; // Current element
int diff; // Comparison with current element
// Do a binary search for the string...
left = 0;
right = pdf->num_strings - 1;
do
{
current = (left + right) / 2;
diff = strcmp(s, pdf->strings[current]);
if (diff == 0)
break;
else if (diff < 0)
right = current;
else
left = current;
}
while ((right - left) > 1);
if (diff != 0)
{
// Check the last 1 or 2 elements...
if ((diff = strcmp(s, pdf->strings[left])) <= 0)
{
current = left;
}
else
{
diff = strcmp(s, pdf->strings[right]);
current = right;
}
}
// Return the closest string and the difference...
*rdiff = diff;
return (current);
}

View File

@ -1,16 +1,12 @@
//
// PDF token parsing functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2023 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -129,9 +125,20 @@ _pdfioTokenGet(_pdfio_token_t *tb, // I - Token buffer/stack
if (tb->num_tokens > 0)
{
// Yes, return it...
size_t len; // Length of token
tb->num_tokens --;
strncpy(buffer, tb->tokens[tb->num_tokens], bufsize - 1);
buffer[bufsize - 1] = '\0';
if ((len = strlen(tb->tokens[tb->num_tokens])) > (bufsize - 1))
{
// Value too large...
PDFIO_DEBUG("_pdfioTokenGet(tb=%p, buffer=%p, bufsize=%u): Token '%s' from stack too large.\n", tb, buffer, (unsigned)bufsize, tb->tokens[tb->num_tokens]);
*buffer = '\0';
return (false);
}
memcpy(buffer, tb->tokens[tb->num_tokens], len);
buffer[len] = '\0';
PDFIO_DEBUG("_pdfioTokenGet(tb=%p, buffer=%p, bufsize=%u): Popping '%s' from stack.\n", tb, buffer, (unsigned)bufsize, buffer);
@ -200,9 +207,11 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
char *bufptr, // Pointer into buffer
*bufend, // End of buffer
state = '\0'; // Current state
bool saw_nul = false; // Did we see a nul character?
size_t count = 0; // Number of whitespace/comment bytes
//
// "state" is:
//
// - '\0' for idle
@ -221,21 +230,45 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
// Skip leading whitespace...
while ((ch = get_char(tb)) != EOF)
{
count ++;
if (ch == '%')
{
// Skip comment
PDFIO_DEBUG("_pdfioTokenRead: Skipping comment...\n");
while ((ch = get_char(tb)) != EOF)
{
count ++;
if (ch == '\n' || ch == '\r')
{
break;
}
else if (count > 2048)
{
_pdfioFileError(tb->pdf, "Comment too long.");
*bufptr = '\0';
return (false);
}
}
}
else if (!isspace(ch))
{
break;
}
else if (count > 2048)
{
_pdfioFileError(tb->pdf, "Too much whitespace.");
*bufptr = '\0';
return (false);
}
}
if (ch == EOF)
{
*bufptr = '\0';
return (false);
}
// Check for delimiters...
if (strchr(PDFIO_DELIM_CHARS, ch) != NULL)
@ -255,11 +288,16 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
*bufptr++ = (char)ch;
}
PDFIO_DEBUG("_pdfioTokenRead: state='%c'\n", state);
switch (state)
{
case '(' : // Literal string
while ((ch = get_char(tb)) != EOF)
{
if (ch == 0)
saw_nul = true;
if (ch == '\\')
{
// Quoted character...
@ -280,7 +318,9 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
int tch = get_char(tb); // Next char
if (tch >= '0' && tch <= '7')
{
ch = (char)((ch << 3) | (tch - '0'));
}
else
{
tb->bufptr --;
@ -341,6 +381,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Out of space
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
}
@ -348,8 +389,40 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
if (ch != ')')
{
_pdfioFileError(tb->pdf, "Unterminated string literal.");
*bufptr = '\0';
return (false);
}
if (saw_nul)
{
// Convert to a hex (binary) string...
char *litptr, // Pointer to literal character
*hexptr; // Pointer to hex character
size_t bytes = (size_t)(bufptr - buffer - 1);
// Bytes of data...
static const char *hexchars = "0123456789ABCDEF";
// Hex digits
PDFIO_DEBUG("_pdfioTokenRead: Converting nul-containing string to binary.\n");
if ((2 * (bytes + 1)) > bufsize)
{
// Out of space...
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
*buffer = '<';
for (litptr = bufptr - 1, hexptr = buffer + 2 * bytes - 1; litptr > buffer; litptr --, hexptr -= 2)
{
int litch = *litptr; // Grab the character
hexptr[0] = hexchars[(litch >> 4) & 15];
hexptr[1] = hexchars[litch & 15];
}
bufptr = buffer + 2 * bytes + 1;
}
break;
case 'K' : // keyword
@ -370,9 +443,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Out of space...
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
}
if (ch == '\r')
{
// Look for a trailing LF
if ((ch = get_char(tb)) != EOF && ch != '\n')
tb->bufptr --;
}
break;
case 'N' : // number
@ -381,6 +462,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
if (!isdigit(ch) && ch != '.')
{
// End of number...
PDFIO_DEBUG("_pdfioTokenRead: End of number with ch=0x%02x\n", ch);
tb->bufptr --;
break;
}
@ -393,6 +475,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Out of space...
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
}
@ -419,12 +502,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
if (!isxdigit(tch & 255))
{
_pdfioFileError(tb->pdf, "Bad # escape in name.");
*bufptr = '\0';
return (false);
}
else if (isdigit(tch))
{
ch = ((ch & 255) << 4) | (tch - '0');
}
else
{
ch = ((ch & 255) << 4) | (tolower(tch) - 'a' + 10);
}
}
}
@ -436,9 +524,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Out of space
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
}
if (bufptr == (buffer + 1))
{
_pdfioFileError(tb->pdf, "Empty name.");
*bufptr = '\0';
return (false);
}
break;
case '<' : // Potential hex string
@ -448,13 +544,23 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
*bufptr++ = (char)ch;
break;
}
else if (ch == '>')
{
// Issue #46: Empty hex string from Microsoft PDF generator; treat as
// empty literal string...
*buffer = '(';
break;
}
else if (!isspace(ch & 255) && !isxdigit(ch & 255))
{
_pdfioFileError(tb->pdf, "Syntax error: '<%c'", ch);
*bufptr = '\0';
return (false);
}
while ((ch = get_char(tb)) != EOF && ch != '>')
count = 0;
do
{
if (isxdigit(ch))
{
@ -462,24 +568,39 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Hex digit
*bufptr++ = (char)ch;
count = 0;
}
else
{
// Too large
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
}
else if (!isspace(ch))
{
_pdfioFileError(tb->pdf, "Invalid hex string character '%c'.", ch);
*bufptr = '\0';
return (false);
}
else
{
count ++;
if (count > 2048)
{
_pdfioFileError(tb->pdf, "Too much whitespace.");
*bufptr = '\0';
return (false);
}
}
}
while ((ch = get_char(tb)) != EOF && ch != '>');
if (ch == EOF)
{
_pdfioFileError(tb->pdf, "Unterminated hex string.");
*bufptr = '\0';
return (false);
}
break;
@ -492,14 +613,12 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
else
{
_pdfioFileError(tb->pdf, "Syntax error: '>%c'.", ch);
*bufptr = '\0';
return (false);
}
break;
}
while (tb->bufptr < tb->bufend && isspace(*(tb->bufptr)))
tb->bufptr ++;
*bufptr = '\0';
PDFIO_DEBUG("_pdfioTokenRead: Read '%s'.\n", buffer);

View File

@ -1,17 +1,20 @@
//
// PDF value functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
#include "pdfio-private.h"
//
// Include necessary headers...
// Local functions...
//
#include "pdfio-private.h"
static time_t get_date_time(const char *s);
//
@ -109,6 +112,101 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file
}
//
// '_pdfioValueDecrypt()' - Decrypt a value.
//
bool // O - `true` on success, `false` on error
_pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object
_pdfio_value_t *v, // I - Value
size_t depth)// I - Depth
{
_pdfio_crypto_ctx_t ctx; // Decryption context
_pdfio_crypto_cb_t cb; // Decryption callback
size_t ivlen; // Number of initialization vector bytes
uint8_t temp[32768]; // Temporary buffer for decryption
size_t templen; // Number of actual data bytes
time_t timeval; // Date/time value
if (depth > PDFIO_MAX_DEPTH)
{
_pdfioFileError(pdf, "Value too deep.");
return (false);
}
switch (v->type)
{
default :
// Do nothing
break;
case PDFIO_VALTYPE_ARRAY :
return (_pdfioArrayDecrypt(pdf, obj, v->value.array, depth + 1));
break;
case PDFIO_VALTYPE_DICT :
return (_pdfioDictDecrypt(pdf, obj, v->value.dict, depth + 1));
break;
case PDFIO_VALTYPE_BINARY :
// Decrypt the binary string...
if (v->value.binary.datalen > (sizeof(temp) - 32))
{
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
return (false);
}
ivlen = v->value.binary.datalen;
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL)
return (false);
templen = (cb)(&ctx, temp, v->value.binary.data + ivlen, v->value.binary.datalen - ivlen);
// Copy the decrypted string back to the value and adjust the length...
memcpy(v->value.binary.data, temp, templen);
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128)
v->value.binary.datalen = templen - temp[templen - 1];
else
v->value.binary.datalen = templen;
break;
case PDFIO_VALTYPE_STRING :
// Decrypt regular string...
templen = strlen(v->value.string);
if (templen > (sizeof(temp) - 33))
{
_pdfioFileError(pdf, "Unable to read encrypted string - too long.");
return (false);
}
ivlen = templen;
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, (uint8_t *)v->value.string, &ivlen)) == NULL)
return (false);
templen = (cb)(&ctx, temp, (uint8_t *)v->value.string + ivlen, templen - ivlen);
temp[templen] = '\0';
if ((timeval = get_date_time((char *)temp)) != 0)
{
// Change the type to date...
v->type = PDFIO_VALTYPE_DATE;
v->value.date = timeval;
}
else
{
// Copy the decrypted string back to the value...
v->value.string = pdfioStringCreate(pdf, (char *)temp);
}
break;
}
return (true);
}
//
// '_pdfioValueDebug()' - Print the contents of a value.
//
@ -117,6 +215,9 @@ void
_pdfioValueDebug(_pdfio_value_t *v, // I - Value
FILE *fp) // I - Output file
{
if (!v)
return;
switch (v->type)
{
case PDFIO_VALTYPE_ARRAY :
@ -194,10 +295,13 @@ _pdfioValueDelete(_pdfio_value_t *v) // I - Value
_pdfio_value_t * // O - Value or `NULL` on error/EOF
_pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object, if any
_pdfio_token_t *tb, // I - Token buffer/stack
_pdfio_value_t *v) // I - Value
_pdfio_value_t *v, // I - Value
size_t depth) // I - Depth of value
{
char token[32768]; // Token buffer
time_t timeval; // Date/time value
#ifdef DEBUG
static const char * const valtypes[] =
{
@ -216,7 +320,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
#endif // DEBUG
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, v=%p)\n", pdf, v);
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v);
if (!_pdfioTokenGet(tb, token, sizeof(token)))
return (NULL);
@ -224,81 +328,33 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
if (!strcmp(token, "["))
{
// Start of array
if (depth >= PDFIO_MAX_DEPTH)
{
_pdfioFileError(pdf, "Too many nested arrays.");
return (NULL);
}
v->type = PDFIO_VALTYPE_ARRAY;
if ((v->value.array = _pdfioArrayRead(pdf, tb)) == NULL)
if ((v->value.array = _pdfioArrayRead(pdf, obj, tb, depth + 1)) == NULL)
return (NULL);
}
else if (!strcmp(token, "<<"))
{
// Start of dictionary
if (depth >= PDFIO_MAX_DEPTH)
{
_pdfioFileError(pdf, "Too many nested dictionaries.");
return (NULL);
}
v->type = PDFIO_VALTYPE_DICT;
if ((v->value.dict = _pdfioDictRead(pdf, tb)) == NULL)
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
return (NULL);
}
else if (!strncmp(token, "(D:", 3))
else if (!strncmp(token, "(D:", 3) && (timeval = get_date_time(token + 1)) != 0)
{
// Possible date value of the form:
//
// (D:YYYYMMDDhhmmssZ)
// (D:YYYYMMDDhhmmss+HH'mm)
// (D:YYYYMMDDhhmmss-HH'mm)
//
int i; // Looping var
struct tm dateval; // Date value
int offset; // Date offset
for (i = 3; i < 17; i ++)
{
if (!isdigit(token[i] & 255))
break;
}
if (i >= 17)
{
if (token[i] == 'Z')
{
i ++;
}
else if (token[i] == '-' || token[i] == '+')
{
if (isdigit(token[i + 1] & 255) && isdigit(token[i + 2] & 255) && token[i + 3] == '\'' && isdigit(token[i + 4] & 255) && isdigit(token[i + 5] & 255))
{
i += 6;
if (token[i] == '\'')
i ++;
}
}
}
if (token[i])
{
// Just a string...
v->type = PDFIO_VALTYPE_STRING;
v->value.string = pdfioStringCreate(pdf, token + 1);
}
else
{
// Date value...
dateval.tm_year = (token[3] - '0') * 1000 + (token[4] - '0') * 100 + (token[5] - '0') * 10 + token[6] - '0' - 1900;
dateval.tm_mon = (token[7] - '0') * 10 + token[8] - '0' - 1;
dateval.tm_mday = (token[9] - '0') * 10 + token[10] - '0';
dateval.tm_hour = (token[11] - '0') * 10 + token[12] - '0';
dateval.tm_min = (token[13] - '0') * 10 + token[14] - '0';
dateval.tm_sec = (token[15] - '0') * 10 + token[16] - '0';
if (token[17] == 'Z')
{
offset = 0;
}
else
{
offset = (token[18] - '0') * 600 + (token[19] - '0') * 60 + (token[20] - '0') * 10 + token[21] - '0';
if (token[17] == '-')
offset = -offset;
}
v->type = PDFIO_VALTYPE_DATE;
v->value.date = mktime(&dateval) + offset;
}
v->type = PDFIO_VALTYPE_DATE;
v->value.date = timeval;
}
else if (token[0] == '(')
{
@ -396,6 +452,9 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
tempptr = tb->bufptr;
while (tempptr < tb->bufend && isspace(*tempptr & 255))
tempptr ++; // Skip whitespace as needed...
if (tempptr < tb->bufend && isdigit(*tempptr & 255))
{
// Integer...
@ -408,7 +467,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
}
while (tempptr < tb->bufend && isspace(*tempptr & 255))
tempptr ++;
tempptr ++; // Skip whitespace
if (tempptr < tb->bufend && *tempptr == 'R')
{
@ -441,7 +500,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
// If we get here, we have a number...
v->type = PDFIO_VALTYPE_NUMBER;
v->value.number = (double)strtod(token, NULL);
v->value.number = _pdfio_strtod(pdf, token);
}
else if (!strcmp(token, "true") || !strcmp(token, "false"))
{
@ -472,6 +531,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
bool // O - `true` on success, `false` on failure
_pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object, if any
_pdfio_value_t *v, // I - Value
off_t *length)// O - Offset to /Length value, if any
{
@ -481,23 +541,47 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
return (false);
case PDFIO_VALTYPE_ARRAY :
return (_pdfioArrayWrite(v->value.array));
return (_pdfioArrayWrite(v->value.array, obj));
case PDFIO_VALTYPE_BINARY :
{
size_t i; // Looping var
unsigned char *dataptr; // Pointer into data
size_t databytes; // Bytes to write
uint8_t temp[32768], // Temporary buffer for encryption
*dataptr; // Pointer into data
if (obj && pdf->encryption)
{
// Write encrypted string...
_pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback
size_t ivlen; // Number of initialization vector bytes
if (v->value.binary.datalen > (sizeof(temp) - 32))
{
_pdfioFileError(pdf, "Unable to write encrypted binary string - too long.");
return (false);
}
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
databytes = (cb)(&ctx, temp + ivlen, v->value.binary.data, v->value.binary.datalen) + ivlen;
dataptr = temp;
}
else
{
dataptr = v->value.binary.data;
databytes = v->value.binary.datalen;
}
if (!_pdfioFilePuts(pdf, "<"))
return (false);
for (i = v->value.binary.datalen, dataptr = v->value.binary.data; i > 1; i -= 2, dataptr += 2)
for (; databytes > 1; databytes -= 2, dataptr += 2)
{
if (!_pdfioFilePrintf(pdf, "%02X%02X", dataptr[0], dataptr[1]))
return (false);
}
if (i > 0)
if (databytes > 0)
return (_pdfioFilePrintf(pdf, "%02X>", dataptr[0]));
else
return (_pdfioFilePuts(pdf, ">"));
@ -512,6 +596,7 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
case PDFIO_VALTYPE_DATE :
{
struct tm date; // Date values
char datestr[32]; // Formatted date value
#ifdef _WIN32
gmtime_s(&date, &v->value.date);
@ -519,69 +604,182 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
gmtime_r(&v->value.date, &date);
#endif // _WIN32
return (_pdfioFilePrintf(pdf, "(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)
{
// Write encrypted string...
uint8_t temp[32768], // Encrypted bytes
*tempptr; // Pointer into encrypted bytes
_pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback
size_t len = strlen(datestr),
// Length of value
ivlen, // Number of initialization vector bytes
tempbytes; // Number of output bytes
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)datestr, len) + ivlen;
if (!_pdfioFilePuts(pdf, "<"))
return (false);
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
{
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1]))
return (false);
}
if (tempbytes > 0)
return (_pdfioFilePrintf(pdf, "%02X>", *tempptr));
else
return (_pdfioFilePuts(pdf, ">"));
}
else
{
return (_pdfioFilePrintf(pdf, "%S", datestr));
}
}
case PDFIO_VALTYPE_DICT :
return (_pdfioDictWrite(v->value.dict, length));
return (_pdfioDictWrite(v->value.dict, obj, length));
case PDFIO_VALTYPE_INDIRECT :
return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation));
case PDFIO_VALTYPE_NAME :
return (_pdfioFilePrintf(pdf, "/%s", v->value.name));
return (_pdfioFilePrintf(pdf, "%N", v->value.name));
case PDFIO_VALTYPE_NULL :
return (_pdfioFilePuts(pdf, " null"));
case PDFIO_VALTYPE_NUMBER :
return (_pdfioFilePrintf(pdf, " %g", v->value.number));
return (_pdfioFilePrintf(pdf, " %.6f", v->value.number));
case PDFIO_VALTYPE_STRING :
if (obj && pdf->encryption)
{
const char *start, // Start of fragment
*end; // End of fragment
// Write encrypted string...
uint8_t temp[32768], // Encrypted bytes
*tempptr; // Pointer into encrypted bytes
_pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback
size_t len = strlen(v->value.string),
// Length of value
ivlen, // Number of initialization vector bytes
tempbytes; // Number of output bytes
if (!_pdfioFilePuts(pdf, "("))
return (false);
// Write a quoted string value...
for (start = v->value.string; *start; start = end)
if (len > (sizeof(temp) - 32))
{
// Find the next character that needs to be quoted...
for (end = start; *end; end ++)
{
if (*end == '\\' || *end == ')' || (*end & 255) < ' ')
break;
}
if (end > start)
{
// Write unquoted (safe) characters...
if (!_pdfioFileWrite(pdf, start, (size_t)(end - start)))
return (false);
}
if (*end)
{
// Quote this character...
bool success; // Did the write work?
if (*end == '\\' || *end == ')')
success = _pdfioFilePrintf(pdf, "\\%c", *end);
else
success = _pdfioFilePrintf(pdf, "\\%03o", *end);
if (!success)
return (false);
end ++;
}
_pdfioFileError(pdf, "Unable to write encrypted string - too long.");
return (false);
}
return (_pdfioFilePuts(pdf, ")"));
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)v->value.string, len) + ivlen;
if (!_pdfioFilePuts(pdf, "<"))
return (false);
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
{
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1]))
return (false);
}
if (tempbytes > 0)
return (_pdfioFilePrintf(pdf, "%02X>", *tempptr));
else
return (_pdfioFilePuts(pdf, ">"));
}
else
{
// Write unencrypted string...
return (_pdfioFilePrintf(pdf, "%S", v->value.string));
}
}
return (false);
}
//
// 'get_date_time()' - Convert PDF date/time value to time_t.
//
static time_t // O - Time in seconds
get_date_time(const char *s) // I - PDF date/time value
{
int i; // Looping var
struct tm dateval; // Date value
int offset; // Date offset
PDFIO_DEBUG("get_date_time(s=\"%s\")\n", s);
// Possible date value of the form:
//
// (D:YYYYMMDDhhmmssZ)
// (D:YYYYMMDDhhmmss+HH'mm)
// (D:YYYYMMDDhhmmss-HH'mm)
//
for (i = 2; i < 16; i ++)
{
if (!isdigit(s[i] & 255) || !s[i])
break;
}
if (i >= 16)
{
if (s[i] == 'Z')
{
// UTC...
i ++;
}
else if (s[i] == '-' || s[i] == '+')
{
// Timezone offset from UTC...
if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255))
{
i += 6;
if (s[i] == '\'')
i ++;
}
}
else if (!s[i])
{
// Missing zone info, invalid date string...
return (0);
}
}
if (s[i])
{
// Just a string...
return (0);
}
// Date value...
memset(&dateval, 0, sizeof(dateval));
dateval.tm_year = (s[2] - '0') * 1000 + (s[3] - '0') * 100 + (s[4] - '0') * 10 + s[5] - '0' - 1900;
dateval.tm_mon = (s[6] - '0') * 10 + s[7] - '0' - 1;
dateval.tm_mday = (s[8] - '0') * 10 + s[9] - '0';
dateval.tm_hour = (s[10] - '0') * 10 + s[11] - '0';
dateval.tm_min = (s[12] - '0') * 10 + s[13] - '0';
dateval.tm_sec = (s[14] - '0') * 10 + s[15] - '0';
if (s[16] == 'Z')
{
offset = 0;
}
else
{
offset = (s[17] - '0') * 600 + (s[18] - '0') * 60 + (s[19] - '0') * 10 + s[20] - '0';
if (s[16] == '-')
offset = -offset;
}
return (mktime(&dateval) + offset);
}

82
pdfio.h
View File

@ -1,7 +1,7 @@
//
// Public header file for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -9,37 +9,35 @@
#ifndef PDFIO_H
# define PDFIO_H
//
// Include necessary headers...
//
# include <stdio.h>
# include <stdlib.h>
# include <stdbool.h>
# include <sys/types.h>
# include <time.h>
//
// C++ magic...
//
# ifdef __cplusplus
extern "C" {
# endif // __cplusplus
//
// Version numbers...
//
# define PDFIO_VERSION "1.5.1"
# define PDFIO_VERSION_MAJOR 1
# define PDFIO_VERSION_MINOR 5
//
// Visibility and other annotations...
//
# if defined(__has_extension) || defined(__GNUC__)
# define _PDFIO_PUBLIC __attribute__ ((visibility("default")))
# define _PDFIO_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b)))
# define _PDFIO_DEPRECATED __attribute__ ((deprecated)) _PDFIO_PUBLIC
# else
# define _PDFIO_PUBLIC
# define _PDFIO_FORMAT(a,b)
# define _PDFIO_DEPRECATED
# endif // __has_extension || __GNUC__
@ -55,10 +53,20 @@ typedef struct _pdfio_array_s pdfio_array_t;
// Array of PDF values
typedef struct _pdfio_dict_s pdfio_dict_t;
// Key/value dictionary
typedef bool (*pdfio_dict_cb_t)(pdfio_dict_t *dict, const char *key, void *cb_data);
// Dictionary iterator callback
typedef struct _pdfio_file_s pdfio_file_t;
// PDF file
typedef bool (*pdfio_error_cb_t)(pdfio_file_t *pdf, const char *message, void *data);
// Error callback
typedef enum pdfio_encryption_e // PDF encryption modes
{
PDFIO_ENCRYPTION_NONE = 0, // No encryption
PDFIO_ENCRYPTION_RC4_40, // 40-bit RC4 encryption (PDF 1.3)
PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4)
PDFIO_ENCRYPTION_AES_128, // 128-bit AES encryption (PDF 1.6)
PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0) @exclude all@
} pdfio_encryption_t;
typedef enum pdfio_filter_e // Compression/decompression filters for streams
{
PDFIO_FILTER_NONE, // No filter
@ -74,6 +82,24 @@ typedef enum pdfio_filter_e // Compression/decompression filters for streams
PDFIO_FILTER_RUNLENGTH, // RunLengthDecode filter (reading only)
} pdfio_filter_t;
typedef struct _pdfio_obj_s pdfio_obj_t;// Numbered object in PDF file
typedef ssize_t (*pdfio_output_cb_t)(void *ctx, const void *data, size_t datalen);
// Output callback for pdfioFileCreateOutput
typedef const char *(*pdfio_password_cb_t)(void *data, const char *filename);
// Password callback for pdfioFileOpen
enum pdfio_permission_e // PDF permission bits
{
PDFIO_PERMISSION_NONE = 0, // No permissions
PDFIO_PERMISSION_PRINT = 0x0004, // PDF allows printing
PDFIO_PERMISSION_MODIFY = 0x0008, // PDF allows modification
PDFIO_PERMISSION_COPY = 0x0010, // PDF allows copying
PDFIO_PERMISSION_ANNOTATE = 0x0020, // PDF allows annotation
PDFIO_PERMISSION_FORMS = 0x0100, // PDF allows filling in forms
PDFIO_PERMISSION_READING = 0x0200, // PDF allows screen reading/accessibility (deprecated in PDF 2.0)
PDFIO_PERMISSION_ASSEMBLE = 0x0400, // PDF allows assembly (insert, delete, or rotate pages, add document outlines and thumbnails)
PDFIO_PERMISSION_PRINT_HIGH = 0x0800, // PDF allows high quality printing
PDFIO_PERMISSION_ALL = ~0 // All permissions
};
typedef int pdfio_permission_t; // PDF permission bitfield
typedef struct pdfio_rect_s // PDF rectangle
{
double x1; // Lower-left X coordinate
@ -125,7 +151,9 @@ extern pdfio_obj_t *pdfioArrayGetObj(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
extern size_t pdfioArrayGetSize(pdfio_array_t *a) _PDFIO_PUBLIC;
extern const char *pdfioArrayGetString(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
extern pdfio_valtype_t pdfioArrayGetType(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
extern bool pdfioArrayRemove(pdfio_array_t *a, size_t n) _PDFIO_PUBLIC;
extern bool pdfioDictClear(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioDictCopy(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioDictCreate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_array_t *pdfioDictGetArray(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
@ -133,12 +161,15 @@ extern unsigned char *pdfioDictGetBinary(pdfio_dict_t *dict, const char *key, si
extern bool pdfioDictGetBoolean(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern time_t pdfioDictGetDate(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioDictGetDict(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern const char *pdfioDictGetKey(pdfio_dict_t *dict, size_t n) _PDFIO_PUBLIC;
extern const char *pdfioDictGetName(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern size_t pdfioDictGetNumPairs(pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern double pdfioDictGetNumber(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioDictGetObj(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern pdfio_rect_t *pdfioDictGetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *rect) _PDFIO_PUBLIC;
extern const char *pdfioDictGetString(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern pdfio_valtype_t pdfioDictGetType(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern void pdfioDictIterateKeys(pdfio_dict_t *dict, pdfio_dict_cb_t cb, void *cb_data) _PDFIO_PUBLIC;
extern bool pdfioDictSetArray(pdfio_dict_t *dict, const char *key, pdfio_array_t *value) _PDFIO_PUBLIC;
extern bool pdfioDictSetBinary(pdfio_dict_t *dict, const char *key, const unsigned char *value, size_t valuelen) _PDFIO_PUBLIC;
extern bool pdfioDictSetBoolean(pdfio_dict_t *dict, const char *key, bool value) _PDFIO_PUBLIC;
@ -150,34 +181,44 @@ extern bool pdfioDictSetNumber(pdfio_dict_t *dict, const char *key, double valu
extern bool pdfioDictSetObj(pdfio_dict_t *dict, const char *key, pdfio_obj_t *value) _PDFIO_PUBLIC;
extern bool pdfioDictSetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *value) _PDFIO_PUBLIC;
extern bool pdfioDictSetString(pdfio_dict_t *dict, const char *key, const char *value) _PDFIO_PUBLIC;
extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4);
extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char *format, ...) _PDFIO_PUBLIC;
extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateArrayObj(pdfio_file_t *pdf, pdfio_array_t *array) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateNameObj(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateNumberObj(pdfio_file_t *pdf, double number) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreateOutput(pdfio_output_cb_t output_cb, void *output_ctx, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
// TODO: Add number, array, string, etc. versions of pdfioFileCreateObject?
extern pdfio_stream_t *pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateStringObj(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreateTemporary(char *buffer, size_t bufsize, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC;
extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioFileGetCatalog(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetKeywords(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 size_t pdfioFileGetNumObjs(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern size_t pdfioFileGetNumPages(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileGetObj(pdfio_file_t *pdf, size_t n) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileGetPage(pdfio_file_t *pdf, size_t n) _PDFIO_PUBLIC;
extern pdfio_permission_t pdfioFileGetPermissions(pdfio_file_t *pdf, pdfio_encryption_t *encryption) _PDFIO_PUBLIC;
extern const char *pdfioFileGetProducer(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetSubject(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetTitle(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetVersion(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileOpen(const char *filename, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileOpen(const char *filename, pdfio_password_cb_t password_cb, void *password_data, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern void pdfioFileSetAuthor(pdfio_file_t *pdf, const char *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 pdfioFileSetKeywords(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetModificationDate(pdfio_file_t *pdf, time_t value) _PDFIO_PUBLIC;
extern bool pdfioFileSetPermissions(pdfio_file_t *pdf, pdfio_permission_t permissions, pdfio_encryption_t encryption, const char *owner_password, const char *user_password) _PDFIO_PUBLIC;
extern void pdfioFileSetSubject(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetTitle(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
@ -188,6 +229,7 @@ extern pdfio_array_t *pdfioObjGetArray(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioObjGetDict(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern unsigned short pdfioObjGetGeneration(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern size_t pdfioObjGetLength(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern const char *pdfioObjGetName(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern size_t pdfioObjGetNumber(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern const char *pdfioObjGetSubtype(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern const char *pdfioObjGetType(pdfio_obj_t *obj) _PDFIO_PUBLIC;
@ -201,20 +243,16 @@ extern bool pdfioStreamClose(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioStreamConsume(pdfio_stream_t *st, size_t bytes) _PDFIO_PUBLIC;
extern bool pdfioStreamGetToken(pdfio_stream_t *st, char *buffer, size_t bufsize) _PDFIO_PUBLIC;
extern ssize_t pdfioStreamPeek(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC;
extern bool pdfioStreamPrintf(pdfio_stream_t *st, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(2,3);
extern bool pdfioStreamPrintf(pdfio_stream_t *st, const char *format, ...) _PDFIO_PUBLIC;
extern bool pdfioStreamPutChar(pdfio_stream_t *st, int ch) _PDFIO_PUBLIC;
extern bool pdfioStreamPuts(pdfio_stream_t *st, const char *s) _PDFIO_PUBLIC;
extern ssize_t pdfioStreamRead(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC;
extern bool pdfioStreamWrite(pdfio_stream_t *st, const void *buffer, size_t bytes) _PDFIO_PUBLIC;
extern char *pdfioStringCreate(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC;
extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_PUBLIC;
extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_PUBLIC;
//
// C++ magic...
//
# ifdef __cplusplus
}
# endif // __cplusplus

View File

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

View File

@ -87,7 +87,7 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
@ -101,7 +101,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
@ -115,7 +115,7 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HAVE_LIBPNG;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
@ -130,7 +130,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HAVE_LIBPNG;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
@ -148,13 +148,18 @@
<ClInclude Include="ttf.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pdfio-aes.c" />
<ClCompile Include="pdfio-array.c" />
<ClCompile Include="pdfio-common.c" />
<ClCompile Include="pdfio-content.c" />
<ClCompile Include="pdfio-crypto.c" />
<ClCompile Include="pdfio-dict.c" />
<ClCompile Include="pdfio-file.c" />
<ClCompile Include="pdfio-md5.c" />
<ClCompile Include="pdfio-object.c" />
<ClCompile Include="pdfio-page.c" />
<ClCompile Include="pdfio-rc4.c" />
<ClCompile Include="pdfio-sha256.c" />
<ClCompile Include="pdfio-stream.c" />
<ClCompile Include="pdfio-string.c" />
<ClCompile Include="pdfio-token.c" />
@ -167,6 +172,8 @@
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="packages\libpng_native.redist.1.6.30\build\native\libpng_native.redist.targets" Condition="Exists('packages\libpng_native.redist.1.6.30\build\native\libpng_native.redist.targets')" />
<Import Project="packages\libpng_native.1.6.30\build\native\libpng_native.targets" Condition="Exists('packages\libpng_native.1.6.30\build\native\libpng_native.targets')" />
<Import Project="packages\zlib_native.redist.1.2.11\build\native\zlib_native.redist.targets" Condition="Exists('packages\zlib_native.redist.1.2.11\build\native\zlib_native.redist.targets')" />
<Import Project="packages\zlib_native.1.2.11\build\native\zlib_native.targets" Condition="Exists('packages\zlib_native.1.2.11\build\native\zlib_native.targets')" />
</ImportGroup>

View File

@ -65,6 +65,21 @@
<ClCompile Include="ttf.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pdfio-aes.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pdfio-crypto.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pdfio-md5.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pdfio-rc4.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pdfio-sha256.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@ -25,7 +25,13 @@
279E1035267D043B00D3A349 /* ttf.h in Headers */ = {isa = PBXBuildFile; fileRef = 279E1033267D043B00D3A349 /* ttf.h */; };
279E1036267D043B00D3A349 /* ttf.c in Sources */ = {isa = PBXBuildFile; fileRef = 279E1034267D043B00D3A349 /* ttf.c */; };
279E103B267D04E600D3A349 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 279E103A267D04E600D3A349 /* libz.tbd */; };
27CF90442711DFFE00E50FE4 /* pdfio-aes.c in Sources */ = {isa = PBXBuildFile; fileRef = 27CF90432711DFFE00E50FE4 /* pdfio-aes.c */; };
27ECBD8926419DAB0025312A /* libpdfio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 273440B0263D6FE200FBFD63 /* libpdfio.a */; };
27F2F0602710BE92008ECD36 /* pdfio-md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F05D2710BE92008ECD36 /* pdfio-md5.c */; };
27F2F0612710BE92008ECD36 /* pdfio-rc4.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F05E2710BE92008ECD36 /* pdfio-rc4.c */; };
27F2F0622710BE92008ECD36 /* pdfio-crypto.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */; };
27F2F0642711243D008ECD36 /* pdfio-sha256.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F0632711243D008ECD36 /* pdfio-sha256.c */; };
27FCBDE42D19F9B300485EEE /* pdfio-base-font-widths.h in Headers */ = {isa = PBXBuildFile; fileRef = 27FCBDE32D19F9B300485EEE /* pdfio-base-font-widths.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -79,6 +85,12 @@
279E1033267D043B00D3A349 /* ttf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ttf.h; sourceTree = "<group>"; };
279E1034267D043B00D3A349 /* ttf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ttf.c; sourceTree = "<group>"; };
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>"; };
27F2F05D2710BE92008ECD36 /* pdfio-md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-md5.c"; sourceTree = "<group>"; };
27F2F05E2710BE92008ECD36 /* pdfio-rc4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-rc4.c"; sourceTree = "<group>"; };
27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-crypto.c"; sourceTree = "<group>"; };
27F2F0632711243D008ECD36 /* pdfio-sha256.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-sha256.c"; sourceTree = "<group>"; };
27FCBDE32D19F9B300485EEE /* pdfio-base-font-widths.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "pdfio-base-font-widths.h"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -150,19 +162,25 @@
279E1038267D045C00D3A349 /* Library */ = {
isa = PBXGroup;
children = (
27CF90432711DFFE00E50FE4 /* pdfio-aes.c */,
273440BA263D727800FBFD63 /* pdfio-array.c */,
27FCBDE32D19F9B300485EEE /* pdfio-base-font-widths.h */,
273440BB263D727800FBFD63 /* pdfio-common.c */,
271EA703265B2B1000ACDD39 /* pdfio-content.c */,
27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */,
273440BE263D727800FBFD63 /* pdfio-dict.c */,
273440BD263D727800FBFD63 /* pdfio-file.c */,
27F2F05D2710BE92008ECD36 /* pdfio-md5.c */,
273440BC263D727800FBFD63 /* pdfio-object.c */,
273440C2263D727800FBFD63 /* pdfio-page.c */,
27F2F05E2710BE92008ECD36 /* pdfio-rc4.c */,
27F2F0632711243D008ECD36 /* pdfio-sha256.c */,
273440BF263D727800FBFD63 /* pdfio-stream.c */,
273440B9263D727800FBFD63 /* pdfio-string.c */,
273440E3263DD7EA00FBFD63 /* pdfio-token.c */,
273440C0263D727800FBFD63 /* pdfio-value.c */,
279E1033267D043B00D3A349 /* ttf.h */,
279E1034267D043B00D3A349 /* ttf.c */,
279E1033267D043B00D3A349 /* ttf.h */,
);
name = Library;
sourceTree = "<group>";
@ -190,6 +208,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
27FCBDE42D19F9B300485EEE /* pdfio-base-font-widths.h in Headers */,
273440CC263D727800FBFD63 /* pdfio.h in Headers */,
271EA706265B2B1000ACDD39 /* pdfio-content.h in Headers */,
273440C3263D727800FBFD63 /* pdfio-private.h in Headers */,
@ -241,7 +260,8 @@
273440A8263D6FE200FBFD63 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1250;
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1600;
TargetAttributes = {
273440AF263D6FE200FBFD63 = {
CreatedOnToolsVersion = 12.5;
@ -281,11 +301,16 @@
273440CB263D727800FBFD63 /* pdfio-value.c in Sources */,
273440CA263D727800FBFD63 /* pdfio-stream.c in Sources */,
273440CD263D727800FBFD63 /* pdfio-page.c in Sources */,
27F2F0622710BE92008ECD36 /* pdfio-crypto.c in Sources */,
27F2F0642711243D008ECD36 /* pdfio-sha256.c in Sources */,
273440C5263D727800FBFD63 /* pdfio-array.c in Sources */,
273440E4263DD7EA00FBFD63 /* pdfio-token.c in Sources */,
273440C7263D727800FBFD63 /* pdfio-object.c in Sources */,
27F2F0602710BE92008ECD36 /* pdfio-md5.c in Sources */,
273440C4263D727800FBFD63 /* pdfio-string.c in Sources */,
27CF90442711DFFE00E50FE4 /* pdfio-aes.c in Sources */,
271EA705265B2B1000ACDD39 /* pdfio-content.c in Sources */,
27F2F0612710BE92008ECD36 /* pdfio-rc4.c in Sources */,
273440C6263D727800FBFD63 /* pdfio-common.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -350,18 +375,19 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.0;
CURRENT_PROJECT_VERSION = 1.1.2;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=1",
"'PDFIO_VERSION=\"$(CURRENT_PROJECT_VERSION)\"'",
);
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -377,7 +403,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@ -428,19 +454,18 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.0;
CURRENT_PROJECT_VERSION = 1.1.2;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"'PDFIO_VERSION=\"$(CURRENT_PROJECT_VERSION)\"'",
);
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
@ -455,7 +480,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
RUN_CLANG_STATIC_ANALYZER = YES;
@ -468,6 +493,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = RU58A2256H;
EXECUTABLE_PREFIX = lib;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -479,6 +505,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = RU58A2256H;
EXECUTABLE_PREFIX = lib;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -491,11 +518,12 @@
buildSettings = {
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpdfio;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -507,9 +535,10 @@
buildSettings = {
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpdfio;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

@ -1,6 +1,17 @@
LIBRARY pdfio1
VERSION 1.0
VERSION 1.5
EXPORTS
_pdfioCryptoAESDecrypt
_pdfioCryptoAESEncrypt
_pdfioCryptoAESInit
_pdfioCryptoMD5Append
_pdfioCryptoMD5Finish
_pdfioCryptoMD5Init
_pdfioCryptoRC4Crypt
_pdfioCryptoRC4Init
_pdfioCryptoSHA256Append
_pdfioCryptoSHA256Finish
_pdfioCryptoSHA256Init
_pdfioTokenInit
_pdfioValueDebug
_pdfioValueRead
@ -31,6 +42,7 @@ pdfioArrayGetObj
pdfioArrayGetSize
pdfioArrayGetString
pdfioArrayGetType
pdfioArrayRemove
pdfioContentClip
pdfioContentDrawImage
pdfioContentFill
@ -43,6 +55,7 @@ pdfioContentPathClose
pdfioContentPathCurve
pdfioContentPathCurve13
pdfioContentPathCurve23
pdfioContentPathEnd
pdfioContentPathLineTo
pdfioContentPathMoveTo
pdfioContentPathRect
@ -77,12 +90,17 @@ pdfioContentSetTextXScaling
pdfioContentStroke
pdfioContentTextBegin
pdfioContentTextEnd
pdfioContentTextMeasure
pdfioContentTextMoveLine
pdfioContentTextMoveTo
pdfioContentTextNewLine
pdfioContentTextNewLineShow
pdfioContentTextNewLineShowf
pdfioContentTextNextLine
pdfioContentTextShow
pdfioContentTextShowJustified
pdfioContentTextShowf
pdfioDictClear
pdfioDictCopy
pdfioDictCreate
pdfioDictGetArray
@ -90,12 +108,15 @@ pdfioDictGetBinary
pdfioDictGetBoolean
pdfioDictGetDate
pdfioDictGetDict
pdfioDictGetKey
pdfioDictGetName
pdfioDictGetNumPairs
pdfioDictGetNumber
pdfioDictGetObj
pdfioDictGetRect
pdfioDictGetString
pdfioDictGetType
pdfioDictIterateKeys
pdfioDictSetArray
pdfioDictSetBinary
pdfioDictSetBoolean
@ -113,22 +134,31 @@ pdfioFileCreate
pdfioFileCreateArrayObj
pdfioFileCreateFontObjFromBase
pdfioFileCreateFontObjFromFile
pdfioFileCreateICCObjFromData
pdfioFileCreateICCObjFromFile
pdfioFileCreateImageObjFromData
pdfioFileCreateImageObjFromFile
pdfioFileCreateNameObj
pdfioFileCreateNumberObj
pdfioFileCreateObj
pdfioFileCreateOutput
pdfioFileCreatePage
pdfioFileCreateStringObj
pdfioFileCreateTemporary
pdfioFileFindObj
pdfioFileGetAuthor
pdfioFileGetCatalog
pdfioFileGetCreationDate
pdfioFileGetCreator
pdfioFileGetID
pdfioFileGetKeywords
pdfioFileGetModificationDate
pdfioFileGetName
pdfioFileGetNumObjs
pdfioFileGetNumPages
pdfioFileGetObj
pdfioFileGetPage
pdfioFileGetPermissions
pdfioFileGetProducer
pdfioFileGetSubject
pdfioFileGetTitle
@ -138,6 +168,8 @@ pdfioFileSetAuthor
pdfioFileSetCreationDate
pdfioFileSetCreator
pdfioFileSetKeywords
pdfioFileSetModificationDate
pdfioFileSetPermissions
pdfioFileSetSubject
pdfioFileSetTitle
pdfioImageGetBytesPerLine
@ -150,6 +182,7 @@ pdfioObjGetArray
pdfioObjGetDict
pdfioObjGetGeneration
pdfioObjGetLength
pdfioObjGetName
pdfioObjGetNumber
pdfioObjGetSubtype
pdfioObjGetType
@ -158,6 +191,8 @@ pdfioPageCopy
pdfioPageDictAddColorSpace
pdfioPageDictAddFont
pdfioPageDictAddImage
pdfioPageGetNumStreams
pdfioPageOpenStream
pdfioStreamClose
pdfioStreamConsume
pdfioStreamGetToken

View File

@ -3,7 +3,7 @@
<metadata>
<id>pdfio_native</id>
<title>PDFio Library for VS2019+</title>
<version>1.0.0-b2</version>
<version>1.5.1</version>
<authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
@ -12,9 +12,14 @@
<readme>build/native/README.md</readme>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>PDFio Library for VS2019+</description>
<summary>PDFio is a simple C library for reading and writing PDF files. PDFio is licensed under the Apache License Version 2.0 with an exception to allow linking against GNU GPL2-only software.</summary>
<copyright>Copyright © 2019-2021 by Michael R Sweet</copyright>
<summary>PDFio is a simple C library for reading and writing PDF files. PDFio is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GNU GPL2-only software.</summary>
<copyright>Copyright © 2019-2025 by Michael R Sweet</copyright>
<tags>pdf file native</tags>
<dependencies>
<dependency id="pdfio_native.redist" version="1.5.1" />
<dependency id="libpng_native.redist" version="1.6.30" />
<dependency id="zlib_native.redist" version="1.2.11" />
</dependencies>
</metadata>
<files>
<file src="doc\pdfio-128.png" target="build\native" />
@ -22,7 +27,7 @@
<file src="pdfio_native.props" target="build\native" />
<file src="pdfio.h" target="build\native\include" />
<file src="pdfio-content.h" target="build\native\include" />
<!--<file src="Win32\**\pdfio*.lib" target="build\native\lib\Win32" />-->
<file src="x64\**\pdfio.lib" target="build\native\lib\x64" />
<!--<file src="Win32\**\pdfio1.lib" target="build\native\lib\Win32" />-->
<file src="x64\**\pdfio1.lib" target="build\native\lib\x64" />
</files>
</package>

View File

@ -5,7 +5,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>$(MSBuildThisFileDirectory)\lib\$(Platform)\$(Configuration)\pdfio.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>$(MSBuildThisFileDirectory)\lib\$(Platform)\$(Configuration)\pdfio1.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
</Project>

View File

@ -0,0 +1,29 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>pdfio_native.redist</id>
<title>PDFio Library for VS2019+</title>
<version>1.5.1</version>
<authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
<license type="expression">Apache-2.0</license>
<icon>build/native/pdfio-128.png</icon>
<readme>build/native/README.md</readme>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>PDFio Library for VS2019+</description>
<summary>PDFio is a simple C library for reading and writing PDF files. This package provides the redistributable content for the PDFio library. PDFio is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GNU GPL2-only software.</summary>
<copyright>Copyright © 2019-2025 by Michael R Sweet</copyright>
<tags>pdf file native</tags>
<dependencies>
<dependency id="libpng_native.redist" version="1.6.30" />
<dependency id="zlib_native.redist" version="1.2.11" />
</dependencies>
</metadata>
<files>
<file src="doc\pdfio-128.png" target="build\native" />
<file src="README.md" target="build\native" />
<!--<file src="Win32\**\pdfio1.dll" target="build\native\bin\Win32" />-->
<file src="x64\**\pdfio1.dll" target="build\native\bin\x64" />
</files>
</package>

18
runtests.bat Normal file
View File

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

View File

@ -1,3 +1,7 @@
https://www.color.org/chardata/rgb/rommrgb.xalter
Copyright © 2006 Hewlett-Packard
Terms of use
This profile is made available by ICC, and may be copied, distributed, embedded, made, used, and sold without restriction. Altered versions of this profile shall have the original identification and copyright information removed and shall not be misrepresented as the original profile.

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