219 Commits

Author SHA1 Message Date
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
66 changed files with 23911 additions and 1599 deletions

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

50
.github/workflows/codeql.yml vendored Normal file
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
- 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 }}"

View File

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

15
.gitignore vendored
View File

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

View File

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

View File

@ -2,14 +2,148 @@ Changes in PDFio
================ ================
v1.0rc1 (Month DD, YYYY) v1.4.0 - YYYY-MM-DD
------------------------ -------------------
- 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. - Fixed a few stack/buffer overflow bugs discovered via fuzzing.
v1.0b2 (November 7, 2021) v1.0b2 - 2021-11-07
------------------------- -------------------
- Added `pdfioFileCreateOutput` API to support streaming output of PDF - Added `pdfioFileCreateOutput` API to support streaming output of PDF
(Issue #21) (Issue #21)
@ -20,7 +154,7 @@ v1.0b2 (November 7, 2021)
- Fixed some issues identified by a Coverity scan. - Fixed some issues identified by a Coverity scan.
v1.0b1 (August 30, 2021) v1.0b1 - 2021-08-30
------------------------ -------------------
- Initial release - Initial release

View File

@ -43,14 +43,17 @@ Contents
Build System Build System
------------ ------------
The build system uses a simple POSIX makefile to build a static or shared The build system uses [GNU autoconf][AUTOCONF] to create a simple POSIX makefile
library. To improve portability, makefiles *must not* make use of features to build static and/or shared libraries. To improve portability, makefiles
unique to GNU make. See the [Makefile Guidelines](#makefile-guidelines) section *must not* make use of features unique to GNU make. See the
for a description of the allowed make features and makefile guidelines. [Makefile Guidelines](#makefile-guidelines) section for a description of the
allowed make features and makefile guidelines.
An Xcode project is provided for macOS/iOS developers, and a Visual Studio An Xcode project is provided for macOS/iOS developers, and a Visual Studio
solution and projects for Windows developers. solution and projects for Windows developers.
[AUTOCONF]: https://www.gnu.org/software/autoconf/
Version Numbering Version Numbering
----------------- -----------------
@ -118,7 +121,7 @@ the source file and the copyright and licensing notice:
// //
// Description of file contents. // Description of file contents.
// //
// Copyright YYYY by AUTHOR. // Copyright © YYYY by AUTHOR.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -330,7 +333,7 @@ typedef, for example:
All constant names are uppercase with underscores between words, e.g., All constant names are uppercase with underscores between words, e.g.,
`PDFIO_THIS_CONSTANT`, `PDFIO_THAT_CONSTANT`, etc. Constants begin with the `PDFIO_THIS_CONSTANT`, `PDFIO_THAT_CONSTANT`, etc. Constants begin with the
"PDFio\_" prefix to avoid conflicts with system constants. Private constants "PDFIO\_" prefix to avoid conflicts with system constants. Private constants
start with an underscore, e.g., `_PDFIO_THIS_CONSTANT`, start with an underscore, e.g., `_PDFIO_THIS_CONSTANT`,
`_PDFIO_THAT_CONSTANT`, etc. `_PDFIO_THAT_CONSTANT`, etc.
@ -369,17 +372,19 @@ extensions MUST NOT be used.
The following variables are defined in the makefile: The following variables are defined in the makefile:
- `AR`; the static library archiver command, - `AR`; the static library archiver command,
- `ARFLAGS`; options for the static library archiver command, - `ARFLAGS`; options for the static library archiver,
- `CC`; the C compiler command, - `CC`; the C compiler command,
- `CFLAGS`; options for the C compiler command, - `CFLAGS`; options for the C compiler,
- `CODESIGN_IDENTITY`: the code signing identity, - `CODESIGN_IDENTITY`: the code signing identity,
- `COMMONFLAGS`; common compiler optimization options, - `CPPFLAGS`; options for the C preprocessor,
- `DESTDIR`/`DSTROOT`: the destination root directory when installing. - `DESTDIR`/`DSTROOT`: the destination root directory when installing.
- `DSO`; the shared library building command, - `DSO`; the shared library building command,
- `DSOFLAGS`; options for the shared library building command, - `DSOFLAGS`; options for the shared library building command,
- `DSONAME`: the root name of the shared library
- `LDFLAGS`; options for the linker, - `LDFLAGS`; options for the linker,
- `LIBPDFIO`: the name of the primary (shared or static) library
- `LIBPDFIO_STATIC`: the name of the secondary (static) library
- `LIBS`; libraries for all programs, - `LIBS`; libraries for all programs,
- `OPTIM`; common compiler optimization options,
- `prefix`; the installation prefix directory, - `prefix`; the installation prefix directory,
- `RANLIB`; the static library indexing command, - `RANLIB`; the static library indexing command,
- `SHELL`; the sh (POSIX shell) command, - `SHELL`; the sh (POSIX shell) command,
@ -388,11 +393,7 @@ The following variables are defined in the makefile:
The following standard targets are defined in the makefile: The following standard targets are defined in the makefile:
- `all`; creates the static library and unit test program. - `all`; creates the static library and unit test program.
- `all-shared`; creates a shared library appropriate for the local system.
- `clean`; removes all target programs libraries, documentation files, and - `clean`; removes all target programs libraries, documentation files, and
object files, object files,
- `debug`: creates a clean build of the static library and unit test program
with debug printfs and the clang address sanitizer enabled.
- `install`; installs all distribution files in their corresponding locations. - `install`; installs all distribution files in their corresponding locations.
- `install-shared`; same as `install` but also installs the shared library.
- `test`; runs the unit test program, building it as needed. - `test`; runs the unit test program, building it as needed.

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.

199
Makefile
View File

@ -1,199 +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
#COMMONFLAGS = -O0 -g -fsanitize=address
CPPFLAGS = '-DPDFIO_VERSION="$(VERSION)"'
DESTDIR = $(DSTROOT)
DSO = cc
DSOFLAGS =
DSONAME =
LDFLAGS =
LIBS = -lm -lz
RANLIB = ranlib
VERSION = 1.0rc1
prefix = /usr/local
# Base rules
.SUFFIXES: .c .h .o
.c.o:
$(CC) $(CFLAGS) $(CPPFLAGS) $(COMMONFLAGS) -c $<
# Files
PUBHEADERS = \
pdfio.h \
pdfio-content.h
PUBOBJS = \
pdfio-aes.o \
pdfio-array.o \
pdfio-common.o \
pdfio-content.o \
pdfio-crypto.o \
pdfio-dict.o \
pdfio-file.o \
pdfio-md5.o \
pdfio-object.o \
pdfio-page.o \
pdfio-rc4.o \
pdfio-sha256.o \
pdfio-stream.o \
pdfio-string.o \
pdfio-token.o \
pdfio-value.o
LIBOBJS = \
$(PUBOBJS) \
ttf.o
OBJS = \
$(LIBOBJS) \
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) COMMONFLAGS="-g -Os -fPIC" 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
valgrind: testpdfio
valgrind --leak-check=full ./testpdfio
# pdfio library
libpdfio.a: $(LIBOBJS)
$(AR) $(ARFLAGS) $@ $(LIBOBJS)
$(RANLIB) $@
libpdfio.so.1: $(LIBOBJS)
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -shared -o $@ -Wl,-soname,$@ $(LIBOBJS) $(LIBS)
libpdfio.1.dylib: $(LIBOBJS)
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -dynamiclib -o $@ -install_name $(prefix)/lib/$@ -current_version $(VERSION) -compatibility_version 1.0 $(LIBOBJS) $(LIBS)
# pdfio1.def (Windows DLL exports file...)
#
# I'd love to use __declspec(dllexport) but MS puts it before the function
# declaration instead of after like everyone else, and it breaks Codedoc and
# other tools I rely on...
pdfio1.def: $(LIBOBJS) Makefile
echo Generating $@...
echo "LIBRARY pdfio1" >$@
echo "VERSION 1.0" >>$@
echo "EXPORTS" >>$@
nm $(LIBOBJS) 2>/dev/null | grep "T _" | awk '{print $$3}' | \
grep -v '^_ttf' | sed -e '1,$$s/^_//' | sort >>$@
# pdfio test program
testpdfio: testpdfio.o libpdfio.a
$(CC) $(LDFLAGS) $(COMMONFLAGS) -o $@ testpdfio.o libpdfio.a $(LIBS)
# Dependencies
$(OBJS): pdfio.h pdfio-private.h Makefile
pdfio-content.o: pdfio-content.h ttf.h
ttf.o: ttf.h
# Make documentation using Codedoc <https://www.msweet.org/codedoc>
DOCFLAGS = \
--author "Michael R Sweet" \
--copyright "Copyright (c) 2021 by Michael R Sweet" \
--docversion $(VERSION)
.PHONY: doc
doc:
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" $(PUBHEADERS) $(PUBOBJS:.o=.c) --body doc/pdfio.md --coverimage doc/pdfio-512.png pdfio.xml >doc/pdfio.html
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" --body doc/pdfio.md --coverimage doc/pdfio-epub.png pdfio.xml --epub doc/pdfio.epub
codedoc $(DOCFLAGS) --title "pdf read/write library" --man pdfio --section 3 --body doc/pdfio.md pdfio.xml >doc/pdfio.3
rm -f pdfio.xml
# Fuzz-test the library <>
.PHONY: afl
afl:
$(MAKE) -$(MAKEFLAGS) CC="afl-clang-fast" COMMONFLAGS="-g" clean all
test afl-output || rm -rf afl-output
afl-fuzz -x afl-pdf.dict -i afl-input -o afl-output -V 600 -e pdf -t 5000 ./testpdfio @@
# Analyze code with the Clang static analyzer <https://clang-analyzer.llvm.org>
clang:
clang $(CPPFLAGS) --analyze $(OBJS:.o=.c) 2>clang.log
rm -rf $(OBJS:.o=.plist)
test -s clang.log && (echo "$(GHA_ERROR)Clang detected issues."; echo ""; cat clang.log; exit 1) || exit 0
# Analyze code using Cppcheck <http://cppcheck.sourceforge.net>
cppcheck:
cppcheck $(CPPFLAGS) --template=gcc --addon=cert.py --suppressions-list=.cppcheck $(OBJS:.o=.c) 2>cppcheck.log
test -s cppcheck.log && (echo "$(GHA_ERROR)Cppcheck detected issues."; echo ""; cat cppcheck.log; exit 1) || exit 0

262
Makefile.in Normal file
View File

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

View File

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

View File

@ -5,6 +5,25 @@ This file describes how security issues are reported and handled, and what the
expectations are for security issues reported to this project. expectations are for security issues reported to this project.
Reporting a Security Bug
------------------------
For the purposes of this project, a security bug is a software defect that
allows a *local or remote user* to gain unauthorized access or privileges on the
host computer or to cause the software to crash. Such defects should be
reported to the project security advisory page at
<https://github.com/michaelrsweet/pdfio/security/advisories>.
Alternately, security bugs can be reported to "security AT msweet.org" using the
PGP public key below. Expect a response within 5 business days. Any proposed
embargo date should be at least 30 days and no more than 90 days in the future.
> *Note:* If you've found a software defect that allows a *program* to gain
> unauthorized access or privileges on the host computer or causes the program
> to crash, that defect should be reported as an ordinary project issue at
> <https://github.com/michaelrsweet/pdfio/issues>.
Responsible Disclosure Responsible Disclosure
---------------------- ----------------------
@ -35,7 +54,7 @@ starting at 0. A feature release has a "PATCH" value of 0, for example:
1.1.0 1.1.0
2.0.0 2.0.0
Beta releases and release candidates are *not* prodution releases and use Beta releases and release candidates are *not* production releases and use
semantic version numbers of the form: semantic version numbers of the form:
MAJOR.MINORbNUMBER MAJOR.MINORbNUMBER
@ -50,14 +69,6 @@ example:
1.0rc1 1.0rc1
Reporting a Vulnerability
-------------------------
Report all security issues to "security AT msweet.org". Expect a response
within 5 business days. Any proposed embargo date should be at least 30 days
and no more than 90 days in the future.
PGP Public Key PGP Public Key
-------------- --------------

1774
config.guess vendored Executable file

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

5555
configure vendored Executable file

File diff suppressed because it is too large Load Diff

273
configure.ac Normal file
View File

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

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

59
examples/Makefile Normal file
View File

@ -0,0 +1,59 @@
#
# Makefile for PDFio examples.
#
# Copyright © 2024 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..
LIBS = -L.. -lpdfio -lz
# Targets
TARGETS = \
code128 \
image2pdf \
md2pdf \
pdfioinfo
# 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)
# pdfioinfo
pdfioinfo: pdfioinfo.c
$(CC) $(CFLAGS) -o $@ pdfioinfo.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.

BIN
examples/Roboto-Regular.ttf Normal file

Binary file not shown.

Binary file not shown.

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.

139
examples/image2pdf.c Normal file
View File

@ -0,0 +1,139 @@
//
// Image example for PDFio.
//
// Copyright © 2023-2024 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 *pdfname, // I - PDF filename
const char *imagename, // I - Image 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
// 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

65
examples/pdfioinfo.c Normal file
View File

@ -0,0 +1,65 @@
//
// PDF metadata example for PDFio.
//
// Copyright © 2023-2024 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>
//
// '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
time_t creation_date; // Creation date
struct tm *creation_tm; // Creation date/time information
char creation_text[256]; // Creation date/time as a string
// 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 creation date and convert to a string...
creation_date = pdfioFileGetCreationDate(pdf);
creation_tm = localtime(&creation_date);
strftime(creation_text, sizeof(creation_text), "%c", creation_tm);
// Print file information to stdout...
printf("%s:\n", filename);
printf(" Title: %s\n", pdfioFileGetTitle(pdf));
printf(" Author: %s\n", pdfioFileGetAuthor(pdf));
printf(" Created On: %s\n", creation_text);
printf(" Number Pages: %u\n", (unsigned)pdfioFileGetNumPages(pdf));
// Close the PDF file...
pdfioFileClose(pdf);
return (0);
}

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

View File

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

View File

@ -1,16 +1,12 @@
// //
// PDF array functions for PDFio. // 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 // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
// //
//
// Include necessary headers...
//
#include "pdfio-private.h" #include "pdfio-private.h"
@ -331,6 +327,30 @@ pdfioArrayCreate(pdfio_file_t *pdf) // I - PDF file
} }
//
// '_pdfioArrayDecrypt()' - Decrypt values in an array.
//
bool // O - `true` on success, `false` on error
_pdfioArrayDecrypt(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object
pdfio_array_t *a, // I - Array
size_t depth) // I - Depth
{
size_t i; // Looping var
_pdfio_value_t *v; // Current value
for (i = a->num_values, v = a->values; i > 0; i --, v ++)
{
if (!_pdfioValueDecrypt(pdf, obj, v, depth))
return (false);
}
return (true);
}
// //
// '_pdfioArrayDebug()' - Print the contents of an array. // '_pdfioArrayDebug()' - Print the contents of an array.
// //
@ -343,6 +363,9 @@ _pdfioArrayDebug(pdfio_array_t *a, // I - Array
_pdfio_value_t *v; // Current value _pdfio_value_t *v; // Current value
if (!a)
return;
putc('[', fp); putc('[', fp);
for (i = a->num_values, v = a->values; i > 0; i --, v ++) for (i = a->num_values, v = a->values; i > 0; i --, v ++)
_pdfioValueDebug(v, fp); _pdfioValueDebug(v, fp);
@ -614,6 +637,28 @@ _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. // '_pdfioArrayWrite()' - Write an array to a PDF file.
// //

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. // Common support functions for pdfio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
// //
//
// Include necessary headers...
//
#include "pdfio-private.h" #include "pdfio-private.h"
@ -38,6 +34,8 @@ _pdfioFileConsume(pdfio_file_t *pdf, // I - PDF file
else if (_pdfioFileSeek(pdf, (off_t)bytes, SEEK_CUR) < 0) else if (_pdfioFileSeek(pdf, (off_t)bytes, SEEK_CUR) < 0)
return (false); return (false);
PDFIO_DEBUG("_pdfioFileConsume: pos=%ld\n", (long)(pdf->bufpos + pdf->bufptr - pdf->buffer));
return (true); return (true);
} }
@ -143,7 +141,7 @@ _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
*bufend = buffer + bufsize - 1; // Pointer to end of buffer *bufend = buffer + bufsize - 1; // Pointer to end of buffer
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend); PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p, offset=%lu\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend, (unsigned long)(pdf->bufpos + (pdf->bufptr - pdf->buffer)));
while (!eol) while (!eol)
{ {
@ -263,7 +261,7 @@ _pdfioFilePrintf(pdfio_file_t *pdf, // I - PDF file
// Format the string... // Format the string...
va_start(ap, format); va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap); _pdfio_vsnprintf(pdf, buffer, sizeof(buffer), format, ap);
va_end(ap); va_end(ap);
// Write it... // Write it...
@ -358,12 +356,12 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
off_t offset, // I - Offset off_t offset, // I - Offset
int whence) // I - Offset base int whence) // I - Offset base
{ {
PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d)\n", pdf, (long)offset, whence); PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d) pdf->bufpos=%lu\n", pdf, (long)offset, whence, (unsigned long)(pdf ? pdf->bufpos : 0));
// Adjust offset for relative seeks... // Adjust offset for relative seeks...
if (whence == SEEK_CUR) if (whence == SEEK_CUR)
{ {
offset += pdf->bufpos; offset += pdf->bufpos + (pdf->bufptr - pdf->buffer);
whence = SEEK_SET; whence = SEEK_SET;
} }
@ -373,9 +371,9 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
if (whence != SEEK_END && offset >= pdf->bufpos && offset < (pdf->bufpos + pdf->bufend - pdf->buffer)) if (whence != SEEK_END && offset >= pdf->bufpos && offset < (pdf->bufpos + pdf->bufend - pdf->buffer))
{ {
// Yes, seek within existing buffer... // Yes, seek within existing buffer...
pdf->bufptr = pdf->buffer + offset - pdf->bufpos; pdf->bufptr = pdf->buffer + (offset - pdf->bufpos);
PDFIO_DEBUG("_pdfioFileSeek: Seek within buffer, bufpos=%ld.\n", (long)pdf->bufpos); PDFIO_DEBUG("_pdfioFileSeek: Seek within buffer, bufpos=%ld.\n", (long)pdf->bufpos);
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p, bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufend); PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p(<%02X%02X...>), bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufptr[0] & 255, pdf->bufptr[1] & 255, pdf->bufend);
return (offset); return (offset);
} }
@ -406,7 +404,7 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
return (-1); return (-1);
} }
PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld.\n", (long)pdf->bufpos); PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld, offset=%lu.\n", (long)pdf->bufpos, (unsigned long)offset);
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p, bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufend); PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p, bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufend);
pdf->bufpos = offset; pdf->bufpos = offset;
@ -525,7 +523,7 @@ read_buffer(pdfio_file_t *pdf, // I - PDF file
return (rbytes); return (rbytes);
} }
// //
// 'write_buffer()' - Write a buffer to a PDF file. // 'write_buffer()' - Write a buffer to a PDF file.
// //

View File

@ -1,18 +1,15 @@
// //
// Content helper functions for PDFio. // Content helper functions for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
// //
//
// Include necessary headers...
//
#include "pdfio-private.h" #include "pdfio-private.h"
#include "pdfio-content.h" #include "pdfio-content.h"
#include "pdfio-base-font-widths.h"
#include "ttf.h" #include "ttf.h"
#include <math.h> #include <math.h>
#ifndef M_PI #ifndef M_PI
@ -489,8 +486,11 @@ pdfioContentMatrixRotate(
pdfio_stream_t *st, // I - Stream pdfio_stream_t *st, // I - Stream
double degrees) // I - Rotation angle in degrees counter-clockwise double degrees) // I - Rotation angle in degrees counter-clockwise
{ {
double dcos = cos(degrees / M_PI); // Cosine double dcos = cos(M_PI * degrees / 180.0);
double dsin = sin(degrees / M_PI); // Sine // Cosine
double dsin = sin(M_PI * degrees / 180.0);
// Sine
return (pdfioStreamPrintf(st, "%g %g %g %g 0 0 cm\n", dcos, -dsin, dsin, dcos)); return (pdfioStreamPrintf(st, "%g %g %g %g 0 0 cm\n", dcos, -dsin, dsin, dcos));
} }
@ -586,6 +586,17 @@ pdfioContentPathCurve23(
} }
//
// 'pdfioContentPathEnd()' - Clear the current path.
//
bool // O - `true` on success, `false` on failure
pdfioContentPathEnd(pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "n\n"));
}
// //
// 'pdfioContentPathLineTo()' - Add a straight line to the current path. // 'pdfioContentPathLineTo()' - Add a straight line to the current path.
// //
@ -656,6 +667,9 @@ pdfioContentSave(pdfio_stream_t *st) // I - Stream
// //
// 'pdfioContentSetDashPattern()' - Set the stroke pattern. // 'pdfioContentSetDashPattern()' - Set the stroke pattern.
// //
// This function sets the stroke pattern when drawing lines. If "on" and "off"
// are 0, a solid line is drawn.
//
bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
pdfioContentSetDashPattern( pdfioContentSetDashPattern(
@ -664,7 +678,12 @@ pdfioContentSetDashPattern(
double on, // I - On length double on, // I - On length
double off) // I - Off length double off) // I - Off length
{ {
return (pdfioStreamPrintf(st, "[%g %g] %g d\n", on, off, phase)); if (on <= 0.0 && off <= 0.0)
return (pdfioStreamPrintf(st, "[] %g d\n", phase));
else if (fabs(on - off) < 0.001)
return (pdfioStreamPrintf(st, "[%g] %g d\n", on, phase));
else
return (pdfioStreamPrintf(st, "[%g %g] %g d\n", on, off, phase));
} }
@ -1042,6 +1061,138 @@ pdfioContentTextEnd(pdfio_stream_t *st) // I - Stream
} }
//
// 'pdfioContentTextMeasure()' - Measure a text string and return its width.
//
// This function measures the given text string "s" and returns its width based
// on "size". The text string must always use the UTF-8 (Unicode) encoding but
// any control characters (such as newlines) are ignored.
//
double // O - Width
pdfioContentTextMeasure(
pdfio_obj_t *font, // I - Font object created by @link pdfioFileCreateFontObjFromFile@
const char *s, // I - UTF-8 string
double size) // I - Font size/height
{
const char *basefont; // Base font name
ttf_t *ttf = (ttf_t *)_pdfioObjGetExtension(font);
// TrueType font data
ttf_rect_t extents; // Text extents
int ch; // Unicode character
char temp[1024], // Temporary string
*tempptr; // Pointer into temporary string
if (!ttf && (basefont = pdfioDictGetName(pdfioObjGetDict(font), "BaseFont")) != NULL)
{
// Measure the width using the compiled-in base font tables...
const short *widths; // Widths
int width = 0; // Current width
if (strcmp(basefont, "Symbol") && strcmp(basefont, "Zapf-Dingbats"))
{
// Map non-CP1282 characters to '?', everything else as-is...
tempptr = temp;
while (*s && tempptr < (temp + sizeof(temp) - 3))
{
if ((*s & 0xe0) == 0xc0)
{
// Two-byte UTF-8
ch = ((s[0] & 0x1f) << 6) | (s[1] & 0x3f);
s += 2;
}
else if ((*s & 0xf0) == 0xe0)
{
// Three-byte UTF-8
ch = ((s[0] & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f);
s += 3;
}
else if ((*s & 0xf8) == 0xf0)
{
// Four-byte UTF-8
ch = ((s[0] & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f);
s += 4;
}
else
{
ch = *s++;
}
if (ch > 255)
{
// Try mapping from Unicode to CP1252...
size_t i; // Looping var
for (i = 0; i < (sizeof(_pdfio_cp1252) / sizeof(_pdfio_cp1252[0])); i ++)
{
if (ch == _pdfio_cp1252[i])
break;
}
if (i < (sizeof(_pdfio_cp1252) / sizeof(_pdfio_cp1252[0])))
ch = i + 0x80; // Extra characters from 0x80 to 0x9f
else
ch = '?'; // Unsupported chars map to ?
}
*tempptr++ = (char)ch;
}
*tempptr = '\0';
s = temp;
}
// Choose the appropriate table...
if (!strcmp(basefont, "Courier"))
widths = courier_widths;
else if (!strcmp(basefont, "Courier-Bold"))
widths = courier_bold_widths;
else if (!strcmp(basefont, "Courier-BoldOblique"))
widths = courier_boldoblique_widths;
else if (!strcmp(basefont, "Courier-Oblique"))
widths = courier_oblique_widths;
else if (!strcmp(basefont, "Helvetica"))
widths = helvetica_widths;
else if (!strcmp(basefont, "Helvetica-Bold"))
widths = helvetica_bold_widths;
else if (!strcmp(basefont, "Helvetica-BoldOblique"))
widths = helvetica_boldoblique_widths;
else if (!strcmp(basefont, "Helvetica-Oblique"))
widths = helvetica_oblique_widths;
else if (!strcmp(basefont, "Symbol"))
widths = symbol_widths;
else if (!strcmp(basefont, "Times-Bold"))
widths = times_bold_widths;
else if (!strcmp(basefont, "Times-BoldItalic"))
widths = times_bolditalic_widths;
else if (!strcmp(basefont, "Times-Italic"))
widths = times_italic_widths;
else if (!strcmp(basefont, "Times-Roman"))
widths = times_roman_widths;
else if (!strcmp(basefont, "ZapfDingbats"))
widths = zapfdingbats_widths;
else
return (0.0);
// Calculate the width using the corresponding table...
while (*s)
{
width += widths[*s & 255];
s ++;
}
return (size * 0.001 * width);
}
// If we get here then we need to measure using the TrueType library...
ttfGetExtents(ttf, (float)size, s, &extents);
return (extents.right - extents.left);
}
// //
// 'pdfioContentTextMoveLine()' - Move to the next line and offset. // 'pdfioContentTextMoveLine()' - Move to the next line and offset.
// //
@ -1071,7 +1222,21 @@ pdfioContentTextMoveTo(
// //
// 'pdfioContentTextNextLine()' - Move to the next line. // 'pdfioContentTextNewLine()' - Move to the next line.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextNewLine(
pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "T*\n"));
}
//
// 'pdfioContentTextNextLine()' - Legacy function name preserved for binary compatibility.
//
// @private@
// //
bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
@ -1082,6 +1247,86 @@ pdfioContentTextNextLine(
} }
//
// 'pdfioContentTextNewLineShow()' - Move to the next line and show text.
//
// This function moves to the next line and then shows some text with optional
// word and character spacing in a PDF content stream. The "unicode" argument
// specifies that the current font maps to full Unicode. The "s" argument
// specifies a UTF-8 encoded string.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextNewLineShow(
pdfio_stream_t *st, // I - Stream
double ws, // I - Word spacing or `0.0` for none
double cs, // I - Character spacing or `0.0` for none
bool unicode, // I - Unicode text?
const char *s) // I - String to show
{
bool newline = false; // New line?
char op; // Text operator
// Write word and/or character spacing as needed...
if (ws > 0.0 || cs > 0.0)
{
// Use " operator to show text with word and character spacing...
if (!pdfioStreamPrintf(st, "%g %g", ws, cs))
return (false);
op = '\"';
}
else
{
// Use ' operator to show text with the defaults...
op = '\'';
}
// Write the string...
if (!write_string(st, unicode, s, &newline))
return (false);
// Draw it...
if (newline)
return (pdfioStreamPrintf(st, "%c T*\n", op));
else
return (pdfioStreamPrintf(st, "%c\n", op));
}
//
// 'pdfioContentTextNewLineShowf()' - Show formatted text.
//
// This function moves to the next line and shows some formatted text with
// optional word and character spacing in a PDF content stream. The "unicode"
// argument specifies that the current font maps to full Unicode. The "format"
// argument specifies a UTF-8 encoded `printf`-style format string.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextNewLineShowf(
pdfio_stream_t *st, // I - Stream
double ws, // I - Word spacing or `0.0` for none
double cs, // I - Character spacing or `0.0` for none
bool unicode, // I - Unicode text?
const char *format, // I - `printf`-style format string
...) // I - Additional arguments as needed
{
char buffer[8192]; // Text buffer
va_list ap; // Argument pointer
// Format the string...
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
// Show it...
return (pdfioContentTextNewLineShow(st, ws, cs, unicode, buffer));
}
// //
// 'pdfioContentTextShow()' - Show text. // 'pdfioContentTextShow()' - Show text.
// //
@ -1114,9 +1359,9 @@ pdfioContentTextShow(
// //
// 'pdfioContentTextShowf()' - Show formatted text. // 'pdfioContentTextShowf()' - Show formatted text.
// //
// This function shows some text in a PDF content stream. The "unicode" argument // This function shows some formatted text in a PDF content stream. The
// specifies that the current font maps to full Unicode. The "format" argument // "unicode" argument specifies that the current font maps to full Unicode.
// specifies a UTF-8 encoded `printf`-style format string. // The "format" argument specifies a UTF-8 encoded `printf`-style format string.
// //
bool bool
@ -1211,8 +1456,9 @@ pdfioContentTextShowJustified(
// - "Times-Roman" // - "Times-Roman"
// - "ZapfDingbats" // - "ZapfDingbats"
// //
// Base fonts always use the Windows CP1252 (ISO-8859-1 with additional // Aside from "Symbol" and "Zapf-Dingbats", Base fonts use the Windows CP1252
// characters such as the Euro symbol) subset of Unicode. // (ISO-8859-1 with additional characters such as the Euro symbol) subset of
// Unicode.
// //
pdfio_obj_t * // O - Font object pdfio_obj_t * // O - Font object
@ -1267,13 +1513,13 @@ pdfioFileCreateFontObjFromFile(
pdfio_dict_t *dict, // Font dictionary pdfio_dict_t *dict, // Font dictionary
*desc, // Font descriptor *desc, // Font descriptor
*file; // Font file dictionary *file; // Font file dictionary
pdfio_obj_t *obj, // Font object pdfio_obj_t *obj = NULL, // Font object
*desc_obj, // Font descriptor object *desc_obj, // Font descriptor object
*file_obj; // Font file object *file_obj; // Font file object
const char *basefont; // Base font name const char *basefont; // Base font name
pdfio_array_t *bbox; // Font bounding box array pdfio_array_t *bbox; // Font bounding box array
pdfio_stream_t *st; // Font stream pdfio_stream_t *st; // Font stream
int fd; // File int fd = -1; // File
unsigned char buffer[16384]; // Read buffer unsigned char buffer[16384]; // Read buffer
ssize_t bytes; // Bytes read ssize_t bytes; // Bytes read
@ -1302,48 +1548,32 @@ pdfioFileCreateFontObjFromFile(
// Create the font file dictionary and object... // Create the font file dictionary and object...
if ((file = pdfioDictCreate(pdf)) == NULL) if ((file = pdfioDictCreate(pdf)) == NULL)
{ goto done;
ttfDelete(font);
close(fd);
return (NULL);
}
pdfioDictSetName(file, "Filter", "FlateDecode"); pdfioDictSetName(file, "Filter", "FlateDecode");
if ((file_obj = pdfioFileCreateObj(pdf, file)) == NULL) if ((file_obj = pdfioFileCreateObj(pdf, file)) == NULL)
{ goto done;
ttfDelete(font);
close(fd);
return (NULL);
}
if ((st = pdfioObjCreateStream(file_obj, PDFIO_FILTER_FLATE)) == NULL) if ((st = pdfioObjCreateStream(file_obj, PDFIO_FILTER_FLATE)) == NULL)
{ goto done;
ttfDelete(font);
close(fd);
return (NULL);
}
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
{ {
if (!pdfioStreamWrite(st, buffer, (size_t)bytes)) if (!pdfioStreamWrite(st, buffer, (size_t)bytes))
{ {
ttfDelete(font);
close(fd);
pdfioStreamClose(st); pdfioStreamClose(st);
return (NULL); goto done;
} }
} }
close(fd); close(fd);
fd = -1;
pdfioStreamClose(st); pdfioStreamClose(st);
// Create the font descriptor dictionary and object... // Create the font descriptor dictionary and object...
if ((bbox = pdfioArrayCreate(pdf)) == NULL) if ((bbox = pdfioArrayCreate(pdf)) == NULL)
{ goto done;
ttfDelete(font);
return (NULL);
}
ttfGetBounds(font, &bounds); ttfGetBounds(font, &bounds);
@ -1353,10 +1583,7 @@ pdfioFileCreateFontObjFromFile(
pdfioArrayAppendNumber(bbox, bounds.top); pdfioArrayAppendNumber(bbox, bounds.top);
if ((desc = pdfioDictCreate(pdf)) == NULL) if ((desc = pdfioDictCreate(pdf)) == NULL)
{ goto done;
ttfDelete(font);
return (NULL);
}
basefont = pdfioStringCreate(pdf, ttfGetPostScriptName(font)); basefont = pdfioStringCreate(pdf, ttfGetPostScriptName(font));
@ -1375,59 +1602,70 @@ pdfioFileCreateFontObjFromFile(
pdfioDictSetNumber(desc, "StemV", ttfGetWeight(font) / 4 + 25); pdfioDictSetNumber(desc, "StemV", ttfGetWeight(font) / 4 + 25);
if ((desc_obj = pdfioFileCreateObj(pdf, desc)) == NULL) if ((desc_obj = pdfioFileCreateObj(pdf, desc)) == NULL)
{ goto done;
ttfDelete(font);
return (NULL);
}
pdfioObjClose(desc_obj); pdfioObjClose(desc_obj);
if (unicode) if (unicode)
{ {
// Unicode (CID) font... // Unicode (CID) font...
pdfio_dict_t *cid2gid; // CIDToGIDMap dictionary pdfio_dict_t *cid2gid, // CIDToGIDMap dictionary
pdfio_obj_t *cid2gid_obj; // CIDToGIDMap object *to_unicode; // ToUnicode dictionary
pdfio_obj_t *cid2gid_obj, // CIDToGIDMap object
*to_unicode_obj;// ToUnicode object
size_t i, // Looping var size_t i, // Looping var
start, // Start character
num_cmap; // Number of CMap entries num_cmap; // Number of CMap entries
const int *cmap; // CMap entries const int *cmap; // CMap entries
int min_glyph, // First glyph
max_glyph; // Last glyph
unsigned short glyphs[65536]; // Glyph to Unicode mapping
unsigned char *bufptr, // Pointer into buffer unsigned char *bufptr, // Pointer into buffer
*bufend; // End of buffer *bufend; // End of buffer
pdfio_dict_t *type2; // CIDFontType2 font dictionary pdfio_dict_t *type2; // CIDFontType2 font dictionary
pdfio_obj_t *type2_obj; // CIDFontType2 font object pdfio_obj_t *type2_obj; // CIDFontType2 font object
pdfio_array_t *descendants; // Decendant font list pdfio_array_t *descendants; // Decendant font list
pdfio_dict_t *sidict; // CIDSystemInfo dictionary pdfio_dict_t *sidict; // CIDSystemInfo dictionary
pdfio_array_t *w_array, // Width array
*temp_array; // Temporary width sub-array
int w0, w1; // Widths
// Create a CIDSystemInfo mapping to Adobe UCS2 v0 (Unicode)
if ((sidict = pdfioDictCreate(pdf)) == NULL)
goto done;
pdfioDictSetString(sidict, "Registry", "Adobe");
pdfioDictSetString(sidict, "Ordering", "Identity");
pdfioDictSetNumber(sidict, "Supplement", 0);
// Create a CIDToGIDMap object for the Unicode font... // Create a CIDToGIDMap object for the Unicode font...
if ((cid2gid = pdfioDictCreate(pdf)) == NULL) if ((cid2gid = pdfioDictCreate(pdf)) == NULL)
{ goto done;
ttfDelete(font);
return (NULL);
}
#ifndef DEBUG #ifndef DEBUG
pdfioDictSetName(cid2gid, "Filter", "FlateDecode"); pdfioDictSetName(cid2gid, "Filter", "FlateDecode");
#endif // !DEBUG #endif // !DEBUG
if ((cid2gid_obj = pdfioFileCreateObj(pdf, cid2gid)) == NULL) if ((cid2gid_obj = pdfioFileCreateObj(pdf, cid2gid)) == NULL)
{ goto done;
ttfDelete(font);
return (NULL);
}
#ifdef DEBUG #ifdef DEBUG
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_NONE)) == NULL) if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_NONE)) == NULL)
#else #else
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_FLATE)) == NULL) if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_FLATE)) == NULL)
#endif // DEBUG #endif // DEBUG
{ goto done;
ttfDelete(font);
return (NULL);
}
cmap = ttfGetCMap(font, &num_cmap); cmap = ttfGetCMap(font, &num_cmap);
min_glyph = 65536;
max_glyph = 0;
memset(glyphs, 0, sizeof(glyphs));
PDFIO_DEBUG("pdfioFileCreateFontObjFromFile: num_cmap=%u\n", (unsigned)num_cmap);
for (i = 0, bufptr = buffer, bufend = buffer + sizeof(buffer); i < num_cmap; i ++) for (i = 0, bufptr = buffer, bufend = buffer + sizeof(buffer); i < num_cmap; i ++)
{ {
PDFIO_DEBUG("pdfioFileCreateFontObjFromFile: cmap[%u]=%d\n", (unsigned)i, cmap[i]);
if (cmap[i] < 0) if (cmap[i] < 0)
{ {
// Map undefined glyph to .notdef... // Map undefined glyph to .notdef...
@ -1439,6 +1677,12 @@ pdfioFileCreateFontObjFromFile(
// Map to specified glyph... // Map to specified glyph...
*bufptr++ = (unsigned char)(cmap[i] >> 8); *bufptr++ = (unsigned char)(cmap[i] >> 8);
*bufptr++ = (unsigned char)(cmap[i] & 255); *bufptr++ = (unsigned char)(cmap[i] & 255);
glyphs[cmap[i]] = (unsigned short)i;
if (cmap[i] < min_glyph)
min_glyph = cmap[i];
if (cmap[i] > max_glyph)
max_glyph = cmap[i];
} }
if (bufptr >= bufend) if (bufptr >= bufend)
@ -1447,8 +1691,7 @@ pdfioFileCreateFontObjFromFile(
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer))) if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
{ {
pdfioStreamClose(st); pdfioStreamClose(st);
ttfDelete(font); goto done;
return (NULL);
} }
bufptr = buffer; bufptr = buffer;
@ -1461,30 +1704,102 @@ pdfioFileCreateFontObjFromFile(
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer))) if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
{ {
pdfioStreamClose(st); pdfioStreamClose(st);
ttfDelete(font); goto done;
return (NULL);
} }
} }
pdfioStreamClose(st); pdfioStreamClose(st);
// ToUnicode mapping object
to_unicode = pdfioDictCreate(pdf);
pdfioDictSetName(to_unicode, "Type", "CMap");
pdfioDictSetName(to_unicode, "CMapName", "Adobe-Identity-UCS2");
pdfioDictSetDict(to_unicode, "CIDSystemInfo", sidict);
#ifndef DEBUG
pdfioDictSetName(to_unicode, "Filter", "FlateDecode");
#endif // !DEBUG
if ((to_unicode_obj = pdfioFileCreateObj(pdf, to_unicode)) == NULL)
goto done;
#ifdef DEBUG
if ((st = pdfioObjCreateStream(to_unicode_obj, PDFIO_FILTER_NONE)) == NULL)
#else
if ((st = pdfioObjCreateStream(to_unicode_obj, PDFIO_FILTER_FLATE)) == NULL)
#endif // DEBUG
goto done;
pdfioStreamPuts(st,
"stream\n"
"/CIDInit /ProcSet findresource begin\n"
"12 dict begin\n"
"begincmap\n"
"/CIDSystemInfo<<\n"
"/Registry (Adobe)\n"
"/Ordering (UCS2)\n"
"/Supplement 0\n"
">> def\n"
"/CMapName /Adobe-Identity-UCS2 def\n"
"/CMapType 2 def\n"
"1 begincodespacerange\n"
"<0000> <FFFF>\n"
"endcodespacerange\n"
"1 beginbfrange\n"
"<0000> <FFFF> <0000>\n"
"endbfrange\n"
"endcmap\n"
"CMapName currentdict /CMap defineresource pop\n"
"end\n"
"end\n");
pdfioStreamClose(st);
// Create a CIDFontType2 dictionary for the Unicode font... // Create a CIDFontType2 dictionary for the Unicode font...
if ((type2 = pdfioDictCreate(pdf)) == NULL) if ((type2 = pdfioDictCreate(pdf)) == NULL)
{ goto done;
ttfDelete(font);
return (NULL);
}
if ((sidict = pdfioDictCreate(pdf)) == NULL) // Width array
{ if ((w_array = pdfioArrayCreate(pdf)) == NULL)
ttfDelete(font); goto done;
return (NULL);
}
// CIDSystemInfo mapping to Adobe UCS2 v0 (Unicode) for (start = 0, w0 = ttfGetWidth(font, 0), w1 = 0, i = 1; i < 65536; start = i, w0 = w1, i ++)
pdfioDictSetString(sidict, "Registry", "Adobe"); {
pdfioDictSetString(sidict, "Ordering", "Identity"); while (i < 65536 && (w1 = ttfGetWidth(font, (int)i)) == w0)
pdfioDictSetNumber(sidict, "Supplement", 0); i ++;
if ((i - start) > 1)
{
// Encode a repeating sequence...
pdfioArrayAppendNumber(w_array, start);
pdfioArrayAppendNumber(w_array, i - 1);
pdfioArrayAppendNumber(w_array, w0);
}
else
{
// Encode a non-repeating sequence...
pdfioArrayAppendNumber(w_array, start);
if ((temp_array = pdfioArrayCreate(pdf)) == NULL)
goto done;
pdfioArrayAppendNumber(temp_array, w0);
for (w0 = w1, i ++; i < 65536; w0 = w1, i ++)
{
if ((w1 = ttfGetWidth(font, (int)i)) == w0 && i < 65535)
break;
pdfioArrayAppendNumber(temp_array, w0);
}
if (i == 65536)
pdfioArrayAppendNumber(temp_array, w0);
else
i --;
pdfioArrayAppendArray(w_array, temp_array);
}
}
// Then the dictionary for the CID base font... // Then the dictionary for the CID base font...
pdfioDictSetName(type2, "Type", "Font"); pdfioDictSetName(type2, "Type", "Font");
@ -1493,56 +1808,41 @@ pdfioFileCreateFontObjFromFile(
pdfioDictSetDict(type2, "CIDSystemInfo", sidict); pdfioDictSetDict(type2, "CIDSystemInfo", sidict);
pdfioDictSetObj(type2, "CIDToGIDMap", cid2gid_obj); pdfioDictSetObj(type2, "CIDToGIDMap", cid2gid_obj);
pdfioDictSetObj(type2, "FontDescriptor", desc_obj); pdfioDictSetObj(type2, "FontDescriptor", desc_obj);
pdfioDictSetArray(type2, "W", w_array);
if ((type2_obj = pdfioFileCreateObj(pdf, type2)) == NULL) if ((type2_obj = pdfioFileCreateObj(pdf, type2)) == NULL)
{ goto done;
ttfDelete(font);
return (NULL);
}
pdfioObjClose(type2_obj); pdfioObjClose(type2_obj);
// Create a Type 0 font object... // Create a Type 0 font object...
if ((descendants = pdfioArrayCreate(pdf)) == NULL) if ((descendants = pdfioArrayCreate(pdf)) == NULL)
{ goto done;
ttfDelete(font);
return (NULL);
}
pdfioArrayAppendObj(descendants, type2_obj); pdfioArrayAppendObj(descendants, type2_obj);
if ((dict = pdfioDictCreate(pdf)) == NULL) if ((dict = pdfioDictCreate(pdf)) == NULL)
{ goto done;
ttfDelete(font);
return (NULL);
}
pdfioDictSetName(dict, "Type", "Font"); pdfioDictSetName(dict, "Type", "Font");
pdfioDictSetName(dict, "Subtype", "Type0"); pdfioDictSetName(dict, "Subtype", "Type0");
pdfioDictSetName(dict, "BaseFont", basefont); pdfioDictSetName(dict, "BaseFont", basefont);
pdfioDictSetArray(dict, "DescendantFonts", descendants); pdfioDictSetArray(dict, "DescendantFonts", descendants);
pdfioDictSetName(dict, "Encoding", "Identity-H"); pdfioDictSetName(dict, "Encoding", "Identity-H");
pdfioDictSetObj(dict, "ToUnicode", to_unicode_obj);
if ((obj = pdfioFileCreateObj(pdf, dict)) == NULL) if ((obj = pdfioFileCreateObj(pdf, dict)) != NULL)
return (NULL); pdfioObjClose(obj);
pdfioObjClose(obj);
} }
else else
{ {
// Simple (CP1282 or custom encoding) 8-bit font... // Simple (CP1282 or custom encoding) 8-bit font...
if (ttfGetMaxChar(font) >= 255 && !pdf->cp1252_obj && !create_cp1252(pdf)) if (ttfGetMaxChar(font) >= 255 && !pdf->cp1252_obj && !create_cp1252(pdf))
{ goto done;
ttfDelete(font);
return (NULL);
}
// Create a TrueType font object... // Create a TrueType font object...
if ((dict = pdfioDictCreate(pdf)) == NULL) if ((dict = pdfioDictCreate(pdf)) == NULL)
{ goto done;
ttfDelete(font);
return (NULL);
}
pdfioDictSetName(dict, "Type", "Font"); pdfioDictSetName(dict, "Type", "Font");
pdfioDictSetName(dict, "Subtype", "TrueType"); pdfioDictSetName(dict, "Subtype", "TrueType");
@ -1552,16 +1852,16 @@ pdfioFileCreateFontObjFromFile(
pdfioDictSetObj(dict, "FontDescriptor", desc_obj); pdfioDictSetObj(dict, "FontDescriptor", desc_obj);
if ((obj = pdfioFileCreateObj(pdf, dict)) == NULL) if ((obj = pdfioFileCreateObj(pdf, dict)) != NULL)
{ pdfioObjClose(obj);
ttfDelete(font);
return (NULL);
}
pdfioObjClose(obj);
} }
ttfDelete(font); done:
if (fd >= 0)
close(fd);
_pdfioObjSetExtension(obj, font, (_pdfio_extfree_t)ttfDelete);
return (obj); return (obj);
} }
@ -2085,7 +2385,7 @@ pdfioPageDictAddColorSpace(
bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
pdfioPageDictAddFont( pdfioPageDictAddFont(
pdfio_dict_t *dict, // I - Page dictionary pdfio_dict_t *dict, // I - Page dictionary
const char *name, // I - Font name const char *name, // I - Font name; must not contain spaces
pdfio_obj_t *obj) // I - Font object pdfio_obj_t *obj) // I - Font object
{ {
pdfio_dict_t *resources; // Resource dictionary pdfio_dict_t *resources; // Resource dictionary

View File

@ -1,7 +1,7 @@
// //
// Public content header file for PDFio. // Public content header file for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2023 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -9,18 +9,7 @@
#ifndef PDFIO_CONTENT_H #ifndef PDFIO_CONTENT_H
# define PDFIO_CONTENT_H # define PDFIO_CONTENT_H
//
// Include necessary headers...
//
# include "pdfio.h" # include "pdfio.h"
//
// C++ magic...
//
# ifdef __cplusplus # ifdef __cplusplus
extern "C" { extern "C" {
# endif // __cplusplus # endif // __cplusplus
@ -91,6 +80,7 @@ extern bool pdfioContentPathClose(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentPathCurve(pdfio_stream_t *st, double x1, double y1, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC; extern bool pdfioContentPathCurve(pdfio_stream_t *st, double x1, double y1, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC;
extern bool pdfioContentPathCurve13(pdfio_stream_t *st, double x1, double y1, double x3, double y3) _PDFIO_PUBLIC; extern bool pdfioContentPathCurve13(pdfio_stream_t *st, double x1, double y1, double x3, double y3) _PDFIO_PUBLIC;
extern bool pdfioContentPathCurve23(pdfio_stream_t *st, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC; extern bool pdfioContentPathCurve23(pdfio_stream_t *st, double x2, double y2, double x3, double y3) _PDFIO_PUBLIC;
extern bool pdfioContentPathEnd(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentPathLineTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC; extern bool pdfioContentPathLineTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC;
extern bool pdfioContentPathMoveTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC; extern bool pdfioContentPathMoveTo(pdfio_stream_t *st, double x, double y) _PDFIO_PUBLIC;
extern bool pdfioContentPathRect(pdfio_stream_t *st, double x, double y, double width, double height) _PDFIO_PUBLIC; extern bool pdfioContentPathRect(pdfio_stream_t *st, double x, double y, double width, double height) _PDFIO_PUBLIC;
@ -125,8 +115,12 @@ extern bool pdfioContentSetTextXScaling(pdfio_stream_t *st, double percent) _PD
extern bool pdfioContentStroke(pdfio_stream_t *st) _PDFIO_PUBLIC; extern bool pdfioContentStroke(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentTextBegin(pdfio_stream_t *st) _PDFIO_PUBLIC; extern bool pdfioContentTextBegin(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentTextEnd(pdfio_stream_t *st) _PDFIO_PUBLIC; extern bool pdfioContentTextEnd(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern double pdfioContentTextMeasure(pdfio_obj_t *font, const char *s, double size) _PDFIO_PUBLIC;
extern bool pdfioContentTextMoveLine(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC; extern bool pdfioContentTextMoveLine(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
extern bool pdfioContentTextMoveTo(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC; extern bool pdfioContentTextMoveTo(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
extern bool pdfioContentTextNewLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentTextNewLineShow(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *s) _PDFIO_PUBLIC;
extern bool pdfioContentTextNewLineShowf(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(5,6);
extern bool pdfioContentTextNextLine(pdfio_stream_t *st) _PDFIO_PUBLIC; extern bool pdfioContentTextNextLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentTextShow(pdfio_stream_t *st, bool unicode, const char *s) _PDFIO_PUBLIC; extern bool pdfioContentTextShow(pdfio_stream_t *st, bool unicode, const char *s) _PDFIO_PUBLIC;
extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4); extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4);
@ -150,10 +144,6 @@ extern bool pdfioPageDictAddFont(pdfio_dict_t *dict, const char *name, pdfio_ob
extern bool pdfioPageDictAddImage(pdfio_dict_t *dict, const char *name, pdfio_obj_t *obj) _PDFIO_PUBLIC; extern bool pdfioPageDictAddImage(pdfio_dict_t *dict, const char *name, pdfio_obj_t *obj) _PDFIO_PUBLIC;
//
// C++ magic...
//
# ifdef __cplusplus # ifdef __cplusplus
} }
# endif // __cplusplus # endif // __cplusplus

View File

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

View File

@ -1,16 +1,12 @@
// //
// PDF dictionary functions for PDFio. // PDF dictionary functions for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
// //
//
// Include necessary headers...
//
#include "pdfio-private.h" #include "pdfio-private.h"
@ -22,19 +18,22 @@ static int compare_pairs(_pdfio_pair_t *a, _pdfio_pair_t *b);
// //
// '_pdfioDictClear()' - Remove a key/value pair from a dictionary. // 'pdfioDictClear()' - Remove a key/value pair from a dictionary.
// //
void bool // O - `true` if cleared, `false` otherwise
_pdfioDictClear(pdfio_dict_t *dict, // I - Dictionary pdfioDictClear(pdfio_dict_t *dict, // I - Dictionary
const char *key) // I - Key const char *key) // I - Key
{ {
size_t idx; // Index into pairs size_t idx; // Index into pairs
_pdfio_pair_t *pair, // Current pair _pdfio_pair_t *pair, // Current pair
pkey; // Search key pkey; // Search key
PDFIO_DEBUG("_pdfioDictClear(dict=%p, key=\"%s\")\n", dict, key); PDFIO_DEBUG("pdfioDictClear(dict=%p, key=\"%s\")\n", dict, key);
if (!dict || !key)
return (false);
// See if the key is already set... // See if the key is already set...
if (dict->num_pairs > 0) if (dict->num_pairs > 0)
@ -52,8 +51,12 @@ _pdfioDictClear(pdfio_dict_t *dict, // I - Dictionary
if (idx < dict->num_pairs) if (idx < dict->num_pairs)
memmove(pair, pair + 1, (dict->num_pairs - idx) * sizeof(_pdfio_pair_t)); memmove(pair, pair + 1, (dict->num_pairs - idx) * sizeof(_pdfio_pair_t));
return (true);
} }
} }
return (false);
} }
@ -162,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. // '_pdfioDictDebug()' - Dump a dictionary to stderr.
// //
@ -174,6 +201,9 @@ _pdfioDictDebug(pdfio_dict_t *dict, // I - Dictionary
_pdfio_pair_t *pair; // Current pair _pdfio_pair_t *pair; // Current pair
if (!dict)
return;
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++) for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{ {
fprintf(fp, "/%s", pair->key); fprintf(fp, "/%s", pair->key);
@ -312,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. // 'pdfioDictGetName()' - Get a key name value from a dictionary.
// //
@ -330,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. // 'pdfioDictGetNumber()' - Get a key number value from a dictionary.
// //
@ -406,9 +459,27 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
if (value && value->type == PDFIO_VALTYPE_STRING) if (value && value->type == PDFIO_VALTYPE_STRING)
{
return (value->value.string); return (value->value.string);
}
else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096)
{
// Convert binary string to regular string...
char temp[4096]; // Temporary string
memcpy(temp, value->value.binary.data, value->value.binary.datalen);
temp[value->value.binary.datalen] = '\0';
free(value->value.binary.data);
value->type = PDFIO_VALTYPE_STRING;
value->value.string = pdfioStringCreate(dict->pdf, temp);
return (value->value.string);
}
else else
{
return (NULL); return (NULL);
}
} }
@ -464,6 +535,47 @@ _pdfioDictGetValue(pdfio_dict_t *dict, // I - Dictionary
} }
//
// 'pdfioDictIterateKeys()' - Iterate the keys in a dictionary.
//
// This function iterates the keys in a dictionary, calling the supplied
// function "cb":
//
// ```
// bool
// my_dict_cb(pdfio_dict_t *dict, const char *key, void *cb_data)
// {
// ... "key" contains the dictionary key ...
// ... return true to continue or false to stop ...
// }
// ```
//
// The iteration continues as long as the callback returns `true` or all keys
// have been iterated.
//
void
pdfioDictIterateKeys(
pdfio_dict_t *dict, // I - Dictionary
pdfio_dict_cb_t cb, // I - Callback function
void *cb_data) // I - Callback data
{
size_t i; // Looping var
_pdfio_pair_t *pair; // Current pair
// Range check input...
if (!dict || !cb)
return;
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{
if (!(cb)(dict, pair->key, cb_data))
break;
}
}
// //
// '_pdfioDictRead()' - Read a dictionary from a PDF file. // '_pdfioDictRead()' - Read a dictionary from a PDF file.
// //
@ -481,7 +593,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
_pdfio_value_t value; // Dictionary value _pdfio_value_t value; // Dictionary value
PDFIO_DEBUG("_pdfioDictRead(pdf=%p)\n", pdf); PDFIO_DEBUG("_pdfioDictRead(pdf=%p, obj=%p, tb=%p, depth=%lu)\n", pdf, obj, tb, (unsigned long)depth);
// Create a dictionary and start reading... // Create a dictionary and start reading...
if ((dict = pdfioDictCreate(pdf)) == NULL) if ((dict = pdfioDictCreate(pdf)) == NULL)
@ -493,6 +605,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
if (!strcmp(key, ">>")) if (!strcmp(key, ">>"))
{ {
// End of dictionary... // End of dictionary...
PDFIO_DEBUG("_pdfioDictRead: Returning dictionary value...\n");
return (dict); return (dict);
} }
else if (key[0] != '/') else if (key[0] != '/')
@ -500,18 +613,25 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
_pdfioFileError(pdf, "Invalid dictionary contents."); _pdfioFileError(pdf, "Invalid dictionary contents.");
break; break;
} }
else if (_pdfioDictGetValue(dict, key + 1))
{
_pdfioFileError(pdf, "Duplicate dictionary key '%s'.", key + 1);
return (NULL);
}
// Then get the next value... // Then get the next value...
PDFIO_DEBUG("_pdfioDictRead: Reading value for '%s'.\n", key + 1);
if (!_pdfioValueRead(pdf, obj, tb, &value, depth)) if (!_pdfioValueRead(pdf, obj, tb, &value, depth))
{ {
_pdfioFileError(pdf, "Missing value for dictionary key."); _pdfioFileError(pdf, "Missing value for dictionary key '%s'.", key + 1);
break; break;
} }
if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value)) if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value))
break; break;
// PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key); PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key);
} }
// Dictionary is invalid - pdfioFileClose will free the memory, return NULL // Dictionary is invalid - pdfioFileClose will free the memory, return NULL
@ -707,7 +827,7 @@ pdfioDictSetNull(pdfio_dict_t *dict, // I - Dictionary
bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
pdfioDictSetNumber(pdfio_dict_t *dict, // I - Dictionary pdfioDictSetNumber(pdfio_dict_t *dict, // I - Dictionary
const char *key, // I - Key const char *key, // I - Key
double value) // I - Value double value) // I - Value
{ {
_pdfio_value_t temp; // New value _pdfio_value_t temp; // New value
@ -891,9 +1011,9 @@ _pdfioDictSetValue(
#ifdef DEBUG #ifdef DEBUG
PDFIO_DEBUG("_pdfioDictSetValue(%p): %lu pairs\n", (void *)dict, (unsigned long)dict->num_pairs); PDFIO_DEBUG("_pdfioDictSetValue(%p): %lu pairs\n", (void *)dict, (unsigned long)dict->num_pairs);
PDFIO_DEBUG("_pdfioDictSetValue(%p): ", (void *)dict); // PDFIO_DEBUG("_pdfioDictSetValue(%p): ", (void *)dict);
PDFIO_DEBUG_DICT(dict); // PDFIO_DEBUG_DICT(dict);
PDFIO_DEBUG("\n"); // PDFIO_DEBUG("\n");
#endif // DEBUG #endif // DEBUG
return (true); return (true);

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,12 @@
// //
// PDF object functions for PDFio. // PDF object functions for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
// //
//
// Include necessary headers...
//
#include "pdfio-private.h" #include "pdfio-private.h"
@ -33,8 +29,14 @@ pdfioObjClose(pdfio_obj_t *obj) // I - Object
if (!obj) if (!obj)
return (false); return (false);
// Clear the current object pointer...
obj->pdf->current_obj = NULL;
if (obj->pdf->mode != _PDFIO_MODE_WRITE) if (obj->pdf->mode != _PDFIO_MODE_WRITE)
return (true); // Nothing to do when reading {
// Nothing to do when reading
return (true);
}
// Write what remains for the object... // Write what remains for the object...
if (!obj->offset) if (!obj->offset)
@ -97,7 +99,7 @@ pdfioObjCopy(pdfio_file_t *pdf, // I - PDF file
return (NULL); return (NULL);
if (dstobj->value.type == PDFIO_VALTYPE_DICT) if (dstobj->value.type == PDFIO_VALTYPE_DICT)
_pdfioDictClear(dstobj->value.value.dict, "Length"); pdfioDictClear(dstobj->value.value.dict, "Length");
if (srcobj->stream_offset) if (srcobj->stream_offset)
{ {
@ -165,6 +167,12 @@ pdfioObjCreateStream(
return (NULL); return (NULL);
} }
if (obj->pdf->current_obj)
{
_pdfioFileError(obj->pdf, "Another object (%u) is already open.", (unsigned)obj->pdf->current_obj->number);
return (NULL);
}
// Write the header... // Write the header...
if (!_pdfioDictGetValue(obj->value.value.dict, "Length")) if (!_pdfioDictGetValue(obj->value.value.dict, "Length"))
{ {
@ -193,7 +201,8 @@ pdfioObjCreateStream(
if (!_pdfioFilePuts(obj->pdf, "stream\n")) if (!_pdfioFilePuts(obj->pdf, "stream\n"))
return (NULL); return (NULL);
obj->stream_offset = _pdfioFileTell(obj->pdf); obj->stream_offset = _pdfioFileTell(obj->pdf);
obj->pdf->current_obj = obj;
// Return the new stream... // Return the new stream...
return (_pdfioStreamCreate(obj, length_obj, filter)); return (_pdfioStreamCreate(obj, length_obj, filter));
@ -208,8 +217,13 @@ void
_pdfioObjDelete(pdfio_obj_t *obj) // I - Object _pdfioObjDelete(pdfio_obj_t *obj) // I - Object
{ {
if (obj) if (obj)
{
pdfioStreamClose(obj->stream); pdfioStreamClose(obj->stream);
if (obj->datafree)
(obj->datafree)(obj->data);
}
free(obj); free(obj);
} }
@ -254,6 +268,17 @@ pdfioObjGetDict(pdfio_obj_t *obj) // I - Object
} }
//
// '_pdfioObjGetExtension()' - Get the extension pointer for an object.
//
void * // O - Extension data
_pdfioObjGetExtension(pdfio_obj_t *obj) // I - Object
{
return (obj->data);
}
// //
// 'pdfioObjGetGeneration()' - Get the object's generation number. // 'pdfioObjGetGeneration()' - Get the object's generation number.
// //
@ -308,6 +333,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. // 'pdfioObjGetNumber()' - Get the object's number.
// //
@ -322,8 +367,21 @@ pdfioObjGetNumber(pdfio_obj_t *obj) // I - Object
// //
// 'pdfioObjGetSubtype()' - Get an object's subtype. // '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 pdfioObjGetSubtype(pdfio_obj_t *obj) // I - Object
{ {
pdfio_dict_t *dict; // Object dictionary pdfio_dict_t *dict; // Object dictionary
@ -339,8 +397,21 @@ pdfioObjGetSubtype(pdfio_obj_t *obj) // I - Object
// //
// 'pdfioObjGetType()' - Get an object's type. // '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 pdfioObjGetType(pdfio_obj_t *obj) // I - Object
{ {
pdfio_dict_t *dict; // Object dictionary pdfio_dict_t *dict; // Object dictionary
@ -407,6 +478,9 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
} }
ptr += 3; ptr += 3;
while (*ptr && isspace(*ptr & 255))
ptr ++;
_pdfioFileConsume(obj->pdf, (size_t)(ptr - line)); _pdfioFileConsume(obj->pdf, (size_t)(ptr - line));
// Then grab the object value... // Then grab the object value...
@ -425,15 +499,29 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
return (false); return (false);
} }
PDFIO_DEBUG("_pdfioObjLoad: tb.bufptr=%p, tb.bufend=%p, tb.bufptr[0]=0x%02x, tb.bufptr[1]=0x%02x\n", tb.bufptr, tb.bufend, tb.bufptr[0], tb.bufptr[1]);
_pdfioTokenFlush(&tb); _pdfioTokenFlush(&tb);
if (!strcmp(line, "stream")) if (!strcmp(line, "stream"))
{ {
// Yes, save its location... // Yes, this is an embedded stream so save its location...
obj->stream_offset = _pdfioFileTell(obj->pdf); obj->stream_offset = _pdfioFileTell(obj->pdf);
PDFIO_DEBUG("_pdfioObjLoad: stream_offset=%lu.\n", (unsigned long)obj->stream_offset); PDFIO_DEBUG("_pdfioObjLoad: stream_offset=%lu.\n", (unsigned long)obj->stream_offset);
} }
// Decrypt as needed...
if (obj->pdf->encryption)
{
PDFIO_DEBUG("_pdfioObjLoad: Decrypting value...\n");
if (!_pdfioValueDecrypt(obj->pdf, obj, &obj->value, 0))
{
PDFIO_DEBUG("_pdfioObjLoad: Failed to decrypt.\n");
return (false);
}
}
PDFIO_DEBUG("_pdfioObjLoad: "); PDFIO_DEBUG("_pdfioObjLoad: ");
PDFIO_DEBUG_VALUE(&obj->value); PDFIO_DEBUG_VALUE(&obj->value);
PDFIO_DEBUG("\n"); PDFIO_DEBUG("\n");
@ -454,6 +542,12 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
if (!obj) if (!obj)
return (NULL); return (NULL);
if (obj->pdf->current_obj)
{
_pdfioFileError(obj->pdf, "Another object (%u) is already open.", (unsigned)obj->pdf->current_obj->number);
return (NULL);
}
// Make sure we've loaded the object dictionary... // Make sure we've loaded the object dictionary...
if (!obj->value.type) if (!obj->value.type)
{ {
@ -466,10 +560,27 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
return (NULL); return (NULL);
// Open the stream... // Open the stream...
obj->pdf->current_obj = obj;
return (_pdfioStreamOpen(obj, decode)); return (_pdfioStreamOpen(obj, decode));
} }
//
// '_pdfioObjSetExtension()' - Set extension data for an object.
//
void
_pdfioObjSetExtension(
pdfio_obj_t *obj, // I - Object
void *data, // I - Data
_pdfio_extfree_t datafree) // I - Free function
{
obj->data = data;
obj->datafree = datafree;
}
// //
// 'write_obj_header()' - Write the object header... // 'write_obj_header()' - Write the object header...
// //

View File

@ -1,17 +1,20 @@
// //
// PDF page functions for PDFio. // 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 // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
// //
#include "pdfio-private.h"
// //
// Include necessary headers... // Local functions...
// //
#include "pdfio-private.h" static _pdfio_value_t *get_contents(pdfio_obj_t *page);
// //
@ -47,3 +50,74 @@ pdfioPageCopy(pdfio_file_t *pdf, // I - PDF file
else else
return (_pdfioFileAddPage(pdf, dstpage)); 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. // Private header file for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -9,41 +9,23 @@
#ifndef PDFIO_PRIVATE_H #ifndef PDFIO_PRIVATE_H
# define PDFIO_PRIVATE_H # define PDFIO_PRIVATE_H
//
// Include necessary headers...
//
# ifdef _WIN32 # ifdef _WIN32
/* # define _CRT_SECURE_NO_WARNINGS // Disable bogus VS warnings/errors...
* Disable bogus VS warnings/errors...
*/
# define _CRT_SECURE_NO_WARNINGS
# endif // _WIN32 # endif // _WIN32
# include "pdfio.h" # include "pdfio.h"
# include <stdarg.h> # include <stdarg.h>
# include <stdint.h> # include <stdint.h>
# include <string.h> # include <string.h>
# include <ctype.h>
# include <errno.h> # include <errno.h>
# include <inttypes.h> # include <inttypes.h>
# include <fcntl.h> # include <fcntl.h>
# include <locale.h>
# ifdef _WIN32 # ifdef _WIN32
# include <io.h> # include <io.h>
# include <direct.h> # include <direct.h>
# include <windows.h> // GetTempPathA
/* # define access _access // Map standard POSIX/C99 names
* Microsoft renames the POSIX functions to _name, and introduces
* a broken compatibility layer using the original names. As a result,
* random crashes can occur when, for example, strdup() allocates memory
* from a different heap than used by malloc() and free().
*
* To avoid moronic problems like this, we #define the POSIX function
* names to the corresponding non-standard Microsoft names.
*/
# define access _access
# define close _close # define close _close
# define fileno _fileno # define fileno _fileno
# define lseek _lseek # define lseek _lseek
@ -56,25 +38,20 @@
# define unlink _unlink # define unlink _unlink
# define vsnprintf _vsnprintf # define vsnprintf _vsnprintf
# define write _write # define write _write
# ifndef F_OK
/* # define F_OK 00 // POSIX parameters/flags
* Map various parameters for POSIX... # define W_OK 02
*/ # define R_OK 04
# endif // !F_OK
# define F_OK 00 # define O_RDONLY _O_RDONLY // Map standard POSIX open flags
# define W_OK 02
# define R_OK 04
# define O_RDONLY _O_RDONLY
# define O_WRONLY _O_WRONLY # define O_WRONLY _O_WRONLY
# define O_CREAT _O_CREAT # define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC # define O_TRUNC _O_TRUNC
# define O_BINARY _O_BINARY # define O_BINARY _O_BINARY
# else // !_WIN32 # else // !_WIN32
# include <unistd.h> # include <unistd.h>
# define O_BINARY 0 # define O_BINARY 0 // Map Windows-specific open flag
# endif // _WIN32 # endif // _WIN32
# include <string.h>
# include <ctype.h>
# include <zlib.h> # include <zlib.h>
@ -118,6 +95,9 @@
# define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values # define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values
typedef void (*_pdfio_extfree_t)(void *);
// Extension data free function
typedef enum _pdfio_mode_e // Read/write mode typedef enum _pdfio_mode_e // Read/write mode
{ {
_PDFIO_MODE_READ, // Read a PDF file _PDFIO_MODE_READ, // Read a PDF file
@ -247,6 +227,7 @@ typedef struct _pdfio_objmap_s // PDF object map
struct _pdfio_file_s // PDF file structure struct _pdfio_file_s // PDF file structure
{ {
char *filename; // Filename char *filename; // Filename
struct lconv *loc; // Locale data
char *version; // Version number char *version; // Version number
pdfio_rect_t media_box, // Default MediaBox value pdfio_rect_t media_box, // Default MediaBox value
crop_box; // Default CropBox value crop_box; // Default CropBox value
@ -260,7 +241,8 @@ struct _pdfio_file_s // PDF file structure
pdfio_permission_t permissions; // Access permissions (encrypted PDF files) pdfio_permission_t permissions; // Access permissions (encrypted PDF files)
uint8_t file_key[16], // File encryption key uint8_t file_key[16], // File encryption key
owner_key[32], // Owner encryption key owner_key[32], // Owner encryption key
user_key[32]; // User encryption key user_key[32], // User encryption key
password[32]; // Padded password
size_t file_keylen, // Length of file encryption key size_t file_keylen, // Length of file encryption key
owner_keylen, // Length of owner encryption key owner_keylen, // Length of owner encryption key
user_keylen; // Length of user encryption key user_keylen; // Length of user encryption key
@ -288,8 +270,10 @@ struct _pdfio_file_s // PDF file structure
alloc_dicts; // Allocated dictionaries alloc_dicts; // Allocated dictionaries
pdfio_dict_t **dicts; // Dictionaries pdfio_dict_t **dicts; // Dictionaries
size_t num_objs, // Number of objects size_t num_objs, // Number of objects
alloc_objs; // Allocated objects alloc_objs, // Allocated objects
pdfio_obj_t **objs; // Objects last_obj; // Last object added
pdfio_obj_t **objs, // Objects
*current_obj; // Current object being written/read
size_t num_objmaps, // Number of object maps size_t num_objmaps, // Number of object maps
alloc_objmaps; // Allocated object maps alloc_objmaps; // Allocated object maps
_pdfio_objmap_t *objmaps; // Object maps _pdfio_objmap_t *objmaps; // Object maps
@ -312,6 +296,8 @@ struct _pdfio_obj_s // Object
size_t stream_length; // Length of stream, if any size_t stream_length; // Length of stream, if any
_pdfio_value_t value; // Dictionary/number/etc. value _pdfio_value_t value; // Dictionary/number/etc. value
pdfio_stream_t *stream; // Open stream, if any pdfio_stream_t *stream; // Open stream, if any
void *data; // Extension data, if any
_pdfio_extfree_t datafree; // Free callback for extension data
}; };
struct _pdfio_stream_s // Stream struct _pdfio_stream_s // Stream
@ -340,6 +326,10 @@ struct _pdfio_stream_s // Stream
// Functions... // Functions...
// //
extern double _pdfio_strtod(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
extern ssize_t _pdfio_vsnprintf(pdfio_file_t *pdf, char *buffer, size_t bufsize, const char *format, va_list ap) _PDFIO_INTERNAL;
extern bool _pdfioArrayDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_array_t *a, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL; extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL; extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL; extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL;
@ -363,7 +353,7 @@ extern void _pdfioCryptoSHA256Init(_pdfio_sha256_t *ctx) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Digest) _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 _pdfioCryptoUnlock(pdfio_file_t *pdf, pdfio_password_cb_t password_cb, void *password_data) _PDFIO_INTERNAL;
extern void _pdfioDictClear(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL; extern bool _pdfioDictDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_dict_t *dict, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL; extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL; extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL; extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
@ -390,7 +380,9 @@ extern off_t _pdfioFileTell(pdfio_file_t *pdf) _PDFIO_INTERNAL;
extern bool _pdfioFileWrite(pdfio_file_t *pdf, const void *buffer, size_t bytes) _PDFIO_INTERNAL; extern bool _pdfioFileWrite(pdfio_file_t *pdf, const void *buffer, size_t bytes) _PDFIO_INTERNAL;
extern void _pdfioObjDelete(pdfio_obj_t *obj) _PDFIO_INTERNAL; extern void _pdfioObjDelete(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern void *_pdfioObjGetExtension(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern bool _pdfioObjLoad(pdfio_obj_t *obj) _PDFIO_INTERNAL; extern bool _pdfioObjLoad(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern void _pdfioObjSetExtension(pdfio_obj_t *obj, void *data, _pdfio_extfree_t datafree) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, pdfio_filter_t compression) _PDFIO_INTERNAL; extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, pdfio_filter_t compression) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL; extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
@ -405,9 +397,11 @@ extern void _pdfioTokenPush(_pdfio_token_t *tb, const char *token) _PDFIO_INTER
extern bool _pdfioTokenRead(_pdfio_token_t *tb, char *buffer, size_t bufsize); extern bool _pdfioTokenRead(_pdfio_token_t *tb, char *buffer, size_t bufsize);
extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vdst, pdfio_file_t *pdfsrc, _pdfio_value_t *vsrc) _PDFIO_INTERNAL; extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vdst, pdfio_file_t *pdfsrc, _pdfio_value_t *vsrc) _PDFIO_INTERNAL;
extern bool _pdfioValueDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL; extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioValueDelete(_pdfio_value_t *v) _PDFIO_INTERNAL; extern void _pdfioValueDelete(_pdfio_value_t *v) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL; extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL;
extern bool _pdfioValueWrite(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL; extern bool _pdfioValueWrite(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL;
#endif // !PDFIO_PRIVATE_H #endif // !PDFIO_PRIVATE_H

View File

@ -1,7 +1,7 @@
// //
// SHA-256 functions for PDFio. // SHA-256 functions for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2023 by Michael R Sweet.
// Copyright © 2011 IETF Trust and the persons identified as authors of the // Copyright © 2011 IETF Trust and the persons identified as authors of the
// code. All rights reserved. // code. All rights reserved.
// //
@ -217,7 +217,7 @@ _pdfioCryptoSHA256Append(_pdfio_sha256_t *context, const uint8_t *message_array,
*/ */
void void
_pdfioCryptoSHA256Finish(_pdfio_sha256_t *context, _pdfioCryptoSHA256Finish(_pdfio_sha256_t *context,
uint8_t Message_Digest[SHA256HashSize]) uint8_t *Message_Digest)
{ {
SHA224_256ResultN(context, Message_Digest, SHA256HashSize); SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
} }

View File

@ -1,16 +1,12 @@
// //
// PDF stream functions for PDFio. // PDF stream functions for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
// //
//
// Include necessary headers...
//
#include "pdfio-private.h" #include "pdfio-private.h"
@ -174,6 +170,8 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
done: done:
st->pdf->current_obj = NULL;
free(st->prbuffer); free(st->prbuffer);
free(st->psbuffer); free(st->psbuffer);
free(st); free(st);
@ -364,6 +362,13 @@ pdfioStreamConsume(pdfio_stream_t *st, // I - Stream
// //
// 'pdfioStreamGetToken()' - Read a single PDF token from a stream. // 'pdfioStreamGetToken()' - Read a single PDF token from a stream.
// //
// This function reads a single PDF token from a stream. Operator tokens,
// boolean values, and numbers are returned as-is in the provided string buffer.
// String values start with the opening parenthesis ('(') but have all escaping
// resolved and the terminating parenthesis removed. Hexadecimal string values
// start with the opening angle bracket ('<') and have all whitespace and the
// terminating angle bracket removed.
//
bool // O - `true` on success, `false` on EOF bool // O - `true` on success, `false` on EOF
pdfioStreamGetToken( pdfioStreamGetToken(
@ -372,6 +377,7 @@ pdfioStreamGetToken(
size_t bufsize) // I - Size of string buffer size_t bufsize) // I - Size of string buffer
{ {
_pdfio_token_t tb; // Token buffer/stack _pdfio_token_t tb; // Token buffer/stack
bool ret; // Return value
// Range check input... // Range check input...
@ -381,7 +387,10 @@ pdfioStreamGetToken(
// Read using the token engine... // Read using the token engine...
_pdfioTokenInit(&tb, st->pdf, (_pdfio_tconsume_cb_t)pdfioStreamConsume, (_pdfio_tpeek_cb_t)pdfioStreamPeek, st); _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);
} }
@ -399,6 +408,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
pdfio_stream_t *st; // Stream pdfio_stream_t *st; // Stream
pdfio_dict_t *dict = pdfioObjGetDict(obj); pdfio_dict_t *dict = pdfioObjGetDict(obj);
// Object dictionary // Object dictionary
const char *type; // Object type
PDFIO_DEBUG("_pdfioStreamOpen(obj=%p(%u), decode=%s)\n", obj, (unsigned)obj->number, decode ? "true" : "false"); PDFIO_DEBUG("_pdfioStreamOpen(obj=%p(%u), decode=%s)\n", obj, (unsigned)obj->number, decode ? "true" : "false");
@ -425,7 +435,9 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
return (NULL); return (NULL);
} }
if (obj->pdf->encryption) type = pdfioObjGetType(obj);
if (obj->pdf->encryption && (!type || strcmp(type, "XRef")))
{ {
uint8_t iv[64]; // Initialization vector uint8_t iv[64]; // Initialization vector
size_t ivlen; // Length of initialization vector, if any size_t ivlen; // Length of initialization vector, if any
@ -439,6 +451,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
return (NULL); return (NULL);
} }
PDFIO_DEBUG("_pdfioStreamOpen: ivlen=%d\n", (int)ivlen);
if (ivlen > 0) if (ivlen > 0)
_pdfioFileConsume(st->pdf, ivlen); _pdfioFileConsume(st->pdf, ivlen);
@ -451,11 +464,19 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
// Try to decode/decompress the contents of this object... // Try to decode/decompress the contents of this object...
const char *filter = pdfioDictGetName(dict, "Filter"); const char *filter = pdfioDictGetName(dict, "Filter");
// Filter value // Filter value
pdfio_array_t *fa = pdfioDictGetArray(dict, "Filter");
// Filter array
if (!filter && fa && pdfioArrayGetSize(fa) == 1)
{
// Support single-valued arrays...
filter = pdfioArrayGetName(fa, 0);
}
if (!filter) if (!filter)
{ {
// No single filter name, do we have a compound filter? // No single filter name, do we have a compound filter?
if (pdfioDictGetArray(dict, "Filter")) if (fa)
{ {
// TODO: Implement compound filters... // TODO: Implement compound filters...
_pdfioFileError(st->pdf, "Unsupported compound stream filter."); _pdfioFileError(st->pdf, "Unsupported compound stream filter.");
@ -546,6 +567,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
else else
st->predictor = _PDFIO_PREDICTOR_NONE; st->predictor = _PDFIO_PREDICTOR_NONE;
PDFIO_DEBUG("_pdfioStreamOpen: pos=%ld\n", (long)_pdfioFileTell(st->pdf));
if (sizeof(st->cbuffer) > st->remaining) if (sizeof(st->cbuffer) > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining); rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else else
@ -566,12 +588,6 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
st->flate.next_in = (Bytef *)st->cbuffer; st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes; st->flate.avail_in = (uInt)rbytes;
if (st->cbuffer[0] == 0x0a)
{
st->flate.next_in ++; // Skip newline
st->flate.avail_in --;
}
PDFIO_DEBUG("_pdfioStreamOpen: avail_in=%u, cbuffer=<%02X%02X%02X%02X%02X%02X%02X%02X...>\n", st->flate.avail_in, st->cbuffer[0], st->cbuffer[1], st->cbuffer[2], st->cbuffer[3], st->cbuffer[4], st->cbuffer[5], st->cbuffer[6], st->cbuffer[7]); PDFIO_DEBUG("_pdfioStreamOpen: avail_in=%u, cbuffer=<%02X%02X%02X%02X%02X%02X%02X%02X...>\n", st->flate.avail_in, st->cbuffer[0], st->cbuffer[1], st->cbuffer[2], st->cbuffer[3], st->cbuffer[4], st->cbuffer[5], st->cbuffer[6], st->cbuffer[7]);
if ((status = inflateInit(&(st->flate))) != Z_OK) if ((status = inflateInit(&(st->flate))) != Z_OK)
@ -674,7 +690,7 @@ pdfioStreamPrintf(
// Format the string... // Format the string...
va_start(ap, format); va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap); _pdfio_vsnprintf(st->pdf, buffer, sizeof(buffer), format, ap);
va_end(ap); va_end(ap);
// Write the string... // Write the string...
@ -995,6 +1011,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
size_t bytes) // I - Number of bytes to read size_t bytes) // I - Number of bytes to read
{ {
ssize_t rbytes; // Bytes read ssize_t rbytes; // Bytes read
uInt avail_in, avail_out; // Previous flate values
if (st->filter == PDFIO_FILTER_NONE) if (st->filter == PDFIO_FILTER_NONE)
@ -1049,7 +1066,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK) if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{ {
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status)); _pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1); return (-1);
} }
@ -1100,12 +1117,15 @@ stream_read(pdfio_stream_t *st, // I - Stream
st->flate.avail_in = (uInt)rbytes; st->flate.avail_in = (uInt)rbytes;
} }
avail_in = st->flate.avail_in;
avail_out = st->flate.avail_out;
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK) if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{ {
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status)); _pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1); return (-1);
} }
else if (status == Z_STREAM_END) else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
break; break;
} }
@ -1167,12 +1187,15 @@ stream_read(pdfio_stream_t *st, // I - Stream
st->flate.avail_in = (uInt)rbytes; st->flate.avail_in = (uInt)rbytes;
} }
avail_in = st->flate.avail_in;
avail_out = st->flate.avail_out;
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK) if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{ {
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status)); _pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1); return (-1);
} }
else if (status == Z_STREAM_END) else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
break; break;
} }

View File

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

View File

@ -1,16 +1,12 @@
// //
// PDF token parsing functions for PDFio. // PDF token parsing functions for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2023 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
// //
//
// Include necessary headers...
//
#include "pdfio-private.h" #include "pdfio-private.h"
@ -129,9 +125,20 @@ _pdfioTokenGet(_pdfio_token_t *tb, // I - Token buffer/stack
if (tb->num_tokens > 0) if (tb->num_tokens > 0)
{ {
// Yes, return it... // Yes, return it...
size_t len; // Length of token
tb->num_tokens --; tb->num_tokens --;
strncpy(buffer, tb->tokens[tb->num_tokens], bufsize - 1);
buffer[bufsize - 1] = '\0'; if ((len = strlen(tb->tokens[tb->num_tokens])) > (bufsize - 1))
{
// Value too large...
PDFIO_DEBUG("_pdfioTokenGet(tb=%p, buffer=%p, bufsize=%u): Token '%s' from stack too large.\n", tb, buffer, (unsigned)bufsize, tb->tokens[tb->num_tokens]);
*buffer = '\0';
return (false);
}
memcpy(buffer, tb->tokens[tb->num_tokens], len);
buffer[len] = '\0';
PDFIO_DEBUG("_pdfioTokenGet(tb=%p, buffer=%p, bufsize=%u): Popping '%s' from stack.\n", tb, buffer, (unsigned)bufsize, buffer); PDFIO_DEBUG("_pdfioTokenGet(tb=%p, buffer=%p, bufsize=%u): Popping '%s' from stack.\n", tb, buffer, (unsigned)bufsize, buffer);
@ -201,9 +208,10 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
*bufend, // End of buffer *bufend, // End of buffer
state = '\0'; // Current state state = '\0'; // Current state
bool saw_nul = false; // Did we see a nul character? bool saw_nul = false; // Did we see a nul character?
size_t count = 0; // Number of whitespace/comment bytes
//
// "state" is: // "state" is:
// //
// - '\0' for idle // - '\0' for idle
@ -222,21 +230,45 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
// Skip leading whitespace... // Skip leading whitespace...
while ((ch = get_char(tb)) != EOF) while ((ch = get_char(tb)) != EOF)
{ {
count ++;
if (ch == '%') if (ch == '%')
{ {
// Skip comment // Skip comment
PDFIO_DEBUG("_pdfioTokenRead: Skipping comment...\n");
while ((ch = get_char(tb)) != EOF) while ((ch = get_char(tb)) != EOF)
{ {
count ++;
if (ch == '\n' || ch == '\r') if (ch == '\n' || ch == '\r')
{
break; break;
}
else if (count > 2048)
{
_pdfioFileError(tb->pdf, "Comment too long.");
*bufptr = '\0';
return (false);
}
} }
} }
else if (!isspace(ch)) else if (!isspace(ch))
{
break; break;
}
else if (count > 2048)
{
_pdfioFileError(tb->pdf, "Too much whitespace.");
*bufptr = '\0';
return (false);
}
} }
if (ch == EOF) if (ch == EOF)
{
*bufptr = '\0';
return (false); return (false);
}
// Check for delimiters... // Check for delimiters...
if (strchr(PDFIO_DELIM_CHARS, ch) != NULL) if (strchr(PDFIO_DELIM_CHARS, ch) != NULL)
@ -256,6 +288,8 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
*bufptr++ = (char)ch; *bufptr++ = (char)ch;
} }
PDFIO_DEBUG("_pdfioTokenRead: state='%c'\n", state);
switch (state) switch (state)
{ {
case '(' : // Literal string case '(' : // Literal string
@ -347,6 +381,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{ {
// Out of space // Out of space
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false); return (false);
} }
} }
@ -354,6 +389,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
if (ch != ')') if (ch != ')')
{ {
_pdfioFileError(tb->pdf, "Unterminated string literal."); _pdfioFileError(tb->pdf, "Unterminated string literal.");
*bufptr = '\0';
return (false); return (false);
} }
@ -373,6 +409,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{ {
// Out of space... // Out of space...
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false); return (false);
} }
@ -406,9 +443,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{ {
// Out of space... // Out of space...
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false); return (false);
} }
} }
if (ch == '\r')
{
// Look for a trailing LF
if ((ch = get_char(tb)) != EOF && ch != '\n')
tb->bufptr --;
}
break; break;
case 'N' : // number case 'N' : // number
@ -417,6 +462,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
if (!isdigit(ch) && ch != '.') if (!isdigit(ch) && ch != '.')
{ {
// End of number... // End of number...
PDFIO_DEBUG("_pdfioTokenRead: End of number with ch=0x%02x\n", ch);
tb->bufptr --; tb->bufptr --;
break; break;
} }
@ -429,6 +475,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{ {
// Out of space... // Out of space...
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false); return (false);
} }
} }
@ -455,12 +502,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
if (!isxdigit(tch & 255)) if (!isxdigit(tch & 255))
{ {
_pdfioFileError(tb->pdf, "Bad # escape in name."); _pdfioFileError(tb->pdf, "Bad # escape in name.");
*bufptr = '\0';
return (false); return (false);
} }
else if (isdigit(tch)) else if (isdigit(tch))
{
ch = ((ch & 255) << 4) | (tch - '0'); ch = ((ch & 255) << 4) | (tch - '0');
}
else else
{
ch = ((ch & 255) << 4) | (tolower(tch) - 'a' + 10); ch = ((ch & 255) << 4) | (tolower(tch) - 'a' + 10);
}
} }
} }
@ -472,9 +524,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{ {
// Out of space // Out of space
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false); return (false);
} }
} }
if (bufptr == (buffer + 1))
{
_pdfioFileError(tb->pdf, "Empty name.");
*bufptr = '\0';
return (false);
}
break; break;
case '<' : // Potential hex string case '<' : // Potential hex string
@ -484,12 +544,22 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
*bufptr++ = (char)ch; *bufptr++ = (char)ch;
break; break;
} }
else if (ch == '>')
{
// Issue #46: Empty hex string from Microsoft PDF generator; treat as
// empty literal string...
*buffer = '(';
break;
}
else if (!isspace(ch & 255) && !isxdigit(ch & 255)) else if (!isspace(ch & 255) && !isxdigit(ch & 255))
{ {
_pdfioFileError(tb->pdf, "Syntax error: '<%c'", ch); _pdfioFileError(tb->pdf, "Syntax error: '<%c'", ch);
*bufptr = '\0';
return (false); return (false);
} }
count = 0;
do do
{ {
if (isxdigit(ch)) if (isxdigit(ch))
@ -498,25 +568,39 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{ {
// Hex digit // Hex digit
*bufptr++ = (char)ch; *bufptr++ = (char)ch;
count = 0;
} }
else else
{ {
// Too large // Too large
_pdfioFileError(tb->pdf, "Token too large."); _pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false); return (false);
} }
} }
else if (!isspace(ch)) else if (!isspace(ch))
{ {
_pdfioFileError(tb->pdf, "Invalid hex string character '%c'.", ch); _pdfioFileError(tb->pdf, "Invalid hex string character '%c'.", ch);
*bufptr = '\0';
return (false); return (false);
} }
else
{
count ++;
if (count > 2048)
{
_pdfioFileError(tb->pdf, "Too much whitespace.");
*bufptr = '\0';
return (false);
}
}
} }
while ((ch = get_char(tb)) != EOF && ch != '>'); while ((ch = get_char(tb)) != EOF && ch != '>');
if (ch == EOF) if (ch == EOF)
{ {
_pdfioFileError(tb->pdf, "Unterminated hex string."); _pdfioFileError(tb->pdf, "Unterminated hex string.");
*bufptr = '\0';
return (false); return (false);
} }
break; break;
@ -529,6 +613,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
else else
{ {
_pdfioFileError(tb->pdf, "Syntax error: '>%c'.", ch); _pdfioFileError(tb->pdf, "Syntax error: '>%c'.", ch);
*bufptr = '\0';
return (false); return (false);
} }
break; break;

View File

@ -1,17 +1,20 @@
// //
// PDF value functions for PDFio. // PDF value functions for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
// //
#include "pdfio-private.h"
// //
// Include necessary headers... // Local functions...
// //
#include "pdfio-private.h" static time_t get_date_time(const char *s);
// //
@ -109,6 +112,101 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file
} }
//
// '_pdfioValueDecrypt()' - Decrypt a value.
//
bool // O - `true` on success, `false` on error
_pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object
_pdfio_value_t *v, // I - Value
size_t depth)// I - Depth
{
_pdfio_crypto_ctx_t ctx; // Decryption context
_pdfio_crypto_cb_t cb; // Decryption callback
size_t ivlen; // Number of initialization vector bytes
uint8_t temp[32768]; // Temporary buffer for decryption
size_t templen; // Number of actual data bytes
time_t timeval; // Date/time value
if (depth > PDFIO_MAX_DEPTH)
{
_pdfioFileError(pdf, "Value too deep.");
return (false);
}
switch (v->type)
{
default :
// Do nothing
break;
case PDFIO_VALTYPE_ARRAY :
return (_pdfioArrayDecrypt(pdf, obj, v->value.array, depth + 1));
break;
case PDFIO_VALTYPE_DICT :
return (_pdfioDictDecrypt(pdf, obj, v->value.dict, depth + 1));
break;
case PDFIO_VALTYPE_BINARY :
// Decrypt the binary string...
if (v->value.binary.datalen > (sizeof(temp) - 32))
{
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
return (false);
}
ivlen = v->value.binary.datalen;
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL)
return (false);
templen = (cb)(&ctx, temp, v->value.binary.data + ivlen, v->value.binary.datalen - ivlen);
// Copy the decrypted string back to the value and adjust the length...
memcpy(v->value.binary.data, temp, templen);
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128)
v->value.binary.datalen = templen - temp[templen - 1];
else
v->value.binary.datalen = templen;
break;
case PDFIO_VALTYPE_STRING :
// Decrypt regular string...
templen = strlen(v->value.string);
if (templen > (sizeof(temp) - 33))
{
_pdfioFileError(pdf, "Unable to read encrypted string - too long.");
return (false);
}
ivlen = templen;
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, (uint8_t *)v->value.string, &ivlen)) == NULL)
return (false);
templen = (cb)(&ctx, temp, (uint8_t *)v->value.string + ivlen, templen - ivlen);
temp[templen] = '\0';
if ((timeval = get_date_time((char *)temp)) != 0)
{
// Change the type to date...
v->type = PDFIO_VALTYPE_DATE;
v->value.date = timeval;
}
else
{
// Copy the decrypted string back to the value...
v->value.string = pdfioStringCreate(pdf, (char *)temp);
}
break;
}
return (true);
}
// //
// '_pdfioValueDebug()' - Print the contents of a value. // '_pdfioValueDebug()' - Print the contents of a value.
// //
@ -117,6 +215,9 @@ void
_pdfioValueDebug(_pdfio_value_t *v, // I - Value _pdfioValueDebug(_pdfio_value_t *v, // I - Value
FILE *fp) // I - Output file FILE *fp) // I - Output file
{ {
if (!v)
return;
switch (v->type) switch (v->type)
{ {
case PDFIO_VALTYPE_ARRAY : case PDFIO_VALTYPE_ARRAY :
@ -200,6 +301,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
size_t depth) // I - Depth of value size_t depth) // I - Depth of value
{ {
char token[32768]; // Token buffer char token[32768]; // Token buffer
time_t timeval; // Date/time value
#ifdef DEBUG #ifdef DEBUG
static const char * const valtypes[] = static const char * const valtypes[] =
{ {
@ -219,7 +321,6 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v); PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v);
(void)obj; // TODO: Implement decryption
if (!_pdfioTokenGet(tb, token, sizeof(token))) if (!_pdfioTokenGet(tb, token, sizeof(token)))
return (NULL); return (NULL);
@ -250,72 +351,10 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL) if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
return (NULL); return (NULL);
} }
else if (!strncmp(token, "(D:", 3)) else if (!strncmp(token, "(D:", 3) && (timeval = get_date_time(token + 1)) != 0)
{ {
// Possible date value of the form: v->type = PDFIO_VALTYPE_DATE;
// v->value.date = timeval;
// (D:YYYYMMDDhhmmssZ)
// (D:YYYYMMDDhhmmss+HH'mm)
// (D:YYYYMMDDhhmmss-HH'mm)
//
int i; // Looping var
struct tm dateval; // Date value
int offset; // Date offset
for (i = 3; i < 17; i ++)
{
if (!isdigit(token[i] & 255))
break;
}
if (i >= 17)
{
if (token[i] == 'Z')
{
i ++;
}
else if (token[i] == '-' || token[i] == '+')
{
if (isdigit(token[i + 1] & 255) && isdigit(token[i + 2] & 255) && token[i + 3] == '\'' && isdigit(token[i + 4] & 255) && isdigit(token[i + 5] & 255))
{
i += 6;
if (token[i] == '\'')
i ++;
}
}
}
if (token[i])
{
// Just a string...
v->type = PDFIO_VALTYPE_STRING;
v->value.string = pdfioStringCreate(pdf, token + 1);
}
else
{
// Date value...
memset(&dateval, 0, sizeof(dateval));
dateval.tm_year = (token[3] - '0') * 1000 + (token[4] - '0') * 100 + (token[5] - '0') * 10 + token[6] - '0' - 1900;
dateval.tm_mon = (token[7] - '0') * 10 + token[8] - '0' - 1;
dateval.tm_mday = (token[9] - '0') * 10 + token[10] - '0';
dateval.tm_hour = (token[11] - '0') * 10 + token[12] - '0';
dateval.tm_min = (token[13] - '0') * 10 + token[14] - '0';
dateval.tm_sec = (token[15] - '0') * 10 + token[16] - '0';
if (token[17] == 'Z')
{
offset = 0;
}
else
{
offset = (token[18] - '0') * 600 + (token[19] - '0') * 60 + (token[20] - '0') * 10 + token[21] - '0';
if (token[17] == '-')
offset = -offset;
}
v->type = PDFIO_VALTYPE_DATE;
v->value.date = mktime(&dateval) + offset;
}
} }
else if (token[0] == '(') else if (token[0] == '(')
{ {
@ -461,7 +500,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
// If we get here, we have a number... // If we get here, we have a number...
v->type = PDFIO_VALTYPE_NUMBER; v->type = PDFIO_VALTYPE_NUMBER;
v->value.number = (double)strtod(token, NULL); v->value.number = _pdfio_strtod(pdf, token);
} }
else if (!strcmp(token, "true") || !strcmp(token, "false")) else if (!strcmp(token, "true") || !strcmp(token, "false"))
{ {
@ -702,3 +741,85 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
return (false); return (false);
} }
//
// 'get_date_time()' - Convert PDF date/time value to time_t.
//
static time_t // O - Time in seconds
get_date_time(const char *s) // I - PDF date/time value
{
int i; // Looping var
struct tm dateval; // Date value
int offset; // Date offset
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);
}

41
pdfio.h
View File

@ -1,7 +1,7 @@
// //
// Public header file for PDFio. // Public header file for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -9,27 +9,23 @@
#ifndef PDFIO_H #ifndef PDFIO_H
# define PDFIO_H # define PDFIO_H
//
// Include necessary headers...
//
# include <stdio.h> # include <stdio.h>
# include <stdlib.h> # include <stdlib.h>
# include <stdbool.h> # include <stdbool.h>
# include <sys/types.h> # include <sys/types.h>
# include <time.h> # include <time.h>
//
// C++ magic...
//
# ifdef __cplusplus # ifdef __cplusplus
extern "C" { extern "C" {
# endif // __cplusplus # endif // __cplusplus
//
// Version number...
//
# define PDFIO_VERSION "1.4.0"
// //
// Visibility and other annotations... // Visibility and other annotations...
// //
@ -37,9 +33,11 @@ extern "C" {
# if defined(__has_extension) || defined(__GNUC__) # if defined(__has_extension) || defined(__GNUC__)
# define _PDFIO_PUBLIC __attribute__ ((visibility("default"))) # define _PDFIO_PUBLIC __attribute__ ((visibility("default")))
# define _PDFIO_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b))) # define _PDFIO_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b)))
# define _PDFIO_DEPRECATED __attribute__ ((deprecated)) _PDFIO_PUBLIC
# else # else
# define _PDFIO_PUBLIC # define _PDFIO_PUBLIC
# define _PDFIO_FORMAT(a,b) # define _PDFIO_FORMAT(a,b)
# define _PDFIO_DEPRECATED
# endif // __has_extension || __GNUC__ # endif // __has_extension || __GNUC__
@ -55,6 +53,8 @@ typedef struct _pdfio_array_s pdfio_array_t;
// Array of PDF values // Array of PDF values
typedef struct _pdfio_dict_s pdfio_dict_t; typedef struct _pdfio_dict_s pdfio_dict_t;
// Key/value dictionary // Key/value dictionary
typedef bool (*pdfio_dict_cb_t)(pdfio_dict_t *dict, const char *key, void *cb_data);
// Dictionary iterator callback
typedef struct _pdfio_file_s pdfio_file_t; typedef struct _pdfio_file_s pdfio_file_t;
// PDF file // PDF file
typedef bool (*pdfio_error_cb_t)(pdfio_file_t *pdf, const char *message, void *data); typedef bool (*pdfio_error_cb_t)(pdfio_file_t *pdf, const char *message, void *data);
@ -65,7 +65,7 @@ typedef enum pdfio_encryption_e // PDF encryption modes
PDFIO_ENCRYPTION_RC4_40, // 40-bit RC4 encryption (PDF 1.3) PDFIO_ENCRYPTION_RC4_40, // 40-bit RC4 encryption (PDF 1.3)
PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4) PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4)
PDFIO_ENCRYPTION_AES_128, // 128-bit AES encryption (PDF 1.6) PDFIO_ENCRYPTION_AES_128, // 128-bit AES encryption (PDF 1.6)
PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0) PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0) @exclude all@
} pdfio_encryption_t; } pdfio_encryption_t;
typedef enum pdfio_filter_e // Compression/decompression filters for streams typedef enum pdfio_filter_e // Compression/decompression filters for streams
{ {
@ -151,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 size_t pdfioArrayGetSize(pdfio_array_t *a) _PDFIO_PUBLIC;
extern const char *pdfioArrayGetString(pdfio_array_t *a, size_t n) _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 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 *pdfioDictCopy(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioDictCreate(pdfio_file_t *pdf) _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; extern pdfio_array_t *pdfioDictGetArray(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
@ -159,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 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 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 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 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 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_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 pdfio_rect_t *pdfioDictGetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *rect) _PDFIO_PUBLIC;
extern const char *pdfioDictGetString(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC; extern const char *pdfioDictGetString(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern pdfio_valtype_t pdfioDictGetType(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC; extern pdfio_valtype_t pdfioDictGetType(pdfio_dict_t *dict, const char *key) _PDFIO_PUBLIC;
extern void pdfioDictIterateKeys(pdfio_dict_t *dict, pdfio_dict_cb_t cb, void *cb_data) _PDFIO_PUBLIC;
extern bool pdfioDictSetArray(pdfio_dict_t *dict, const char *key, pdfio_array_t *value) _PDFIO_PUBLIC; extern bool pdfioDictSetArray(pdfio_dict_t *dict, const char *key, pdfio_array_t *value) _PDFIO_PUBLIC;
extern bool pdfioDictSetBinary(pdfio_dict_t *dict, const char *key, const unsigned char *value, size_t valuelen) _PDFIO_PUBLIC; extern bool pdfioDictSetBinary(pdfio_dict_t *dict, const char *key, const unsigned char *value, size_t valuelen) _PDFIO_PUBLIC;
extern bool pdfioDictSetBoolean(pdfio_dict_t *dict, const char *key, bool value) _PDFIO_PUBLIC; extern bool pdfioDictSetBoolean(pdfio_dict_t *dict, const char *key, bool value) _PDFIO_PUBLIC;
@ -181,12 +186,17 @@ extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char
extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC; extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateArrayObj(pdfio_file_t *pdf, pdfio_array_t *array) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateArrayObj(pdfio_file_t *pdf, pdfio_array_t *array) _PDFIO_PUBLIC;
extern pdfio_obj_t *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_obj_t *pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreateOutput(pdfio_output_cb_t output_cb, void *output_ctx, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC; extern pdfio_file_t *pdfioFileCreateOutput(pdfio_output_cb_t output_cb, void *output_ctx, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
// TODO: Add number, array, string, etc. versions of pdfioFileCreateObject? // TODO: Add number, array, string, etc. versions of pdfioFileCreateObject?
extern pdfio_stream_t *pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC; extern pdfio_stream_t *pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateStringObj(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreateTemporary(char *buffer, size_t bufsize, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC;
extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioFileGetCatalog(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC;
@ -217,6 +227,7 @@ extern pdfio_array_t *pdfioObjGetArray(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioObjGetDict(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 unsigned short pdfioObjGetGeneration(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern size_t pdfioObjGetLength(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 size_t pdfioObjGetNumber(pdfio_obj_t *obj) _PDFIO_PUBLIC;
extern const char *pdfioObjGetSubtype(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; extern const char *pdfioObjGetType(pdfio_obj_t *obj) _PDFIO_PUBLIC;
@ -240,10 +251,6 @@ extern char *pdfioStringCreate(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC
extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_PUBLIC; extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_PUBLIC;
//
// C++ magic...
//
# ifdef __cplusplus # ifdef __cplusplus
} }
# endif // __cplusplus # endif // __cplusplus

View File

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

View File

@ -87,7 +87,7 @@
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0rc1";WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
@ -101,7 +101,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0rc1";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
@ -115,7 +115,7 @@
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0rc1";_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
@ -130,7 +130,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0rc1";NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>

View File

@ -372,7 +372,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.0.0; CURRENT_PROJECT_VERSION = 1.1.2;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
@ -381,7 +381,6 @@
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)", "$(inherited)",
"DEBUG=1", "DEBUG=1",
"'PDFIO_VERSION=\"$(CURRENT_PROJECT_VERSION)\"'",
); );
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -397,7 +396,7 @@
GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES; GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
@ -450,7 +449,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.0.0; CURRENT_PROJECT_VERSION = 1.1.2;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
@ -459,7 +458,6 @@
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)", "$(inherited)",
"'PDFIO_VERSION=\"$(CURRENT_PROJECT_VERSION)\"'",
); );
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -475,7 +473,7 @@
GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES; GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
RUN_CLANG_STATIC_ANALYZER = YES; RUN_CLANG_STATIC_ANALYZER = YES;
@ -515,7 +513,7 @@
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0; GCC_OPTIMIZATION_LEVEL = 0;
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpdfio; PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpdfio;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -529,7 +527,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14; MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpdfio; PRODUCT_BUNDLE_IDENTIFIER = org.msweet.testpdfio;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

View File

@ -1,7 +1,8 @@
LIBRARY pdfio1 LIBRARY pdfio1
VERSION 1.0 VERSION 1.4
EXPORTS EXPORTS
_pdfioArrayDebug _pdfioArrayDebug
_pdfioArrayDecrypt
_pdfioArrayDelete _pdfioArrayDelete
_pdfioArrayGetValue _pdfioArrayGetValue
_pdfioArrayRead _pdfioArrayRead
@ -22,8 +23,8 @@ _pdfioCryptoSHA256Append
_pdfioCryptoSHA256Finish _pdfioCryptoSHA256Finish
_pdfioCryptoSHA256Init _pdfioCryptoSHA256Init
_pdfioCryptoUnlock _pdfioCryptoUnlock
_pdfioDictClear
_pdfioDictDebug _pdfioDictDebug
_pdfioDictDecrypt
_pdfioDictDelete _pdfioDictDelete
_pdfioDictGetValue _pdfioDictGetValue
_pdfioDictRead _pdfioDictRead
@ -47,7 +48,9 @@ _pdfioFileSeek
_pdfioFileTell _pdfioFileTell
_pdfioFileWrite _pdfioFileWrite
_pdfioObjDelete _pdfioObjDelete
_pdfioObjGetExtension
_pdfioObjLoad _pdfioObjLoad
_pdfioObjSetExtension
_pdfioStreamCreate _pdfioStreamCreate
_pdfioStreamOpen _pdfioStreamOpen
_pdfioStringIsAllocated _pdfioStringIsAllocated
@ -59,9 +62,12 @@ _pdfioTokenPush
_pdfioTokenRead _pdfioTokenRead
_pdfioValueCopy _pdfioValueCopy
_pdfioValueDebug _pdfioValueDebug
_pdfioValueDecrypt
_pdfioValueDelete _pdfioValueDelete
_pdfioValueRead _pdfioValueRead
_pdfioValueWrite _pdfioValueWrite
_pdfio_strtod
_pdfio_vsnprintf
pdfioArrayAppendArray pdfioArrayAppendArray
pdfioArrayAppendBinary pdfioArrayAppendBinary
pdfioArrayAppendBoolean pdfioArrayAppendBoolean
@ -89,6 +95,7 @@ pdfioArrayGetObj
pdfioArrayGetSize pdfioArrayGetSize
pdfioArrayGetString pdfioArrayGetString
pdfioArrayGetType pdfioArrayGetType
pdfioArrayRemove
pdfioContentClip pdfioContentClip
pdfioContentDrawImage pdfioContentDrawImage
pdfioContentFill pdfioContentFill
@ -101,6 +108,7 @@ pdfioContentPathClose
pdfioContentPathCurve pdfioContentPathCurve
pdfioContentPathCurve13 pdfioContentPathCurve13
pdfioContentPathCurve23 pdfioContentPathCurve23
pdfioContentPathEnd
pdfioContentPathLineTo pdfioContentPathLineTo
pdfioContentPathMoveTo pdfioContentPathMoveTo
pdfioContentPathRect pdfioContentPathRect
@ -135,12 +143,17 @@ pdfioContentSetTextXScaling
pdfioContentStroke pdfioContentStroke
pdfioContentTextBegin pdfioContentTextBegin
pdfioContentTextEnd pdfioContentTextEnd
pdfioContentTextMeasure
pdfioContentTextMoveLine pdfioContentTextMoveLine
pdfioContentTextMoveTo pdfioContentTextMoveTo
pdfioContentTextNewLine
pdfioContentTextNewLineShow
pdfioContentTextNewLineShowf
pdfioContentTextNextLine pdfioContentTextNextLine
pdfioContentTextShow pdfioContentTextShow
pdfioContentTextShowJustified pdfioContentTextShowJustified
pdfioContentTextShowf pdfioContentTextShowf
pdfioDictClear
pdfioDictCopy pdfioDictCopy
pdfioDictCreate pdfioDictCreate
pdfioDictGetArray pdfioDictGetArray
@ -148,12 +161,15 @@ pdfioDictGetBinary
pdfioDictGetBoolean pdfioDictGetBoolean
pdfioDictGetDate pdfioDictGetDate
pdfioDictGetDict pdfioDictGetDict
pdfioDictGetKey
pdfioDictGetName pdfioDictGetName
pdfioDictGetNumPairs
pdfioDictGetNumber pdfioDictGetNumber
pdfioDictGetObj pdfioDictGetObj
pdfioDictGetRect pdfioDictGetRect
pdfioDictGetString pdfioDictGetString
pdfioDictGetType pdfioDictGetType
pdfioDictIterateKeys
pdfioDictSetArray pdfioDictSetArray
pdfioDictSetBinary pdfioDictSetBinary
pdfioDictSetBoolean pdfioDictSetBoolean
@ -174,11 +190,16 @@ pdfioFileCreateFontObjFromFile
pdfioFileCreateICCObjFromFile pdfioFileCreateICCObjFromFile
pdfioFileCreateImageObjFromData pdfioFileCreateImageObjFromData
pdfioFileCreateImageObjFromFile pdfioFileCreateImageObjFromFile
pdfioFileCreateNameObj
pdfioFileCreateNumberObj
pdfioFileCreateObj pdfioFileCreateObj
pdfioFileCreateOutput pdfioFileCreateOutput
pdfioFileCreatePage pdfioFileCreatePage
pdfioFileCreateStringObj
pdfioFileCreateTemporary
pdfioFileFindObj pdfioFileFindObj
pdfioFileGetAuthor pdfioFileGetAuthor
pdfioFileGetCatalog
pdfioFileGetCreationDate pdfioFileGetCreationDate
pdfioFileGetCreator pdfioFileGetCreator
pdfioFileGetID pdfioFileGetID
@ -211,6 +232,7 @@ pdfioObjGetArray
pdfioObjGetDict pdfioObjGetDict
pdfioObjGetGeneration pdfioObjGetGeneration
pdfioObjGetLength pdfioObjGetLength
pdfioObjGetName
pdfioObjGetNumber pdfioObjGetNumber
pdfioObjGetSubtype pdfioObjGetSubtype
pdfioObjGetType pdfioObjGetType
@ -219,6 +241,8 @@ pdfioPageCopy
pdfioPageDictAddColorSpace pdfioPageDictAddColorSpace
pdfioPageDictAddFont pdfioPageDictAddFont
pdfioPageDictAddImage pdfioPageDictAddImage
pdfioPageGetNumStreams
pdfioPageOpenStream
pdfioStreamClose pdfioStreamClose
pdfioStreamConsume pdfioStreamConsume
pdfioStreamGetToken pdfioStreamGetToken

View File

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

View File

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

95
pdfiototext.c Normal file
View File

@ -0,0 +1,95 @@
//
// PDF to text program for PDFio.
//
// Copyright © 2022 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// Usage:
//
// ./pdfiototext FILENAME.pdf > FILENAME.txt
//
#include "pdfio.h"
#include <string.h>
//
// 'main()' - Main entry.
//
int // O - Exit status
main(int argc, // I - Number of command-line arguments
char *argv[]) // I - Command-line arguments
{
pdfio_file_t *file; // PDF file
size_t i, j, // Looping vars
num_pages, // Number of pages
num_streams; // Number of streams for page
pdfio_obj_t *obj; // Current page object
pdfio_stream_t *st; // Current page content stream
char buffer[1024]; // String buffer
bool first; // First string token?
// Verify command-line arguments...
if (argc != 2)
{
puts("Usage: pdfiototext FILENAME.pdf > FILENAME.txt");
return (1);
}
// Open the PDF file...
if ((file = pdfioFileOpen(argv[1], NULL, NULL, NULL, NULL)) == NULL)
return (1);
// printf("%s: %u pages\n", argv[1], (unsigned)pdfioFileGetNumPages(file));
// Try grabbing content from all of the pages...
for (i = 0, num_pages = pdfioFileGetNumPages(file); i < num_pages; i ++)
{
if ((obj = pdfioFileGetPage(file, i)) == NULL)
continue;
num_streams = pdfioPageGetNumStreams(obj);
// printf("%s: page%u=%p, num_streams=%u\n", argv[1], (unsigned)i, obj, (unsigned)num_streams);
for (j = 0; j < num_streams; j ++)
{
if ((st = pdfioPageOpenStream(obj, j, true)) == NULL)
continue;
// printf("%s: page%u st%u=%p\n", argv[1], (unsigned)i, (unsigned)j, st);
first = true;
while (pdfioStreamGetToken(st, buffer, sizeof(buffer)))
{
if (buffer[0] == '(')
{
if (first)
first = false;
else
putchar(' ');
fputs(buffer + 1, stdout);
}
else if (!strcmp(buffer, "Td") || !strcmp(buffer, "TD") || !strcmp(buffer, "T*") || !strcmp(buffer, "\'") || !strcmp(buffer, "\""))
{
putchar('\n');
first = true;
}
}
if (!first)
putchar('\n');
pdfioStreamClose(st);
}
}
pdfioFileClose(file);
return (0);
}

16
runtests.bat Normal file
View File

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

View File

@ -1,7 +1,7 @@
// //
// Test program for PDFio. // Test program for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -10,16 +10,13 @@
// //
// ./testpdfio // ./testpdfio
// //
// ./testpdfio FILENAME [OBJECT-NUMBER] [FILENAME [OBJECT-NUMBER]] ... // ./testpdfio [--verbose] FILENAME [OBJECT-NUMBER] [FILENAME [OBJECT-NUMBER]] ...
//
//
// Include necessary headers...
// //
#include "pdfio-private.h" #include "pdfio-private.h"
#include "pdfio-content.h" #include "pdfio-content.h"
#include <math.h> #include <math.h>
#include <locale.h>
#ifndef M_PI #ifndef M_PI
# define M_PI 3.14159265358979323846264338327950288 # define M_PI 3.14159265358979323846264338327950288
#endif // M_PI #endif // M_PI
@ -30,27 +27,29 @@
// //
static int do_crypto_tests(void); static int do_crypto_tests(void);
static int do_test_file(const char *filename, int objnum, bool verbose); static int do_test_file(const char *filename, int objnum, const char *password, bool verbose);
static int do_unit_tests(void); static int do_unit_tests(void);
static int draw_image(pdfio_stream_t *st, const char *name, double x, double y, double w, double h, const char *label); static int draw_image(pdfio_stream_t *st, const char *name, double x, double y, double w, double h, const char *label);
static bool error_cb(pdfio_file_t *pdf, const char *message, bool *error); static bool error_cb(pdfio_file_t *pdf, const char *message, bool *error);
static bool iterate_cb(pdfio_dict_t *dict, const char *key, void *cb_data);
static ssize_t output_cb(int *fd, const void *buffer, size_t bytes); static ssize_t output_cb(int *fd, const void *buffer, size_t bytes);
static const char *password_cb(void *data, const char *filename); static const char *password_cb(void *data, const char *filename);
static int read_unit_file(const char *filename, size_t num_pages, size_t first_image, bool is_output); static int read_unit_file(const char *filename, size_t num_pages, size_t first_image, bool is_output);
static ssize_t token_consume_cb(const char **s, size_t bytes); static ssize_t token_consume_cb(const char **s, size_t bytes);
static ssize_t token_peek_cb(const char **s, char *buffer, size_t bytes); static ssize_t token_peek_cb(const char **s, char *buffer, size_t bytes);
static int usage(FILE *fp);
static int verify_image(pdfio_file_t *pdf, size_t number); static int verify_image(pdfio_file_t *pdf, size_t number);
static int write_alpha_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font); static int write_alpha_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_color_patch(pdfio_stream_t *st, bool device); static int write_color_patch(pdfio_stream_t *st, bool device);
static int write_color_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font); static int write_color_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_font_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font, bool unicode); static int write_font_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font, const char *textfontfile, bool unicode);
static int write_header_footer(pdfio_stream_t *st, const char *title, int number); static int write_header_footer(pdfio_stream_t *st, const char *title, int number);
static pdfio_obj_t *write_image_object(pdfio_file_t *pdf, _pdfio_predictor_t predictor); static pdfio_obj_t *write_image_object(pdfio_file_t *pdf, _pdfio_predictor_t predictor);
static int write_images_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font); static int write_images_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_jpeg_test(pdfio_file_t *pdf, const char *title, int number, pdfio_obj_t *font, pdfio_obj_t *image); static int write_jpeg_test(pdfio_file_t *pdf, const char *title, int number, pdfio_obj_t *font, pdfio_obj_t *image);
static int write_png_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font); static int write_png_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_text_test(pdfio_file_t *pdf, int first_page, pdfio_obj_t *font, const char *filename); static int write_text_test(pdfio_file_t *pdf, int first_page, pdfio_obj_t *font, const char *filename);
static int write_unit_file(pdfio_file_t *inpdf, pdfio_file_t *outpdf, size_t *num_pages, size_t *first_image); static int write_unit_file(pdfio_file_t *inpdf, const char *outname, pdfio_file_t *outpdf, size_t *num_pages, size_t *first_image);
// //
@ -61,20 +60,33 @@ int // O - Exit status
main(int argc, // I - Number of command-line arguments main(int argc, // I - Number of command-line arguments
char *argv[]) // I - Command-line arguments char *argv[]) // I - Command-line arguments
{ {
int ret = 0; // Return value int ret = 0; // Return value
if (argc > 1) if (argc > 1)
{ {
int i; // Looping var int i; // Looping var
const char *password = NULL; // Password
bool verbose = false; // Be verbose? bool verbose = false; // Be verbose?
for (i = 1; i < argc; i ++) for (i = 1; i < argc; i ++)
{ {
if (!strcmp(argv[i], "--help")) if (!strcmp(argv[i], "--help"))
{ {
puts("Usage: ./testpdfio [--help] [--verbose] [filename [objnum] ...]"); return (usage(stdout));
return (0); }
else if (!strcmp(argv[i], "--password"))
{
i ++;
if (i < argc)
{
password = argv[i];
}
else
{
fputs("testpdfio: Missing password after '--password'.\n", stderr);
return (usage(stderr));
}
} }
else if (!strcmp(argv[i], "--verbose")) else if (!strcmp(argv[i], "--verbose"))
{ {
@ -82,24 +94,27 @@ main(int argc, // I - Number of command-line arguments
} }
else if (argv[i][0] == '-') else if (argv[i][0] == '-')
{ {
printf("Unknown option '%s'.\n\n", argv[i]); fprintf(stderr, "testpdfio: Unknown option '%s'.\n", argv[i]);
puts("Usage: ./testpdfio [--help] [--verbose] [filename [objnum] ...]"); return (usage(stderr));
return (1);
} }
else if ((i + 1) < argc && isdigit(argv[i + 1][0] & 255)) else if ((i + 1) < argc && isdigit(argv[i + 1][0] & 255))
{ {
// filename.pdf object-number // filename.pdf object-number
if (do_test_file(argv[i], atoi(argv[i + 1]), verbose)) if (do_test_file(argv[i], atoi(argv[i + 1]), password, verbose))
ret = 1; ret = 1;
i ++; i ++;
} }
else if (do_test_file(argv[i], 0, verbose)) else if (do_test_file(argv[i], 0, password, verbose))
{
ret = 1; ret = 1;
}
} }
} }
else else
{ {
fprintf(stderr, "testpdfio: Test locale is \"%s\".\n", setlocale(LC_ALL, getenv("LANG")));
#if _WIN32 #if _WIN32
// Windows puts executables in Platform/Configuration subdirs... // Windows puts executables in Platform/Configuration subdirs...
if (!_access("../../testfiles", 0)) if (!_access("../../testfiles", 0))
@ -363,6 +378,7 @@ do_crypto_tests(void)
static int // O - Exit status static int // O - Exit status
do_test_file(const char *filename, // I - PDF filename do_test_file(const char *filename, // I - PDF filename
int objnum, // I - Object number to dump, if any int objnum, // I - Object number to dump, if any
const char *password, // I - Password for file
bool verbose) // I - Be verbose? bool verbose) // I - Be verbose?
{ {
bool error = false; // Have we shown an error yet? bool error = false; // Have we shown an error yet?
@ -381,7 +397,7 @@ do_test_file(const char *filename, // I - PDF filename
fflush(stdout); fflush(stdout);
} }
if ((pdf = pdfioFileOpen(filename, /*password_cb*/NULL, /*password_data*/NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL) if ((pdf = pdfioFileOpen(filename, password_cb, (void *)password, (pdfio_error_cb_t)error_cb, &error)) != NULL)
{ {
if (objnum) if (objnum)
{ {
@ -405,7 +421,7 @@ do_test_file(const char *filename, // I - PDF filename
filter = pdfioDictGetName(dict, "Filter"); filter = pdfioDictGetName(dict, "Filter");
if ((st = pdfioObjOpenStream(obj, (filter && !strcmp(filter, "FlateDecode")) ? PDFIO_FILTER_FLATE : PDFIO_FILTER_NONE)) == NULL) if ((st = pdfioObjOpenStream(obj, filter && !strcmp(filter, "FlateDecode"))) == NULL)
{ {
_pdfioValueDebug(&obj->value, stdout); _pdfioValueDebug(&obj->value, stdout);
putchar('\n'); putchar('\n');
@ -454,7 +470,7 @@ do_test_file(const char *filename, // I - PDF filename
} }
} }
printf(" Page #%d is %gx%g.\n", (int)n + 1, media_box.x2, media_box.y2); printf(" Page #%d (obj %d) is %gx%g.\n", (int)n + 1, (int)pdfioObjGetNumber(obj), media_box.x2, media_box.y2);
} }
} }
@ -506,6 +522,9 @@ do_unit_tests(void)
_pdfio_value_t value; // Value _pdfio_value_t value; // Value
size_t first_image, // First image object size_t first_image, // First image object
num_pages; // Number of pages written num_pages; // Number of pages written
char temppdf[1024]; // Temporary PDF file
pdfio_dict_t *dict; // Test dictionary
int count = 0; // Number of key/value pairs
static const char *complex_dict = // Complex dictionary value static const char *complex_dict = // Complex dictionary value
"<</Annots 5457 0 R/Contents 5469 0 R/CropBox[0 0 595.4 842]/Group 725 0 R" "<</Annots 5457 0 R/Contents 5469 0 R/CropBox[0 0 595.4 842]/Group 725 0 R"
"/MediaBox[0 0 595.4 842]/Parent 23513 0 R/Resources<</ColorSpace<<" "/MediaBox[0 0 595.4 842]/Parent 23513 0 R/Resources<</ColorSpace<<"
@ -967,6 +986,41 @@ do_unit_tests(void)
// TODO: Test for known values in this test file. // TODO: Test for known values in this test file.
// Test dictionary APIs
fputs("pdfioDictCreate: ", stdout);
if ((dict = pdfioDictCreate(inpdf)) != NULL)
{
puts("PASS");
fputs("pdfioDictSet*: ", stdout);
if (pdfioDictSetBoolean(dict, "Boolean", true) && pdfioDictSetName(dict, "Name", "Name") && pdfioDictSetNumber(dict, "Number", 42.0) && pdfioDictSetString(dict, "String", "String"))
{
puts("PASS");
}
else
{
puts("FAIL");
return (1);
}
fputs("pdfioDictIterateKeys: ", stdout);
pdfioDictIterateKeys(dict, iterate_cb, &count);
if (count == 4)
{
puts("PASS");
}
else
{
printf("FAIL (got %d, expected 4)\n", count);
return (1);
}
}
else
{
puts("FAIL");
return (1);
}
// Test the value parsers for edge cases... // Test the value parsers for edge cases...
fputs("_pdfioValueRead(complex_dict): ", stdout); fputs("_pdfioValueRead(complex_dict): ", stdout);
s = complex_dict; s = complex_dict;
@ -1006,7 +1060,7 @@ do_unit_tests(void)
else else
goto fail; goto fail;
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-out.pdf", outpdf, &num_pages, &first_image))
goto fail; goto fail;
if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
@ -1025,7 +1079,7 @@ do_unit_tests(void)
else else
goto fail; goto fail;
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-out2.pdf", outpdf, &num_pages, &first_image))
goto fail; goto fail;
close(outfd); close(outfd);
@ -1046,7 +1100,7 @@ do_unit_tests(void)
else else
return (1); return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-rc4.pdf", outpdf, &num_pages, &first_image))
return (1); return (1);
if (read_unit_file("testpdfio-rc4.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-rc4.pdf", num_pages, first_image, false))
@ -1065,7 +1119,7 @@ do_unit_tests(void)
else else
return (1); return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-rc4p.pdf", outpdf, &num_pages, &first_image))
return (1); return (1);
if (read_unit_file("testpdfio-rc4p.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-rc4p.pdf", num_pages, first_image, false))
@ -1083,7 +1137,7 @@ do_unit_tests(void)
else else
return (1); return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-aes.pdf", outpdf, &num_pages, &first_image))
return (1); return (1);
if (read_unit_file("testpdfio-aes.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-aes.pdf", num_pages, first_image, false))
@ -1101,12 +1155,24 @@ do_unit_tests(void)
else else
return (1); return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-aesp.pdf", outpdf, &num_pages, &first_image))
return (1); return (1);
if (read_unit_file("testpdfio-aesp.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-aesp.pdf", num_pages, first_image, false))
return (1); return (1);
fputs("pdfioFileCreateTemporary: ", stdout);
if ((outpdf = pdfioFileCreateTemporary(temppdf, sizeof(temppdf), NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
printf("PASS (%s)\n", temppdf);
else
return (1);
if (write_unit_file(inpdf, "<temporary>", outpdf, &num_pages, &first_image))
return (1);
if (read_unit_file(temppdf, num_pages, first_image, false))
return (1);
pdfioFileClose(inpdf); pdfioFileClose(inpdf);
return (0); return (0);
@ -1199,6 +1265,27 @@ error_cb(pdfio_file_t *pdf, // I - PDF file
} }
//
// 'iterate_cb()' - Test pdfioDictIterateKeys function.
//
static bool // O - `true` to continue, `false` to stop
iterate_cb(pdfio_dict_t *dict, // I - Dictionary
const char *key, // I - Key
void *cb_data) // I - Callback data
{
int *count = (int *)cb_data; // Pointer to counter
if (!dict || !key || !cb_data)
return (false);
(*count)++;
return (true);
}
// //
// 'output_cb()' - Write output to a file. // 'output_cb()' - Write output to a file.
// //
@ -1237,7 +1324,9 @@ read_unit_file(const char *filename, // I - File to read
bool is_output) // I - File written with output callback? bool is_output) // I - File written with output callback?
{ {
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
pdfio_dict_t *catalog; // Catalog dictionary
size_t i; // Looping var size_t i; // Looping var
const char *s; // String
bool error = false; // Error callback data bool error = false; // Error callback data
@ -1248,6 +1337,164 @@ read_unit_file(const char *filename, // I - File to read
else else
return (1); return (1);
// Get the root object/catalog dictionary
fputs("pdfioFileGetCatalog: ", stdout);
if ((catalog = pdfioFileGetCatalog(pdf)) != NULL)
{
puts("PASS");
}
else
{
puts("FAIL (got NULL, expected dictionary)");
return (1);
}
// Verify some catalog values...
fputs("pdfioDictGetName(PageLayout): ", stdout);
if ((s = pdfioDictGetName(catalog, "PageLayout")) != NULL && !strcmp(s, "SinglePage"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'SinglePage')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'SinglePage')");
return (1);
}
fputs("pdfioDictGetName(PageLayout): ", stdout);
if ((s = pdfioDictGetName(catalog, "PageLayout")) != NULL && !strcmp(s, "SinglePage"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'SinglePage')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'SinglePage')");
return (1);
}
fputs("pdfioDictGetName(PageMode): ", stdout);
if ((s = pdfioDictGetName(catalog, "PageMode")) != NULL && !strcmp(s, "UseThumbs"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'UseThumbs')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'UseThumbs')");
return (1);
}
fputs("pdfioDictGetString(Lang): ", stdout);
if ((s = pdfioDictGetString(catalog, "Lang")) != NULL && !strcmp(s, "en"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'en')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'en')");
return (1);
}
// Verify metadata...
fputs("pdfioFileGetAuthor: ", stdout);
if ((s = pdfioFileGetAuthor(pdf)) != NULL && !strcmp(s, "Michael R Sweet"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Michael R Sweet')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Michael R Sweet')");
return (1);
}
fputs("pdfioFileGetCreator: ", stdout);
if ((s = pdfioFileGetCreator(pdf)) != NULL && !strcmp(s, "testpdfio"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'testpdfio')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'testpdfio')");
return (1);
}
fputs("pdfioFileGetKeywords: ", stdout);
if ((s = pdfioFileGetKeywords(pdf)) != NULL && !strcmp(s, "one fish,two fish,red fish,blue fish"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'one fish,two fish,red fish,blue fish')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'one fish,two fish,red fish,blue fish')");
return (1);
}
fputs("pdfioFileGetSubject: ", stdout);
if ((s = pdfioFileGetSubject(pdf)) != NULL && !strcmp(s, "Unit test document"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Unit test document')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Unit test document')");
return (1);
}
fputs("pdfioFileGetTitle: ", stdout);
if ((s = pdfioFileGetTitle(pdf)) != NULL && !strcmp(s, "Test Document"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Test Document')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Test Document')");
return (1);
}
// Verify the number of pages is the same... // Verify the number of pages is the same...
fputs("pdfioFileGetNumPages: ", stdout); fputs("pdfioFileGetNumPages: ", stdout);
if (num_pages == pdfioFileGetNumPages(pdf)) if (num_pages == pdfioFileGetNumPages(pdf))
@ -1328,6 +1575,23 @@ token_peek_cb(const char **s, // IO - Test string
} }
//
// 'usage()' - Show program usage.
//
static int // O - Exit status
usage(FILE *fp) // I - Output file
{
fputs("Usage: ./testpdfio [OPTIONS] [FILENAME [OBJNUM]] ...\n", fp);
fputs("Options:\n", fp);
fputs(" --help Show program help.\n", fp);
fputs(" --password PASSWORD Set PDF password.\n", fp);
fputs(" --verbose Be verbose.\n", fp);
return (fp != stdout);
}
// //
// 'verify_image()' - Verify an image object. // 'verify_image()' - Verify an image object.
// //
@ -2075,14 +2339,20 @@ write_color_test(pdfio_file_t *pdf, // I - PDF file
// //
static int // O - 1 on failure, 0 on success static int // O - 1 on failure, 0 on success
write_font_test(pdfio_file_t *pdf, // I - PDF file write_font_test(
int number, // I - Page number pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *font, // I - Page number font int number, // I - Page number
bool unicode) // I - Use Unicode font? pdfio_obj_t *font, // I - Page number font
const char *textfontfile, // I - Text font file
bool unicode) // I - Use Unicode font?
{ {
pdfio_dict_t *dict; // Page dictionary pdfio_dict_t *dict; // Page dictionary
pdfio_stream_t *st; // Page contents stream pdfio_stream_t *st; // Page contents stream
pdfio_obj_t *opensans; // OpenSans-Regular font pdfio_obj_t *textfont; // Text font
char title[256], // Page title
textname[256], // Name of text font
*ptr; // Pointer into name
double width; // Text width
int i; // Looping var int i; // Looping var
static const char * const welcomes[] =// "Welcome" in many languages static const char * const welcomes[] =// "Welcome" in many languages
{ {
@ -2233,28 +2503,16 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
"Märr-ŋamathirri", "Märr-ŋamathirri",
"Ẹ ku abọ", "Ẹ ku abọ",
"Kíimak 'oolal", "Kíimak 'oolal",
"Ngiyakwemukela" "Ngiyakwemukela",
"いらっしゃいませ"
}; };
#if 0 printf("pdfioFileCreateFontObjFromFile(%s): ", textfontfile);
if (unicode) if ((textfont = pdfioFileCreateFontObjFromFile(pdf, textfontfile, unicode)) != NULL)
{ puts("PASS");
fputs("pdfioFileCreateFontObjFromFile(NotoSansJP-Regular.otf): ", stdout);
if ((opensans = pdfioFileCreateFontObjFromFile(pdf, "testfiles/NotoSansJP-Regular.otf", true)) != NULL)
puts("PASS");
else
return (1);
}
else else
#endif // 0 return (1);
{
fputs("pdfioFileCreateFontObjFromFile(OpenSans-Regular.ttf): ", stdout);
if ((opensans = pdfioFileCreateFontObjFromFile(pdf, "testfiles/OpenSans-Regular.ttf", unicode)) != NULL)
puts("PASS");
else
return (1);
}
fputs("pdfioDictCreate: ", stdout); fputs("pdfioDictCreate: ", stdout);
if ((dict = pdfioDictCreate(pdf)) != NULL) if ((dict = pdfioDictCreate(pdf)) != NULL)
@ -2269,7 +2527,7 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
return (1); return (1);
fputs("pdfioPageDictAddFont(F2): ", stdout); fputs("pdfioPageDictAddFont(F2): ", stdout);
if (pdfioPageDictAddFont(dict, "F2", opensans)) if (pdfioPageDictAddFont(dict, "F2", textfont))
puts("PASS"); puts("PASS");
else else
return (1); return (1);
@ -2281,7 +2539,21 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
else else
return (1); return (1);
if (write_header_footer(st, unicode ? "Unicode TrueType Font Test" : "CP1252 TrueType Font Test", number)) if ((ptr = strrchr(textfontfile, '/')) != NULL)
strncpy(textname, ptr + 1, sizeof(textname) - 1);
else
strncpy(textname, textfontfile, sizeof(textname) - 1);
textname[sizeof(textname) - 1] = '\0';
if ((ptr = strrchr(textname, '.')) != NULL)
*ptr = '\0';
if (unicode)
snprintf(title, sizeof(title), "Unicode %s Font Test", textname);
else
snprintf(title, sizeof(title), "CP1252 %s Font Test", textname);
if (write_header_footer(st, title, number))
goto error; goto error;
fputs("pdfioContentTextBegin(): ", stdout); fputs("pdfioContentTextBegin(): ", stdout);
@ -2302,8 +2574,8 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
else else
goto error; goto error;
fputs("pdfioContentTextMoveTo(36.0, 702.0): ", stdout); fputs("pdfioContentTextMoveTo(198.0, 702.0): ", stdout);
if (pdfioContentTextMoveTo(st, 36.0, 702.0)) if (pdfioContentTextMoveTo(st, 198.0, 702.0))
puts("PASS"); puts("PASS");
else else
return (1); return (1);
@ -2312,18 +2584,36 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
{ {
if (i > 0 && (i % 50) == 0) if (i > 0 && (i % 50) == 0)
{ {
fputs("pdfioContentTextMoveTo(200.0, 600.0): ", stdout); fputs("pdfioContentTextMoveTo(162.0, 600.0): ", stdout);
if (pdfioContentTextMoveTo(st, 200.0, 600.0)) if (pdfioContentTextMoveTo(st, 162.0, 600.0))
puts("PASS"); puts("PASS");
else else
return (1); return (1);
} }
printf("pdfioContentTextMeasure(\"%s\"): ", welcomes[i]);
if ((width = pdfioContentTextMeasure(textfont, welcomes[i], 10.0)) >= 0.0)
puts("PASS");
else
return (1);
printf("pdfioContextTextMoveTo(%g, 0.0): ", -width);
if (pdfioContentTextMoveTo(st, -width, 0.0))
puts("PASS");
else
return (1);
printf("pdfioContentTextShowf(\"%s\"): ", welcomes[i]); printf("pdfioContentTextShowf(\"%s\"): ", welcomes[i]);
if (pdfioContentTextShowf(st, unicode, "%s\n", welcomes[i])) if (pdfioContentTextShowf(st, unicode, "%s\n", welcomes[i]))
puts("PASS"); puts("PASS");
else else
return (1); return (1);
printf("pdfioContextTextMoveTo(%g, 0.0): ", width);
if (pdfioContentTextMoveTo(st, width, 0.0))
puts("PASS");
else
return (1);
} }
fputs("pdfioContentTextEnd(): ", stdout); fputs("pdfioContentTextEnd(): ", stdout);
@ -3067,6 +3357,7 @@ write_text_test(pdfio_file_t *pdf, // I - PDF file
static int // O - Exit status static int // O - Exit status
write_unit_file( write_unit_file(
pdfio_file_t *inpdf, // I - Input PDF file pdfio_file_t *inpdf, // I - Input PDF file
const char *outname, // I - Output PDF file name
pdfio_file_t *outpdf, // I - Output PDF file pdfio_file_t *outpdf, // I - Output PDF file
size_t *num_pages, // O - Number of pages size_t *num_pages, // O - Number of pages
size_t *first_image) // O - First image object size_t *first_image) // O - First image object
@ -3076,8 +3367,26 @@ write_unit_file(
*gray_jpg, // gray.jpg image *gray_jpg, // gray.jpg image
*helvetica, // Helvetica font *helvetica, // Helvetica font
*page; // Page from test PDF file *page; // Page from test PDF file
pdfio_dict_t *catalog; // Catalog dictionary
// Get the root object/catalog dictionary
fputs("pdfioFileGetCatalog: ", stdout);
if ((catalog = pdfioFileGetCatalog(outpdf)) != NULL)
{
puts("PASS");
}
else
{
puts("FAIL (got NULL, expected dictionary)");
return (1);
}
// Set some catalog values...
pdfioDictSetName(catalog, "PageLayout", "SinglePage");
pdfioDictSetName(catalog, "PageMode", "UseThumbs");
pdfioDictSetString(catalog, "Lang", "en");
// Set info values... // Set info values...
fputs("pdfioFileGet/SetAuthor: ", stdout); fputs("pdfioFileGet/SetAuthor: ", stdout);
pdfioFileSetAuthor(outpdf, "Michael R Sweet"); pdfioFileSetAuthor(outpdf, "Michael R Sweet");
@ -3167,13 +3476,13 @@ write_unit_file(
// Create some image objects... // Create some image objects...
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/color.jpg\"): ", stdout); fputs("pdfioFileCreateImageObjFromFile(\"testfiles/color.jpg\"): ", stdout);
if ((color_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/color.jpg", true)) != NULL) if ((color_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/color.jpg", true)) != NULL)
puts("PASS"); printf("PASS (%u)\n", (unsigned)pdfioObjGetNumber(color_jpg));
else else
return (1); return (1);
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/gray.jpg\"): ", stdout); fputs("pdfioFileCreateImageObjFromFile(\"testfiles/gray.jpg\"): ", stdout);
if ((gray_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/gray.jpg", true)) != NULL) if ((gray_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/gray.jpg", true)) != NULL)
puts("PASS"); printf("PASS (%u)\n", (unsigned)pdfioObjGetNumber(gray_jpg));
else else
return (1); return (1);
@ -3236,14 +3545,17 @@ write_unit_file(
return (1); return (1);
// Test TrueType fonts... // Test TrueType fonts...
if (write_font_test(outpdf, 9, helvetica, false)) if (write_font_test(outpdf, 9, helvetica, "testfiles/OpenSans-Regular.ttf", false))
return (1); return (1);
if (write_font_test(outpdf, 10, helvetica, true)) if (write_font_test(outpdf, 10, helvetica, "testfiles/OpenSans-Regular.ttf", true))
return (1);
if (write_font_test(outpdf, 11, helvetica, "testfiles/NotoSansJP-Regular.otf", true))
return (1); return (1);
// Print this text file... // Print this text file...
if (write_text_test(outpdf, 11, helvetica, "README.md")) if (write_text_test(outpdf, 12, helvetica, "README.md"))
return (1); return (1);
fputs("pdfioFileGetNumPages: ", stdout); fputs("pdfioFileGetNumPages: ", stdout);
@ -3258,7 +3570,7 @@ write_unit_file(
} }
// Close the new PDF file... // Close the new PDF file...
fputs("pdfioFileClose(...): ", stdout); printf("pdfioFileClose(\"%s\"): ", outname);
if (pdfioFileClose(outpdf)) if (pdfioFileClose(outpdf))
puts("PASS"); puts("PASS");
else else

305
testttf.c Normal file
View File

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

184
ttf.c
View File

@ -3,16 +3,12 @@
// //
// https://github.com/michaelrsweet/ttf // https://github.com/michaelrsweet/ttf
// //
// Copyright © 2018-2021 by Michael R Sweet. // Copyright © 2018-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
// //
//
// Include necessary headers...
//
#ifdef _WIN32 #ifdef _WIN32
# define _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS
#endif // _WIN32 #endif // _WIN32
@ -66,7 +62,7 @@
# define O_CREAT _O_CREAT # define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC # define O_TRUNC _O_TRUNC
typedef __int64 ssize_t; // POSIX type not present on Windows... typedef __int64 ssize_t; // POSIX type not present on Windows... @private@
#else #else
# include <unistd.h> # include <unistd.h>
@ -75,7 +71,7 @@ typedef __int64 ssize_t; // POSIX type not present on Windows...
// //
// DEBUG is typically defined for debug builds. TTF_DEBUG maps to printf when // DEBUG is typically defined for debug builds. TTF_DEBUG maps to fprintf when
// DEBUG is defined and is a no-op otherwise... // DEBUG is defined and is a no-op otherwise...
// //
@ -103,6 +99,8 @@ typedef __int64 ssize_t; // POSIX type not present on Windows...
// //
#define TTF_FONT_MAX_CHAR 262144 // Maximum number of character values #define TTF_FONT_MAX_CHAR 262144 // Maximum number of character values
#define TTF_FONT_MAX_GROUPS 65536 // Maximum number of sub-groups
#define TTF_FONT_MAX_NAMES 16777216// Maximum size of names table we support
// //
@ -258,7 +256,7 @@ typedef struct _ttf_off_hhea_s // Horizontal header
{ {
short ascender, // Ascender short ascender, // Ascender
descender; // Descender descender; // Descender
int numberOfHMetrics; // Number of horizontal metrics unsigned short numberOfHMetrics; // Number of horizontal metrics
} _ttf_off_hhea_t; } _ttf_off_hhea_t;
typedef struct _ttf_off_os_2_s // OS/2 information typedef struct _ttf_off_os_2_s // OS/2 information
@ -301,7 +299,28 @@ static unsigned seek_table(ttf_t *font, unsigned tag, unsigned offset, bool requ
// //
// 'ttfCreate()' - Create a new font object for the named font family. // 'ttfCreate()' - Create a new font object for the named font file.
//
// This function creates a new font object for the named TrueType or OpenType
// font file or collection. The "filename" argument specifies the name of the
// file to read.
//
// The "idx" argument specifies the font to load from a collection - the first
// font is number `0`. Once created, you can call the @link ttfGetNumFonts@
// function to determine whether the loaded font file is a collection with more
// than one font.
//
// The "err_cb" and "err_data" arguments specify a callback function and data
// pointer for receiving error messages. If `NULL`, errors are sent to the
// `stderr` file. The callback function receives the data pointer and a text
// message string, for example:
//
// ```
// void my_err_cb(void *err_data, const char *message)
// {
// fprintf(stderr, "ERROR: %s\n", message);
// }
// ```
// //
ttf_t * // O - New font object ttf_t * // O - New font object
@ -420,7 +439,7 @@ ttfCreate(const char *filename, // I - Filename
if (read_os_2(font, &os_2)) if (read_os_2(font, &os_2))
{ {
// Copy key values from OS/2 table... // Copy key values from OS/2 table...
static const ttf_stretch_t widths[] = static const ttf_stretch_t stretches[] =
{ {
TTF_STRETCH_ULTRA_CONDENSED, // ultra-condensed TTF_STRETCH_ULTRA_CONDENSED, // ultra-condensed
TTF_STRETCH_EXTRA_CONDENSED, // extra-condensed TTF_STRETCH_EXTRA_CONDENSED, // extra-condensed
@ -433,8 +452,8 @@ ttfCreate(const char *filename, // I - Filename
TTF_STRETCH_ULTRA_EXPANDED // ultra-expanded TTF_STRETCH_ULTRA_EXPANDED // ultra-expanded
}; };
if (os_2.usWidthClass >= 1 && os_2.usWidthClass <= (int)(sizeof(widths) / sizeof(widths[0]))) if (os_2.usWidthClass >= 1 && os_2.usWidthClass <= (int)(sizeof(stretches) / sizeof(stretches[0])))
font->stretch = widths[os_2.usWidthClass - 1]; font->stretch = stretches[os_2.usWidthClass - 1];
font->weight = (short)os_2.usWeightClass; font->weight = (short)os_2.usWeightClass;
font->cap_height = os_2.sCapHeight; font->cap_height = os_2.sCapHeight;
@ -452,7 +471,7 @@ ttfCreate(const char *filename, // I - Filename
font->cap_height = font->ascent; font->cap_height = font->ascent;
if (font->x_height == 0) if (font->x_height == 0)
font->x_height = 3 * font->ascent / 5; font->x_height = 3 * font->ascent / 5;
// Build a sparse glyph widths table... // Build a sparse glyph widths table...
font->min_char = -1; font->min_char = -1;
@ -481,6 +500,11 @@ ttfCreate(const char *filename, // I - Filename
else else
font->widths[bin][i & 255] = widths[glyph]; font->widths[bin][i & 255] = widths[glyph];
} }
#ifdef DEBUG
if (i >= ' ' && i < 127 && font->widths[0])
TTF_DEBUG("ttfCreate: width['%c']=%d(%d)\n", (char)i, font->widths[0][i].width, font->widths[0][i].left_bearing);
#endif // DEBUG
} }
// Cleanup and return the font... // Cleanup and return the font...
@ -549,6 +573,10 @@ ttfGetAscent(ttf_t *font) // I - Font
// //
// 'ttfGetBounds()' - Get the bounds of all characters in a font. // 'ttfGetBounds()' - Get the bounds of all characters in a font.
// //
// This function gets the bounds of all characters in a font. The "bounds"
// argument is a pointer to a `ttf_rect_t` structure that will be filled with
// the limits for characters in the font scaled to a 1000x1000 unit square.
//
ttf_rect_t * // O - Bounds or `NULL` on error ttf_rect_t * // O - Bounds or `NULL` on error
ttfGetBounds(ttf_t *font, // I - Font ttfGetBounds(ttf_t *font, // I - Font
@ -630,8 +658,11 @@ ttfGetDescent(ttf_t *font) // I - Font
// //
// 'ttfGetExtents()' - Get the extents of a UTF-8 string. // 'ttfGetExtents()' - Get the extents of a UTF-8 string.
// //
// This function computes the extents of a UTF-8 string when rendered using the // This function computes the extents of the UTF-8 string "s" when rendered
// specified font and size. // using the specified font "font" and size "size". The "extents" argument is
// a pointer to a `ttf_rect_t` structure that is filled with the extents of a
// simple rendering of the string with no kerning or rewriting applied. The
// values are scaled using the specified font size.
// //
ttf_rect_t * // O - Pointer to extents or `NULL` on error ttf_rect_t * // O - Pointer to extents or `NULL` on error
@ -737,17 +768,6 @@ ttfGetFamily(ttf_t *font) // I - Font
} }
//
// 'ttfIsFixedPitch()' - Determine whether a font is fixedpitch.
//
bool // O - `true` if fixed pitch, `false` otherwise
ttfIsFixedPitch(ttf_t *font) // I - Font
{
return (font ? font->is_fixed : false);
}
// //
// 'ttfGetItalicAngle()' - Get the italic angle. // 'ttfGetItalicAngle()' - Get the italic angle.
// //
@ -856,13 +876,14 @@ int // O - Width in 1000ths
ttfGetWidth(ttf_t *font, // I - Font ttfGetWidth(ttf_t *font, // I - Font
int ch) // I - Unicode character int ch) // I - Unicode character
{ {
int bin = ch >> 8; // Bin in widths array int bin = ch >> 8; // Bin in widths array
// Range check input... // Range check input...
if (!font || ch < ' ' || ch == 0x7f) if (!font || ch < ' ' || ch == 0x7f)
return (0); return (0);
else if (font->widths[bin])
if (font->widths[bin])
return ((int)(1000.0f * font->widths[bin][ch & 255].width / font->units)); return ((int)(1000.0f * font->widths[bin][ch & 255].width / font->units));
else if (font->widths[0]) // .notdef else if (font->widths[0]) // .notdef
return ((int)(1000.0f * font->widths[0][0].width / font->units)); return ((int)(1000.0f * font->widths[0][0].width / font->units));
@ -882,6 +903,17 @@ ttfGetXHeight(ttf_t *font) // I - Font
} }
//
// 'ttfIsFixedPitch()' - Determine whether a font is fixedpitch.
//
bool // O - `true` if fixed pitch, `false` otherwise
ttfIsFixedPitch(ttf_t *font) // I - Font
{
return (font ? font->is_fixed : false);
}
// //
// 'copy_name()' - Copy a name string from a font. // 'copy_name()' - Copy a name string from a font.
// //
@ -1270,20 +1302,38 @@ read_cmap(ttf_t *font) // I - Font
for (i = 0; i < numGlyphIdArray; i ++) for (i = 0; i < numGlyphIdArray; i ++)
glyphIdArray[i] = read_ushort(font); glyphIdArray[i] = read_ushort(font);
#ifdef DEBUG for (i = 0, segment = segments; i < segCount; i ++, segment ++)
for (i = 0; i < segCount; i ++) {
TTF_DEBUG("read_cmap: segment[%d].startCode=%d, endCode=%d, idDelta=%d, idRangeOffset=%d\n", i, segments[i].startCode, segments[i].endCode, segments[i].idDelta, segments[i].idRangeOffset); TTF_DEBUG("read_cmap: segment[%d].startCode=%d, endCode=%d, idDelta=%d, idRangeOffset=%d\n", i, segment->startCode, segment->endCode, segment->idDelta, segment->idRangeOffset);
if (segment->startCode > segment->endCode)
{
errorf(font, "Bad cmap table segment %u to %u.", segments->startCode, segment->endCode);
free(segments);
free(glyphIdArray);
return (false);
}
// Based on the end code of the segment table, allocate space for the
// uncompressed cmap table...
if (segment->endCode >= font->num_cmap)
font->num_cmap = segment->endCode + 1;
}
#ifdef DEBUG
for (i = 0; i < numGlyphIdArray; i ++) for (i = 0; i < numGlyphIdArray; i ++)
TTF_DEBUG("read_cmap: glyphIdArray[%d]=%d\n", i, glyphIdArray[i]); TTF_DEBUG("read_cmap: glyphIdArray[%d]=%d\n", i, glyphIdArray[i]);
#endif /* DEBUG */ #endif /* DEBUG */
// Based on the end code of the segent table, allocate space for the if (font->num_cmap == 0 || font->num_cmap > TTF_FONT_MAX_CHAR)
// uncompressed cmap table... {
segCount --; // Last segment is not used (sigh) errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
free(segments);
free(glyphIdArray);
return (false);
}
font->num_cmap = segments[segCount - 1].endCode + 1; font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
if (!font->cmap) if (!font->cmap)
{ {
@ -1305,17 +1355,18 @@ read_cmap(ttf_t *font) // I - Font
{ {
// Use an "obscure indexing trick" (words from the spec, not // Use an "obscure indexing trick" (words from the spec, not
// mine) to look up the glyph index... // mine) to look up the glyph index...
temp = segment->idRangeOffset / 2 + ch - segment->startCode + seg - segCount - 1; temp = (int)(segment->idRangeOffset / 2 - segCount + (ch - segment->startCode) + (segment - segments));
if (temp < 0 || temp >= numGlyphIdArray || !glyphIdArray[temp]) TTF_DEBUG("read_cmap: ch=%d, temp=%d\n", ch, temp);
if (temp < 0 || temp >= numGlyphIdArray)
glyph = -1; glyph = -1;
else else
glyph = ((glyphIdArray[temp] + segment->idDelta) & 65535) - 1; glyph = (glyphIdArray[temp] + segment->idDelta) & 65535;
} }
else else
{ {
// Just use idDelta to compute a glyph index... // Just use idDelta to compute a glyph index...
glyph = ((ch + segment->idDelta) & 65535) - 1; glyph = (ch + segment->idDelta) & 65535;
} }
cmapptr[ch] = glyph; cmapptr[ch] = glyph;
@ -1353,6 +1404,12 @@ read_cmap(ttf_t *font) // I - Font
TTF_DEBUG("read_cmap: nGroups=%u\n", nGroups); TTF_DEBUG("read_cmap: nGroups=%u\n", nGroups);
if (nGroups > TTF_FONT_MAX_GROUPS)
{
errorf(font, "Invalid cmap table with %u groups.", nGroups);
return (false);
}
if ((groups = (_ttf_off_cmap12_t *)calloc(nGroups, sizeof(_ttf_off_cmap12_t))) == NULL) if ((groups = (_ttf_off_cmap12_t *)calloc(nGroups, sizeof(_ttf_off_cmap12_t))) == NULL)
{ {
errorf(font, "Unable to allocate memory for cmap."); errorf(font, "Unable to allocate memory for cmap.");
@ -1366,6 +1423,13 @@ read_cmap(ttf_t *font) // I - Font
group->startGlyphID = read_ulong(font); group->startGlyphID = read_ulong(font);
TTF_DEBUG("read_cmap: [%u] startCharCode=%u, endCharCode=%u, startGlyphID=%u\n", gidx, group->startCharCode, group->endCharCode, group->startGlyphID); TTF_DEBUG("read_cmap: [%u] startCharCode=%u, endCharCode=%u, startGlyphID=%u\n", gidx, group->startCharCode, group->endCharCode, group->startGlyphID);
if (group->startCharCode > group->endCharCode)
{
errorf(font, "Bad cmap table segment %u to %u.", group->startCharCode, group->endCharCode);
free(groups);
return (false);
}
if (group->endCharCode >= font->num_cmap) if (group->endCharCode >= font->num_cmap)
font->num_cmap = group->endCharCode + 1; font->num_cmap = group->endCharCode + 1;
} }
@ -1373,6 +1437,14 @@ read_cmap(ttf_t *font) // I - Font
// Based on the end code of the segent table, allocate space for the // Based on the end code of the segent table, allocate space for the
// uncompressed cmap table... // uncompressed cmap table...
TTF_DEBUG("read_cmap: num_cmap=%u\n", (unsigned)font->num_cmap); TTF_DEBUG("read_cmap: num_cmap=%u\n", (unsigned)font->num_cmap);
if (font->num_cmap == 0 || font->num_cmap > TTF_FONT_MAX_CHAR)
{
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
free(groups);
return (false);
}
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int)); font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
if (!font->cmap) if (!font->cmap)
@ -1423,6 +1495,12 @@ read_cmap(ttf_t *font) // I - Font
TTF_DEBUG("read_cmap: nGroups=%u\n", nGroups); TTF_DEBUG("read_cmap: nGroups=%u\n", nGroups);
if (nGroups > TTF_FONT_MAX_GROUPS)
{
errorf(font, "Invalid cmap table with %u groups.", nGroups);
return (false);
}
if ((groups = (_ttf_off_cmap13_t *)calloc(nGroups, sizeof(_ttf_off_cmap13_t))) == NULL) if ((groups = (_ttf_off_cmap13_t *)calloc(nGroups, sizeof(_ttf_off_cmap13_t))) == NULL)
{ {
errorf(font, "Unable to allocate memory for cmap."); errorf(font, "Unable to allocate memory for cmap.");
@ -1436,6 +1514,13 @@ read_cmap(ttf_t *font) // I - Font
group->glyphID = read_ulong(font); group->glyphID = read_ulong(font);
TTF_DEBUG("read_cmap: [%u] startCharCode=%u, endCharCode=%u, glyphID=%u\n", gidx, group->startCharCode, group->endCharCode, group->glyphID); TTF_DEBUG("read_cmap: [%u] startCharCode=%u, endCharCode=%u, glyphID=%u\n", gidx, group->startCharCode, group->endCharCode, group->glyphID);
if (group->startCharCode > group->endCharCode)
{
errorf(font, "Bad cmap table segment %u to %u.", group->startCharCode, group->endCharCode);
free(groups);
return (false);
}
if (group->endCharCode >= font->num_cmap) if (group->endCharCode >= font->num_cmap)
font->num_cmap = group->endCharCode + 1; font->num_cmap = group->endCharCode + 1;
} }
@ -1443,6 +1528,14 @@ read_cmap(ttf_t *font) // I - Font
// Based on the end code of the segent table, allocate space for the // Based on the end code of the segent table, allocate space for the
// uncompressed cmap table... // uncompressed cmap table...
TTF_DEBUG("read_cmap: num_cmap=%u\n", (unsigned)font->num_cmap); TTF_DEBUG("read_cmap: num_cmap=%u\n", (unsigned)font->num_cmap);
if (font->num_cmap == 0 || font->num_cmap > TTF_FONT_MAX_CHAR)
{
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
free(groups);
return (false);
}
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int)); font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
if (!font->cmap) if (!font->cmap)
@ -1562,7 +1655,7 @@ read_hmtx(ttf_t *font, // I - Font
_ttf_off_hhea_t *hhea) // O - hhea table data _ttf_off_hhea_t *hhea) // O - hhea table data
{ {
unsigned length; // Length of hmtx table unsigned length; // Length of hmtx table
int i; // Looping var unsigned i; // Looping var
_ttf_metric_t *widths; // Glyph metrics array _ttf_metric_t *widths; // Glyph metrics array
@ -1582,6 +1675,8 @@ read_hmtx(ttf_t *font, // I - Font
{ {
widths[i].width = (short)read_ushort(font); widths[i].width = (short)read_ushort(font);
widths[i].left_bearing = (short)read_short(font); widths[i].left_bearing = (short)read_short(font);
TTF_DEBUG("read_hmtx: widths[%d].width=%d, .left_bearing=%d\n", i, widths[i].width, widths[i].left_bearing);
} }
return (widths); return (widths);
@ -1639,8 +1734,15 @@ read_names(ttf_t *font) // I - Font
return (false); return (false);
font->names.storage_size = length - (unsigned)offset; font->names.storage_size = length - (unsigned)offset;
if (font->names.storage_size > TTF_FONT_MAX_NAMES)
{
errorf(font, "Name table too large - %u bytes.", (unsigned)font->names.storage_size);
return (false);
}
if ((font->names.storage = malloc(font->names.storage_size)) == NULL) if ((font->names.storage = malloc(font->names.storage_size)) == NULL)
return (false); return (false);
memset(font->names.storage, 'A', font->names.storage_size); memset(font->names.storage, 'A', font->names.storage_size);
for (i = font->names.num_names, name = font->names.names; i > 0; i --, name ++) for (i = font->names.num_names, name = font->names.names; i > 0; i --, name ++)

28
ttf.h
View File

@ -3,7 +3,7 @@
// //
// https://github.com/michaelrsweet/ttf // https://github.com/michaelrsweet/ttf
// //
// Copyright © 2018-2021 by Michael R Sweet. // Copyright © 2018-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -11,29 +11,24 @@
#ifndef TTF_H #ifndef TTF_H
# define TTF_H # define TTF_H
# include <stddef.h>
//
// Include necessary headers...
//
# include <stdbool.h> # include <stdbool.h>
# include <sys/types.h> # include <sys/types.h>
# ifdef __cplusplus # ifdef __cplusplus
extern "C" { extern "C" {
# endif // # endif // __cplusplus
// //
// Types... // Types...
// //
typedef struct _ttf_s ttf_t; //// Font object typedef struct _ttf_s ttf_t; // Font object
typedef void (*ttf_err_cb_t)(void *data, const char *message); typedef void (*ttf_err_cb_t)(void *data, const char *message);
//// Font error callback // Font error callback
typedef enum ttf_stretch_e //// Font stretch typedef enum ttf_stretch_e // Font stretch
{ {
TTF_STRETCH_NORMAL, // normal TTF_STRETCH_NORMAL, // normal
TTF_STRETCH_ULTRA_CONDENSED, // ultra-condensed TTF_STRETCH_ULTRA_CONDENSED, // ultra-condensed
@ -46,20 +41,20 @@ typedef enum ttf_stretch_e //// Font stretch
TTF_STRETCH_ULTRA_EXPANDED // ultra-expanded TTF_STRETCH_ULTRA_EXPANDED // ultra-expanded
} ttf_stretch_t; } ttf_stretch_t;
typedef enum ttf_style_e //// Font style typedef enum ttf_style_e // Font style
{ {
TTF_STYLE_NORMAL, // Normal font TTF_STYLE_NORMAL, // Normal font
TTF_STYLE_ITALIC, // Italic font TTF_STYLE_ITALIC, // Italic font
TTF_STYLE_OBLIQUE // Oblique (angled) font TTF_STYLE_OBLIQUE // Oblique (angled) font
} ttf_style_t; } ttf_style_t;
typedef enum ttf_variant_e //// Font variant typedef enum ttf_variant_e // Font variant
{ {
TTF_VARIANT_NORMAL, // Normal font TTF_VARIANT_NORMAL, // Normal font
TTF_VARIANT_SMALL_CAPS // Font whose lowercase letters are small capitals TTF_VARIANT_SMALL_CAPS // Font whose lowercase letters are small capitals
} ttf_variant_t; } ttf_variant_t;
typedef enum ttf_weight_e //// Font weight typedef enum ttf_weight_e // Font weight
{ {
TTF_WEIGHT_100 = 100, // Weight 100 (Thin) TTF_WEIGHT_100 = 100, // Weight 100 (Thin)
TTF_WEIGHT_200 = 200, // Weight 200 (Extra/Ultra-Light) TTF_WEIGHT_200 = 200, // Weight 200 (Extra/Ultra-Light)
@ -72,7 +67,7 @@ typedef enum ttf_weight_e //// Font weight
TTF_WEIGHT_900 = 900 // Weight 900 (Black/Heavy) TTF_WEIGHT_900 = 900 // Weight 900 (Black/Heavy)
} ttf_weight_t; } ttf_weight_t;
typedef struct ttf_rect_s //// Bounding rectangle typedef struct ttf_rect_s // Bounding rectangle
{ {
float left; // Left offset float left; // Left offset
float top; // Top offset float top; // Top offset
@ -89,8 +84,8 @@ extern ttf_t *ttfCreate(const char *filename, size_t idx, ttf_err_cb_t err_cb,
extern void ttfDelete(ttf_t *font); extern void ttfDelete(ttf_t *font);
extern int ttfGetAscent(ttf_t *font); extern int ttfGetAscent(ttf_t *font);
extern ttf_rect_t *ttfGetBounds(ttf_t *font, ttf_rect_t *bounds); extern ttf_rect_t *ttfGetBounds(ttf_t *font, ttf_rect_t *bounds);
extern int ttfGetCapHeight(ttf_t *font);
extern const int *ttfGetCMap(ttf_t *font, size_t *num_cmap); extern const int *ttfGetCMap(ttf_t *font, size_t *num_cmap);
extern int ttfGetCapHeight(ttf_t *font);
extern const char *ttfGetCopyright(ttf_t *font); extern const char *ttfGetCopyright(ttf_t *font);
extern int ttfGetDescent(ttf_t *font); extern int ttfGetDescent(ttf_t *font);
extern ttf_rect_t *ttfGetExtents(ttf_t *font, float size, const char *s, ttf_rect_t *extents); extern ttf_rect_t *ttfGetExtents(ttf_t *font, float size, const char *s, ttf_rect_t *extents);
@ -112,5 +107,4 @@ extern bool ttfIsFixedPitch(ttf_t *font);
# ifdef __cplusplus # ifdef __cplusplus
} }
# endif // __cplusplus # endif // __cplusplus
#endif // !TTF_H #endif // !TTF_H