mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2025-07-15 13:29:56 +02:00
Compare commits
33 Commits
v1.2.0
...
206f75403a
Author | SHA1 | Date | |
---|---|---|---|
206f75403a | |||
7d22477917 | |||
7c3651671b | |||
6cb661f0f4 | |||
7e01451b18 | |||
138f3955d1 | |||
82844ad2ce | |||
d7cce4dfbc | |||
1cec42f399 | |||
f3f70e7877 | |||
90923c3818 | |||
986cc512cd | |||
c35ddbec00 | |||
e4e1c39578 | |||
1d4f77cab1 | |||
b035130cde | |||
d6d5813b04 | |||
6492f210cf | |||
207062a996 | |||
7d37abb0df | |||
0c1122b689 | |||
d4f8dd46b5 | |||
986c5f0438 | |||
a81907bdb9 | |||
63a7a2cdbd | |||
f040cc41c2 | |||
23883268e3 | |||
a1e14503fd | |||
0766869ad1 | |||
6c1db141a1 | |||
b117959725 | |||
e882622233 | |||
c13b5a5e90 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -60,3 +60,5 @@ jobs:
|
|||||||
run: nuget restore pdfio.sln
|
run: nuget restore pdfio.sln
|
||||||
- name: Build PDFio
|
- name: Build PDFio
|
||||||
run: msbuild pdfio.sln
|
run: msbuild pdfio.sln
|
||||||
|
- name: Test PDFio
|
||||||
|
run: .\runtests.bat x64\Debug
|
||||||
|
70
CHANGES.md
70
CHANGES.md
@ -2,8 +2,34 @@ Changes in PDFio
|
|||||||
================
|
================
|
||||||
|
|
||||||
|
|
||||||
v1.2.0 (January 24, 2024)
|
v1.3.2 - YYYY-MM-DD
|
||||||
-------------------------
|
-------------------
|
||||||
|
|
||||||
|
- Added some more sanity checks to the TrueType font reader.
|
||||||
|
- 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)
|
- Now use autoconf to configure the PDFio sources (Issue #54)
|
||||||
- Added `pdfioFileCreateNumberObj` and `pdfioFileCreateStringObj` functions
|
- Added `pdfioFileCreateNumberObj` and `pdfioFileCreateStringObj` functions
|
||||||
@ -26,8 +52,8 @@ v1.2.0 (January 24, 2024)
|
|||||||
65536 in the xref table (Issue #59)
|
65536 in the xref table (Issue #59)
|
||||||
|
|
||||||
|
|
||||||
v1.1.4 (December 3, 2023)
|
v1.1.4 - 2023-12-03
|
||||||
-------------------------
|
-------------------
|
||||||
|
|
||||||
- Fixed detection of encrypted strings that are too short (Issue #52)
|
- Fixed detection of encrypted strings that are too short (Issue #52)
|
||||||
- Fixed a TrueType CMAP decoding bug.
|
- Fixed a TrueType CMAP decoding bug.
|
||||||
@ -35,15 +61,15 @@ v1.1.4 (December 3, 2023)
|
|||||||
- Added a ToUnicode map for Unicode text to support text copying.
|
- Added a ToUnicode map for Unicode text to support text copying.
|
||||||
|
|
||||||
|
|
||||||
v1.1.3 (November 15, 2023)
|
v1.1.3 - 2023-11-15
|
||||||
--------------------------
|
-------------------
|
||||||
|
|
||||||
- Fixed Unicode font support (Issue #16)
|
- Fixed Unicode font support (Issue #16)
|
||||||
- Fixed missing initializer for 40-bit RC4 encryption (Issue #51)
|
- Fixed missing initializer for 40-bit RC4 encryption (Issue #51)
|
||||||
|
|
||||||
|
|
||||||
v1.1.2 (October 10, 2023)
|
v1.1.2 - 2023-10-10
|
||||||
-------------------------
|
-------------------
|
||||||
|
|
||||||
- Updated `pdfioContentSetDashPattern` to support setting a solid (0 length)
|
- Updated `pdfioContentSetDashPattern` to support setting a solid (0 length)
|
||||||
dash pattern (Issue #41)
|
dash pattern (Issue #41)
|
||||||
@ -58,15 +84,15 @@ v1.1.2 (October 10, 2023)
|
|||||||
(Issue #48)
|
(Issue #48)
|
||||||
|
|
||||||
|
|
||||||
v1.1.1 (March 20, 2023)
|
v1.1.1 - 2023-03-20
|
||||||
-----------------------
|
-------------------
|
||||||
|
|
||||||
- CVE-2023-28428: Fixed a potential denial-of-service with corrupt PDF files.
|
- CVE-2023-28428: Fixed a potential denial-of-service with corrupt PDF files.
|
||||||
- Fixed a few build issues.
|
- Fixed a few build issues.
|
||||||
|
|
||||||
|
|
||||||
v1.1.0 (February 6, 2023)
|
v1.1.0 - 2023-02-06
|
||||||
-------------------------
|
-------------------
|
||||||
|
|
||||||
- CVE-2023-24808: Fixed a potential denial-of-service with corrupt PDF files.
|
- CVE-2023-24808: Fixed a potential denial-of-service with corrupt PDF files.
|
||||||
- Added `pdfioFileCreateTemporary` function (Issue #29)
|
- Added `pdfioFileCreateTemporary` function (Issue #29)
|
||||||
@ -80,28 +106,28 @@ v1.1.0 (February 6, 2023)
|
|||||||
- Fixed `pdfioContentMatrixRotate` function.
|
- Fixed `pdfioContentMatrixRotate` function.
|
||||||
|
|
||||||
|
|
||||||
v1.0.1 (March 2, 2022)
|
v1.0.1 - 2022-03-02
|
||||||
----------------------
|
-------------------
|
||||||
|
|
||||||
- Added missing `pdfioPageGetNumStreams` and `pdfioPageOpenStream` functions.
|
- Added missing `pdfioPageGetNumStreams` and `pdfioPageOpenStream` functions.
|
||||||
- Added demo pdfiototext utility.
|
- Added demo pdfiototext utility.
|
||||||
- Fixed bug in `pdfioStreamGetToken`.
|
- Fixed bug in `pdfioStreamGetToken`.
|
||||||
|
|
||||||
|
|
||||||
v1.0.0 (December 14, 2021)
|
v1.0.0 - 2021-12-14
|
||||||
--------------------------
|
-------------------
|
||||||
|
|
||||||
- First stable release.
|
- First stable release.
|
||||||
|
|
||||||
|
|
||||||
v1.0rc1 (November 30, 2021)
|
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)
|
||||||
@ -112,7 +138,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
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# Makefile for PDFio.
|
# Makefile for PDFio.
|
||||||
#
|
#
|
||||||
# Copyright © 2021-2023 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.
|
||||||
@ -163,7 +163,9 @@ install: $(TARGETS)
|
|||||||
# Test everything
|
# Test everything
|
||||||
test: testpdfio testttf
|
test: testpdfio testttf
|
||||||
./testttf 2>test.log
|
./testttf 2>test.log
|
||||||
./testpdfio 2>test.log
|
./testpdfio 2>>test.log
|
||||||
|
LANG=fr_FR.UTF-8 ./testpdfio 2>>test.log
|
||||||
|
|
||||||
|
|
||||||
valgrind: testpdfio
|
valgrind: testpdfio
|
||||||
valgrind --leak-check=full ./testpdfio
|
valgrind --leak-check=full ./testpdfio
|
||||||
|
27
configure
vendored
27
configure
vendored
@ -1,6 +1,6 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# Guess values for system-dependent variables and create Makefiles.
|
# Guess values for system-dependent variables and create Makefiles.
|
||||||
# Generated by GNU Autoconf 2.71 for pdfio 1.2.0.
|
# Generated by GNU Autoconf 2.71 for pdfio 1.3.2.
|
||||||
#
|
#
|
||||||
# Report bugs to <https://github.com/michaelrsweet/pdfio/issues>.
|
# Report bugs to <https://github.com/michaelrsweet/pdfio/issues>.
|
||||||
#
|
#
|
||||||
@ -610,8 +610,8 @@ MAKEFLAGS=
|
|||||||
# Identity of this package.
|
# Identity of this package.
|
||||||
PACKAGE_NAME='pdfio'
|
PACKAGE_NAME='pdfio'
|
||||||
PACKAGE_TARNAME='pdfio'
|
PACKAGE_TARNAME='pdfio'
|
||||||
PACKAGE_VERSION='1.2.0'
|
PACKAGE_VERSION='1.3.2'
|
||||||
PACKAGE_STRING='pdfio 1.2.0'
|
PACKAGE_STRING='pdfio 1.3.2'
|
||||||
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
|
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
|
||||||
PACKAGE_URL='https://www.msweet.org/pdfio'
|
PACKAGE_URL='https://www.msweet.org/pdfio'
|
||||||
|
|
||||||
@ -1293,7 +1293,7 @@ if test "$ac_init_help" = "long"; then
|
|||||||
# Omit some internal or obsolete options to make the list less imposing.
|
# Omit some internal or obsolete options to make the list less imposing.
|
||||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||||
cat <<_ACEOF
|
cat <<_ACEOF
|
||||||
\`configure' configures pdfio 1.2.0 to adapt to many kinds of systems.
|
\`configure' configures pdfio 1.3.2 to adapt to many kinds of systems.
|
||||||
|
|
||||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||||
|
|
||||||
@ -1359,7 +1359,7 @@ fi
|
|||||||
|
|
||||||
if test -n "$ac_init_help"; then
|
if test -n "$ac_init_help"; then
|
||||||
case $ac_init_help in
|
case $ac_init_help in
|
||||||
short | recursive ) echo "Configuration of pdfio 1.2.0:";;
|
short | recursive ) echo "Configuration of pdfio 1.3.2:";;
|
||||||
esac
|
esac
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
|
|
||||||
@ -1456,7 +1456,7 @@ fi
|
|||||||
test -n "$ac_init_help" && exit $ac_status
|
test -n "$ac_init_help" && exit $ac_status
|
||||||
if $ac_init_version; then
|
if $ac_init_version; then
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
pdfio configure 1.2.0
|
pdfio configure 1.3.2
|
||||||
generated by GNU Autoconf 2.71
|
generated by GNU Autoconf 2.71
|
||||||
|
|
||||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||||
@ -1612,7 +1612,7 @@ cat >config.log <<_ACEOF
|
|||||||
This file contains any messages produced by compilers while
|
This file contains any messages produced by compilers while
|
||||||
running configure, to aid debugging if configure makes a mistake.
|
running configure, to aid debugging if configure makes a mistake.
|
||||||
|
|
||||||
It was created by pdfio $as_me 1.2.0, which was
|
It was created by pdfio $as_me 1.3.2, which was
|
||||||
generated by GNU Autoconf 2.71. Invocation command line was
|
generated by GNU Autoconf 2.71. Invocation command line was
|
||||||
|
|
||||||
$ $0$ac_configure_args_raw
|
$ $0$ac_configure_args_raw
|
||||||
@ -2368,9 +2368,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
PDFIO_VERSION="1.2.0"
|
PDFIO_VERSION="1.3.2"
|
||||||
PDFIO_VERSION_MAJOR="`echo 1.2.0 | awk -F. '{print $1}'`"
|
PDFIO_VERSION_MAJOR="`echo 1.3.2 | awk -F. '{print $1}'`"
|
||||||
PDFIO_VERSION_MINOR="`echo 1.2.0 | awk -F. '{printf("%d\n",$2);}'`"
|
PDFIO_VERSION_MINOR="`echo 1.3.2 | awk -F. '{printf("%d\n",$2);}'`"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -4029,8 +4029,8 @@ then :
|
|||||||
|
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||||
printf "%s\n" "yes" >&6; }
|
printf "%s\n" "yes" >&6; }
|
||||||
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
|
|
||||||
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
|
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
|
||||||
|
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
|
||||||
|
|
||||||
else $as_nop
|
else $as_nop
|
||||||
|
|
||||||
@ -4093,6 +4093,7 @@ then :
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
PKGCONFIG_REQUIRES=""
|
||||||
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
|
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
|
||||||
|
|
||||||
fi
|
fi
|
||||||
@ -4934,7 +4935,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
|||||||
# report actual input values of CONFIG_FILES etc. instead of their
|
# report actual input values of CONFIG_FILES etc. instead of their
|
||||||
# values after options handling.
|
# values after options handling.
|
||||||
ac_log="
|
ac_log="
|
||||||
This file was extended by pdfio $as_me 1.2.0, which was
|
This file was extended by pdfio $as_me 1.3.2, which was
|
||||||
generated by GNU Autoconf 2.71. Invocation command line was
|
generated by GNU Autoconf 2.71. Invocation command line was
|
||||||
|
|
||||||
CONFIG_FILES = $CONFIG_FILES
|
CONFIG_FILES = $CONFIG_FILES
|
||||||
@ -4990,7 +4991,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
|||||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||||
ac_cs_config='$ac_cs_config_escaped'
|
ac_cs_config='$ac_cs_config_escaped'
|
||||||
ac_cs_version="\\
|
ac_cs_version="\\
|
||||||
pdfio config.status 1.2.0
|
pdfio config.status 1.3.2
|
||||||
configured by $0, generated by GNU Autoconf 2.71,
|
configured by $0, generated by GNU Autoconf 2.71,
|
||||||
with options \\"\$ac_cs_config\\"
|
with options \\"\$ac_cs_config\\"
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ AC_PREREQ([2.70])
|
|||||||
|
|
||||||
|
|
||||||
dnl Package name and version...
|
dnl Package name and version...
|
||||||
AC_INIT([pdfio], [1.2.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
|
AC_INIT([pdfio], [1.3.2], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
|
||||||
|
|
||||||
PDFIO_VERSION="AC_PACKAGE_VERSION"
|
PDFIO_VERSION="AC_PACKAGE_VERSION"
|
||||||
PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"
|
PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"
|
||||||
|
159
doc/pdfio.3
159
doc/pdfio.3
@ -1,4 +1,4 @@
|
|||||||
.TH pdfio 3 "pdf read/write library" "2024-01-24" "pdf read/write library"
|
.TH pdfio 3 "pdf read/write library" "2024-08-05" "pdf read/write library"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
pdfio \- pdf read/write library
|
pdfio \- pdf read/write library
|
||||||
.SH Introduction
|
.SH Introduction
|
||||||
@ -361,7 +361,7 @@ pdfioStreamWrite writes a buffer of data to the stream
|
|||||||
.PP
|
.PP
|
||||||
The PDF content helper functions provide additional functions for writing specific PDF page stream commands.
|
The PDF content helper functions provide additional functions for writing specific PDF page stream commands.
|
||||||
.PP
|
.PP
|
||||||
When you are done writing the stream, call pdfioStreamCLose to close both the stream and the object.
|
When you are done writing the stream, call pdfioStreamClose to close both the stream and the object.
|
||||||
.SS PDF Content Helper Functions
|
.SS PDF Content Helper Functions
|
||||||
.PP
|
.PP
|
||||||
PDFio includes many helper functions for embedding or writing specific kinds of content to a PDF file. These functions can be roughly grouped into five categories:
|
PDFio includes many helper functions for embedding or writing specific kinds of content to a PDF file. These functions can be roughly grouped into five categories:
|
||||||
@ -787,6 +787,125 @@ pdfioContentTextShowf draws a formatted string in a text block
|
|||||||
pdfioContentTextShowJustified draws an array of literal strings with offsets between them
|
pdfioContentTextShowJustified draws an array of literal strings with offsets between them
|
||||||
|
|
||||||
|
|
||||||
|
.SH Examples
|
||||||
|
.SS Read PDF Metadata
|
||||||
|
.PP
|
||||||
|
The following example function will open a PDF file and print the title, author, creation date, and number of pages:
|
||||||
|
.nf
|
||||||
|
|
||||||
|
#include <pdfio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
show_pdf_info(const char *filename)
|
||||||
|
{
|
||||||
|
pdfio_file_t *pdf;
|
||||||
|
time_t creation_date;
|
||||||
|
struct tm *creation_tm;
|
||||||
|
char creation_text[256];
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
.fi
|
||||||
|
.SS Create PDF File With Text and Image
|
||||||
|
.PP
|
||||||
|
The following example function will create a PDF file, embed a base font and the named JPEG or PNG image file, and then creates a page with the image centered on the page with the text centered below:
|
||||||
|
.nf
|
||||||
|
|
||||||
|
#include <pdfio.h>
|
||||||
|
#include <pdfio\-content.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
create_pdf_image_file(const char *pdfname, const char *imagename, const char *caption)
|
||||||
|
{
|
||||||
|
pdfio_file_t *pdf;
|
||||||
|
pdfio_obj_t *font;
|
||||||
|
pdfio_obj_t *image;
|
||||||
|
pdfio_dict_t *dict;
|
||||||
|
pdfio_stream_t *page;
|
||||||
|
double width, height;
|
||||||
|
double swidth, sheight;
|
||||||
|
double tx, ty;
|
||||||
|
|
||||||
|
|
||||||
|
// Create the PDF file...
|
||||||
|
pdf = pdfioFileCreate(pdfname, /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_cbdata*/NULL);
|
||||||
|
|
||||||
|
// Create a Courier base font for the caption
|
||||||
|
font = pdfioFileCreateFontObjFromBase(pdf, "Courier");
|
||||||
|
|
||||||
|
// Create an image object from the JPEG/PNG image file...
|
||||||
|
image = pdfioFileCreateImageObjFromFile(pdf, imagename, true);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
.fi
|
||||||
|
|
||||||
.SH ENUMERATIONS
|
.SH ENUMERATIONS
|
||||||
.SS pdfio_cs_e
|
.SS pdfio_cs_e
|
||||||
@ -2148,7 +2267,7 @@ pdfio_file_t * pdfioFileCreate (
|
|||||||
pdfio_rect_t *media_box,
|
pdfio_rect_t *media_box,
|
||||||
pdfio_rect_t *crop_box,
|
pdfio_rect_t *crop_box,
|
||||||
pdfio_error_cb_t error_cb,
|
pdfio_error_cb_t error_cb,
|
||||||
void *error_data
|
void *error_cbdata
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.PP
|
||||||
@ -2162,7 +2281,7 @@ The "media_box" and "crop_box" arguments specify the default MediaBox and
|
|||||||
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
|
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
|
||||||
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
||||||
.PP
|
.PP
|
||||||
The "error_cb" and "error_data" arguments specify an error handler callback
|
The "error_cb" and "error_cbdata" arguments specify an error handler callback
|
||||||
and its data pointer - if \fBNULL\fR the default error handler is used that
|
and its data pointer - if \fBNULL\fR the default error handler is used that
|
||||||
writes error messages to \fBstderr\fR.
|
writes error messages to \fBstderr\fR.
|
||||||
.SS pdfioFileCreateArrayObj
|
.SS pdfioFileCreateArrayObj
|
||||||
@ -2325,23 +2444,23 @@ Create a PDF file through an output callback.
|
|||||||
.nf
|
.nf
|
||||||
pdfio_file_t * pdfioFileCreateOutput (
|
pdfio_file_t * pdfioFileCreateOutput (
|
||||||
pdfio_output_cb_t output_cb,
|
pdfio_output_cb_t output_cb,
|
||||||
void *output_ctx,
|
void *output_cbdata,
|
||||||
const char *version,
|
const char *version,
|
||||||
pdfio_rect_t *media_box,
|
pdfio_rect_t *media_box,
|
||||||
pdfio_rect_t *crop_box,
|
pdfio_rect_t *crop_box,
|
||||||
pdfio_error_cb_t error_cb,
|
pdfio_error_cb_t error_cb,
|
||||||
void *error_data
|
void *error_cbdata
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.PP
|
||||||
This function creates a new PDF file that is streamed though an output
|
This function creates a new PDF file that is streamed though an output
|
||||||
callback. The "output_cb" and "output_ctx" arguments specify the output
|
callback. The "output_cb" and "output_cbdata" arguments specify the output
|
||||||
callback and its context pointer which is called whenever data needs to be
|
callback and its data pointer which is called whenever data needs to be
|
||||||
written:
|
written:
|
||||||
.PP
|
.PP
|
||||||
.nf
|
.nf
|
||||||
ssize_t
|
ssize_t
|
||||||
output_cb(void *output_ctx, const void *buffer, size_t bytes)
|
output_cb(void *output_cbdata, const void *buffer, size_t bytes)
|
||||||
{
|
{
|
||||||
// Write buffer to output and return the number of bytes written
|
// Write buffer to output and return the number of bytes written
|
||||||
}
|
}
|
||||||
@ -2355,7 +2474,7 @@ The "media_box" and "crop_box" arguments specify the default MediaBox and
|
|||||||
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
|
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
|
||||||
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
||||||
.PP
|
.PP
|
||||||
The "error_cb" and "error_data" arguments specify an error handler callback
|
The "error_cb" and "error_cbdata" arguments specify an error handler callback
|
||||||
and its data pointer - if \fBNULL\fR the default error handler is used that
|
and its data pointer - if \fBNULL\fR the default error handler is used that
|
||||||
writes error messages to \fBstderr\fR.
|
writes error messages to \fBstderr\fR.
|
||||||
.PP
|
.PP
|
||||||
@ -2397,7 +2516,7 @@ pdfio_file_t * pdfioFileCreateTemporary (
|
|||||||
pdfio_rect_t *media_box,
|
pdfio_rect_t *media_box,
|
||||||
pdfio_rect_t *crop_box,
|
pdfio_rect_t *crop_box,
|
||||||
pdfio_error_cb_t error_cb,
|
pdfio_error_cb_t error_cb,
|
||||||
void *error_data
|
void *error_cbdata
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
.SS pdfioFileFindObj
|
.SS pdfioFileFindObj
|
||||||
@ -2420,6 +2539,16 @@ const char * pdfioFileGetAuthor (
|
|||||||
pdfio_file_t *pdf
|
pdfio_file_t *pdf
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
|
.SS pdfioFileGetCatalog
|
||||||
|
Get the document catalog dictionary.
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
pdfio_dict_t * pdfioFileGetCatalog (
|
||||||
|
pdfio_file_t *pdf
|
||||||
|
);
|
||||||
|
.fi
|
||||||
|
.PP
|
||||||
|
|
||||||
.SS pdfioFileGetCreationDate
|
.SS pdfioFileGetCreationDate
|
||||||
Get the creation date for a PDF file.
|
Get the creation date for a PDF file.
|
||||||
.PP
|
.PP
|
||||||
@ -2545,22 +2674,22 @@ Open a PDF file for reading.
|
|||||||
pdfio_file_t * pdfioFileOpen (
|
pdfio_file_t * pdfioFileOpen (
|
||||||
const char *filename,
|
const char *filename,
|
||||||
pdfio_password_cb_t password_cb,
|
pdfio_password_cb_t password_cb,
|
||||||
void *password_data,
|
void *password_cbdata,
|
||||||
pdfio_error_cb_t error_cb,
|
pdfio_error_cb_t error_cb,
|
||||||
void *error_data
|
void *error_cbdata
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
.PP
|
.PP
|
||||||
This function opens an existing PDF file. The "filename" argument specifies
|
This function opens an existing PDF file. The "filename" argument specifies
|
||||||
the name of the PDF file to create.
|
the name of the PDF file to create.
|
||||||
.PP
|
.PP
|
||||||
The "password_cb" and "password_data" arguments specify a password callback
|
The "password_cb" and "password_cbdata" arguments specify a password callback
|
||||||
and its data pointer for PDF files that use one of the standard Adobe
|
and its data pointer for PDF files that use one of the standard Adobe
|
||||||
"security" handlers. The callback returns a password string or \fBNULL\fR to
|
"security" handlers. The callback returns a password string or \fBNULL\fR to
|
||||||
cancel the open. If \fBNULL\fR is specified for the callback function and the
|
cancel the open. If \fBNULL\fR is specified for the callback function and the
|
||||||
PDF file requires a password, the open will always fail.
|
PDF file requires a password, the open will always fail.
|
||||||
.PP
|
.PP
|
||||||
The "error_cb" and "error_data" arguments specify an error handler callback
|
The "error_cb" and "error_cbdata" arguments specify an error handler callback
|
||||||
and its data pointer - if \fBNULL\fR the default error handler is used that
|
and its data pointer - if \fBNULL\fR the default error handler is used that
|
||||||
writes error messages to \fBstderr\fR.
|
writes error messages to \fBstderr\fR.
|
||||||
.SS pdfioFileSetAuthor
|
.SS pdfioFileSetAuthor
|
||||||
|
483
doc/pdfio.html
483
doc/pdfio.html
File diff suppressed because it is too large
Load Diff
132
doc/pdfio.md
132
doc/pdfio.md
@ -345,7 +345,7 @@ to the stream:
|
|||||||
The [PDF content helper functions](@) provide additional functions for writing
|
The [PDF content helper functions](@) provide additional functions for writing
|
||||||
specific PDF page stream commands.
|
specific PDF page stream commands.
|
||||||
|
|
||||||
When you are done writing the stream, call [`pdfioStreamCLose`](@@) to close
|
When you are done writing the stream, call [`pdfioStreamClose`](@@) to close
|
||||||
both the stream and the object.
|
both the stream and the object.
|
||||||
|
|
||||||
|
|
||||||
@ -586,3 +586,133 @@ escaping, as needed:
|
|||||||
- [`pdfioContentTextShowf`](@@) draws a formatted string in a text block
|
- [`pdfioContentTextShowf`](@@) draws a formatted string in a text block
|
||||||
- [`pdfioContentTextShowJustified`](@@) draws an array of literal strings with
|
- [`pdfioContentTextShowJustified`](@@) draws an array of literal strings with
|
||||||
offsets between them
|
offsets between them
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
========
|
||||||
|
|
||||||
|
Read PDF Metadata
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
The following example function will open a PDF file and print the title, author,
|
||||||
|
creation date, and number of pages:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <pdfio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
show_pdf_info(const char *filename)
|
||||||
|
{
|
||||||
|
pdfio_file_t *pdf;
|
||||||
|
time_t creation_date;
|
||||||
|
struct tm *creation_tm;
|
||||||
|
char creation_text[256];
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Create PDF File With Text and Image
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
The following example function will create a PDF file, embed a base font and the
|
||||||
|
named JPEG or PNG image file, and then creates a page with the image centered on
|
||||||
|
the page with the text centered below:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include <pdfio.h>
|
||||||
|
#include <pdfio-content.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
create_pdf_image_file(const char *pdfname, const char *imagename, const char *caption)
|
||||||
|
{
|
||||||
|
pdfio_file_t *pdf;
|
||||||
|
pdfio_obj_t *font;
|
||||||
|
pdfio_obj_t *image;
|
||||||
|
pdfio_dict_t *dict;
|
||||||
|
pdfio_stream_t *page;
|
||||||
|
double width, height;
|
||||||
|
double swidth, sheight;
|
||||||
|
double tx, ty;
|
||||||
|
|
||||||
|
|
||||||
|
// Create the PDF file...
|
||||||
|
pdf = pdfioFileCreate(pdfname, /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_cbdata*/NULL);
|
||||||
|
|
||||||
|
// Create a Courier base font for the caption
|
||||||
|
font = pdfioFileCreateFontObjFromBase(pdf, "Courier");
|
||||||
|
|
||||||
|
// Create an image object from the JPEG/PNG image file...
|
||||||
|
image = pdfioFileCreateImageObjFromFile(pdf, imagename, true);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
53
makesrcdist
53
makesrcdist
@ -2,18 +2,61 @@
|
|||||||
#
|
#
|
||||||
# makesrcdist - make a source distribution of pdfio.
|
# makesrcdist - make a source distribution of pdfio.
|
||||||
#
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# ./makesrcdist [--snapshot] VERSION
|
||||||
|
#
|
||||||
|
|
||||||
|
# Support "--snapshot" option...
|
||||||
|
if test "$1" == "--snapshot"; then
|
||||||
|
shift
|
||||||
|
snapshot=1
|
||||||
|
else
|
||||||
|
snapshot=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get version...
|
||||||
if test $# != 1; then
|
if test $# != 1; then
|
||||||
echo "Usage: ./makesrcdist version"
|
echo "Usage: ./makesrcdist [--snapshot] VERSION"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
version=$1
|
version=$1
|
||||||
|
|
||||||
echo Creating tag for release...
|
# Check that version number has been updated everywhere...
|
||||||
git tag -m "Tag $version" v$version
|
if test $(grep AC_INIT configure.ac | awk '{print $2}') != "[$version],"; then
|
||||||
git push origin v$version
|
echo "Still need to update AC_INIT version in 'configure.ac'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $(grep PDFIO_VERSION= configure | awk -F \" '{print $2}') != "$version"; then
|
||||||
|
echo "Still need to run 'autoconf -f'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $(grep '<version>' pdfio_native.nuspec | sed -E -e '1,$s/^.*<version>([0-9.]+).*$/\1/') != "$version"; then
|
||||||
|
echo "Still need to update version in 'pdfio_native.nuspec'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $(grep '<version>' pdfio_native.redist.nuspec | sed -E -e '1,$s/^.*<version>([0-9.]+).*$/\1/') != "$version"; then
|
||||||
|
echo "Still need to update version in 'pdfio_native.redist.nuspec'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $(grep PDFIO_VERSION pdfio.h | awk -F \" '{print $2}') != "$version"; then
|
||||||
|
echo "Still need to update PDFIO_VERSION in 'pdfio.h'."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tag release...
|
||||||
|
if test $snapshot = 0; then
|
||||||
|
echo Creating tag for release...
|
||||||
|
git tag -m "Tag $version" v$version
|
||||||
|
git push origin v$version
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make source archives...
|
||||||
echo Creating pdfio-$version.tar.gz...
|
echo Creating pdfio-$version.tar.gz...
|
||||||
git archive --format tar --prefix=pdfio-$version/ HEAD | gzip -v9 >pdfio-$version.tar.gz
|
git archive --format tar --prefix=pdfio-$version/ HEAD | gzip -v9 >pdfio-$version.tar.gz
|
||||||
gpg --detach-sign pdfio-$version.tar.gz
|
gpg --detach-sign pdfio-$version.tar.gz
|
||||||
|
@ -363,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);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// Common support functions for pdfio.
|
// Common support functions for pdfio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2023 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.
|
||||||
@ -261,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...
|
||||||
|
@ -2349,7 +2349,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
|
||||||
|
21
pdfio-dict.c
21
pdfio-dict.c
@ -194,6 +194,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);
|
||||||
@ -426,9 +429,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
632
pdfio-file.c
632
pdfio-file.c
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// PDF file functions for PDFio.
|
// PDF file functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2023 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.
|
||||||
@ -19,11 +19,12 @@
|
|||||||
|
|
||||||
static pdfio_obj_t *add_obj(pdfio_file_t *pdf, size_t number, unsigned short generation, off_t offset);
|
static pdfio_obj_t *add_obj(pdfio_file_t *pdf, size_t number, unsigned short generation, off_t offset);
|
||||||
static int compare_objmaps(_pdfio_objmap_t *a, _pdfio_objmap_t *b);
|
static int compare_objmaps(_pdfio_objmap_t *a, _pdfio_objmap_t *b);
|
||||||
|
static pdfio_file_t *create_common(const char *filename, int fd, pdfio_output_cb_t output_cb, void *output_cbdata, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_cbdata);
|
||||||
static const char *get_info_string(pdfio_file_t *pdf, const char *key);
|
static const char *get_info_string(pdfio_file_t *pdf, const char *key);
|
||||||
|
static struct lconv *get_lconv(void);
|
||||||
static bool load_obj_stream(pdfio_obj_t *obj);
|
static bool load_obj_stream(pdfio_obj_t *obj);
|
||||||
static bool load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj, size_t depth);
|
static bool load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj, size_t depth);
|
||||||
static bool load_xref(pdfio_file_t *pdf, off_t xref_offset, pdfio_password_cb_t password_cb, void *password_data);
|
static bool load_xref(pdfio_file_t *pdf, off_t xref_offset, pdfio_password_cb_t password_cb, void *password_data);
|
||||||
static bool write_catalog(pdfio_file_t *pdf);
|
|
||||||
static bool write_pages(pdfio_file_t *pdf);
|
static bool write_pages(pdfio_file_t *pdf);
|
||||||
static bool write_trailer(pdfio_file_t *pdf);
|
static bool write_trailer(pdfio_file_t *pdf);
|
||||||
|
|
||||||
@ -119,11 +120,8 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
|
|||||||
{
|
{
|
||||||
ret = false;
|
ret = false;
|
||||||
|
|
||||||
if (pdfioObjClose(pdf->info_obj))
|
if (pdfioObjClose(pdf->info_obj) && write_pages(pdf) && pdfioObjClose(pdf->root_obj) && write_trailer(pdf))
|
||||||
if (write_pages(pdf))
|
ret = _pdfioFileFlush(pdf);
|
||||||
if (write_catalog(pdf))
|
|
||||||
if (write_trailer(pdf))
|
|
||||||
ret = _pdfioFileFlush(pdf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pdf->fd >= 0 && close(pdf->fd) < 0)
|
if (pdf->fd >= 0 && close(pdf->fd) < 0)
|
||||||
@ -172,7 +170,7 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
|
|||||||
// CropBox for pages in the PDF file - if `NULL` then a default "Universal" size
|
// CropBox for pages in the PDF file - if `NULL` then a default "Universal" size
|
||||||
// of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
// of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
||||||
//
|
//
|
||||||
// The "error_cb" and "error_data" arguments specify an error handler callback
|
// The "error_cb" and "error_cbdata" arguments specify an error handler callback
|
||||||
// and its data pointer - if `NULL` the default error handler is used that
|
// and its data pointer - if `NULL` the default error handler is used that
|
||||||
// writes error messages to `stderr`.
|
// writes error messages to `stderr`.
|
||||||
//
|
//
|
||||||
@ -184,122 +182,39 @@ pdfioFileCreate(
|
|||||||
pdfio_rect_t *media_box, // I - Default MediaBox for pages
|
pdfio_rect_t *media_box, // I - Default MediaBox for pages
|
||||||
pdfio_rect_t *crop_box, // I - Default CropBox for pages
|
pdfio_rect_t *crop_box, // I - Default CropBox for pages
|
||||||
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
|
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
|
||||||
void *error_data) // I - Error callback data, if any
|
void *error_cbdata) // I - Error callback data, if any
|
||||||
{
|
{
|
||||||
pdfio_file_t *pdf; // PDF file
|
pdfio_file_t *pdf; // PDF file
|
||||||
pdfio_dict_t *dict; // Dictionary for pages object
|
int fd; // File descriptor
|
||||||
pdfio_dict_t *info_dict; // Dictionary for information object
|
|
||||||
unsigned char id_value[16]; // File ID value
|
|
||||||
|
|
||||||
|
|
||||||
|
PDFIO_DEBUG("pdfioFileCreate(filename=\"%s\", version=\"%s\", media_box=%p, crop_box=%p, error_cb=%p, error_cbdata=%p)\n", filename, version, (void *)media_box, (void *)crop_box, (void *)error_cb, (void *)error_cbdata);
|
||||||
|
|
||||||
// Range check input...
|
// Range check input...
|
||||||
if (!filename)
|
if (!filename)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
if (!version)
|
// Create the file...
|
||||||
version = "2.0";
|
if ((fd = open(filename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666)) < 0)
|
||||||
|
|
||||||
if (!error_cb)
|
|
||||||
{
|
|
||||||
error_cb = _pdfioFileDefaultError;
|
|
||||||
error_data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate a PDF file structure...
|
|
||||||
if ((pdf = (pdfio_file_t *)calloc(1, sizeof(pdfio_file_t))) == NULL)
|
|
||||||
{
|
{
|
||||||
pdfio_file_t temp; // Dummy file
|
pdfio_file_t temp; // Dummy file
|
||||||
char message[8192]; // Message string
|
char message[8192]; // Message string
|
||||||
|
|
||||||
temp.filename = (char *)filename;
|
temp.filename = (char *)filename;
|
||||||
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno));
|
snprintf(message, sizeof(message), "Unable to create '%s': %s", filename, strerror(errno));
|
||||||
(error_cb)(&temp, message, error_data);
|
(error_cb)(&temp, message, error_cbdata);
|
||||||
|
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
pdf->filename = strdup(filename);
|
if ((pdf = create_common(filename, fd, /*output_cb*/NULL, /*output_cbdata*/NULL, version, media_box, crop_box, error_cb, error_cbdata)) == NULL)
|
||||||
pdf->version = strdup(version);
|
|
||||||
pdf->mode = _PDFIO_MODE_WRITE;
|
|
||||||
pdf->error_cb = error_cb;
|
|
||||||
pdf->error_data = error_data;
|
|
||||||
pdf->permissions = PDFIO_PERMISSION_ALL;
|
|
||||||
pdf->bufptr = pdf->buffer;
|
|
||||||
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
|
|
||||||
|
|
||||||
if (media_box)
|
|
||||||
{
|
{
|
||||||
pdf->media_box = *media_box;
|
// Remove the newly created file if we can't create the PDF file object...
|
||||||
}
|
close(fd);
|
||||||
else
|
unlink(filename);
|
||||||
{
|
|
||||||
// Default to "universal" size (intersection of A4 and US Letter)
|
|
||||||
pdf->media_box.x2 = 210.0 * 72.0f / 25.4f;
|
|
||||||
pdf->media_box.y2 = 11.0f * 72.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crop_box)
|
|
||||||
{
|
|
||||||
pdf->crop_box = *crop_box;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Default to "universal" size (intersection of A4 and US Letter)
|
|
||||||
pdf->crop_box.x2 = 210.0 * 72.0f / 25.4f;
|
|
||||||
pdf->crop_box.y2 = 11.0f * 72.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the file...
|
|
||||||
if ((pdf->fd = open(filename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666)) < 0)
|
|
||||||
{
|
|
||||||
_pdfioFileError(pdf, "Unable to create file - %s", strerror(errno));
|
|
||||||
free(pdf->filename);
|
|
||||||
free(pdf->version);
|
|
||||||
free(pdf);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a standard PDF header...
|
|
||||||
if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", version))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
// Create the pages object...
|
|
||||||
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
pdfioDictSetName(dict, "Type", "Pages");
|
|
||||||
|
|
||||||
if ((pdf->pages_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
// Create the info object...
|
|
||||||
if ((info_dict = pdfioDictCreate(pdf)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
pdfioDictSetDate(info_dict, "CreationDate", time(NULL));
|
|
||||||
pdfioDictSetString(info_dict, "Producer", "pdfio/" PDFIO_VERSION);
|
|
||||||
|
|
||||||
if ((pdf->info_obj = pdfioFileCreateObj(pdf, info_dict)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
// Create random file ID values...
|
|
||||||
_pdfioCryptoMakeRandom(id_value, sizeof(id_value));
|
|
||||||
|
|
||||||
if ((pdf->id_array = pdfioArrayCreate(pdf)) != NULL)
|
|
||||||
{
|
|
||||||
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
|
|
||||||
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (pdf);
|
return (pdf);
|
||||||
|
|
||||||
// Common error handling code...
|
|
||||||
error:
|
|
||||||
|
|
||||||
pdfioFileClose(pdf);
|
|
||||||
|
|
||||||
unlink(filename);
|
|
||||||
|
|
||||||
return (NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -439,13 +354,13 @@ _pdfioFileCreateObj(
|
|||||||
// 'pdfioFileCreateOutput()' - Create a PDF file through an output callback.
|
// 'pdfioFileCreateOutput()' - Create a PDF file through an output callback.
|
||||||
//
|
//
|
||||||
// This function creates a new PDF file that is streamed though an output
|
// This function creates a new PDF file that is streamed though an output
|
||||||
// callback. The "output_cb" and "output_ctx" arguments specify the output
|
// callback. The "output_cb" and "output_cbdata" arguments specify the output
|
||||||
// callback and its context pointer which is called whenever data needs to be
|
// callback and its data pointer which is called whenever data needs to be
|
||||||
// written:
|
// written:
|
||||||
//
|
//
|
||||||
// ```
|
// ```
|
||||||
// ssize_t
|
// ssize_t
|
||||||
// output_cb(void *output_ctx, const void *buffer, size_t bytes)
|
// output_cb(void *output_cbdata, const void *buffer, size_t bytes)
|
||||||
// {
|
// {
|
||||||
// // Write buffer to output and return the number of bytes written
|
// // Write buffer to output and return the number of bytes written
|
||||||
// }
|
// }
|
||||||
@ -458,7 +373,7 @@ _pdfioFileCreateObj(
|
|||||||
// CropBox for pages in the PDF file - if `NULL` then a default "Universal" size
|
// CropBox for pages in the PDF file - if `NULL` then a default "Universal" size
|
||||||
// of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
// of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
||||||
//
|
//
|
||||||
// The "error_cb" and "error_data" arguments specify an error handler callback
|
// The "error_cb" and "error_cbdata" arguments specify an error handler callback
|
||||||
// and its data pointer - if `NULL` the default error handler is used that
|
// and its data pointer - if `NULL` the default error handler is used that
|
||||||
// writes error messages to `stderr`.
|
// writes error messages to `stderr`.
|
||||||
//
|
//
|
||||||
@ -469,121 +384,17 @@ _pdfioFileCreateObj(
|
|||||||
|
|
||||||
pdfio_file_t * // O - PDF file or `NULL` on error
|
pdfio_file_t * // O - PDF file or `NULL` on error
|
||||||
pdfioFileCreateOutput(
|
pdfioFileCreateOutput(
|
||||||
pdfio_output_cb_t output_cb, // I - Output callback
|
pdfio_output_cb_t output_cb, // I - Output callback function
|
||||||
void *output_ctx, // I - Output context
|
void *output_cbdata, // I - Output callback data
|
||||||
const char *version, // I - PDF version number or `NULL` for default (2.0)
|
const char *version, // I - PDF version number or `NULL` for default (2.0)
|
||||||
pdfio_rect_t *media_box, // I - Default MediaBox for pages
|
pdfio_rect_t *media_box, // I - Default MediaBox for pages
|
||||||
pdfio_rect_t *crop_box, // I - Default CropBox for pages
|
pdfio_rect_t *crop_box, // I - Default CropBox for pages
|
||||||
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
|
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
|
||||||
void *error_data) // I - Error callback data, if any
|
void *error_cbdata) // I - Error callback data, if any
|
||||||
{
|
{
|
||||||
pdfio_file_t *pdf; // PDF file
|
PDFIO_DEBUG("pdfioFileCreate(output_cb=%p, output_cbdata=%p, version=\"%s\", media_box=%p, crop_box=%p, error_cb=%p, error_cbdata=%p)\n", (void *)output_cb, (void *)output_cbdata, version, (void *)media_box, (void *)crop_box, (void *)error_cb, (void *)error_cbdata);
|
||||||
pdfio_dict_t *dict; // Dictionary for pages object
|
|
||||||
pdfio_dict_t *info_dict; // Dictionary for information object
|
|
||||||
unsigned char id_value[16]; // File ID value
|
|
||||||
|
|
||||||
|
return (create_common("output.pdf", /*fd*/-1, output_cb, output_cbdata, version, media_box, crop_box, error_cb, error_cbdata));
|
||||||
// Range check input...
|
|
||||||
if (!output_cb)
|
|
||||||
return (NULL);
|
|
||||||
|
|
||||||
if (!version)
|
|
||||||
version = "2.0";
|
|
||||||
|
|
||||||
if (!error_cb)
|
|
||||||
{
|
|
||||||
error_cb = _pdfioFileDefaultError;
|
|
||||||
error_data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate a PDF file structure...
|
|
||||||
if ((pdf = (pdfio_file_t *)calloc(1, sizeof(pdfio_file_t))) == NULL)
|
|
||||||
{
|
|
||||||
pdfio_file_t temp; // Dummy file
|
|
||||||
char message[8192]; // Message string
|
|
||||||
|
|
||||||
temp.filename = (char *)"output.pdf";
|
|
||||||
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno));
|
|
||||||
(error_cb)(&temp, message, error_data);
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
pdf->filename = strdup("output.pdf");
|
|
||||||
pdf->version = strdup(version);
|
|
||||||
pdf->mode = _PDFIO_MODE_WRITE;
|
|
||||||
pdf->error_cb = error_cb;
|
|
||||||
pdf->error_data = error_data;
|
|
||||||
pdf->permissions = PDFIO_PERMISSION_ALL;
|
|
||||||
pdf->bufptr = pdf->buffer;
|
|
||||||
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
|
|
||||||
|
|
||||||
if (media_box)
|
|
||||||
{
|
|
||||||
pdf->media_box = *media_box;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Default to "universal" size (intersection of A4 and US Letter)
|
|
||||||
pdf->media_box.x2 = 210.0 * 72.0f / 25.4f;
|
|
||||||
pdf->media_box.y2 = 11.0f * 72.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crop_box)
|
|
||||||
{
|
|
||||||
pdf->crop_box = *crop_box;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Default to "universal" size (intersection of A4 and US Letter)
|
|
||||||
pdf->crop_box.x2 = 210.0 * 72.0f / 25.4f;
|
|
||||||
pdf->crop_box.y2 = 11.0f * 72.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save output callback...
|
|
||||||
pdf->fd = -1;
|
|
||||||
pdf->output_cb = output_cb;
|
|
||||||
pdf->output_ctx = output_ctx;
|
|
||||||
|
|
||||||
// Write a standard PDF header...
|
|
||||||
if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", version))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
// Create the pages object...
|
|
||||||
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
pdfioDictSetName(dict, "Type", "Pages");
|
|
||||||
|
|
||||||
if ((pdf->pages_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
// Create the info object...
|
|
||||||
if ((info_dict = pdfioDictCreate(pdf)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
pdfioDictSetDate(info_dict, "CreationDate", time(NULL));
|
|
||||||
pdfioDictSetString(info_dict, "Producer", "pdfio/" PDFIO_VERSION);
|
|
||||||
|
|
||||||
if ((pdf->info_obj = pdfioFileCreateObj(pdf, info_dict)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
// Create random file ID values...
|
|
||||||
_pdfioCryptoMakeRandom(id_value, sizeof(id_value));
|
|
||||||
|
|
||||||
if ((pdf->id_array = pdfioArrayCreate(pdf)) != NULL)
|
|
||||||
{
|
|
||||||
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
|
|
||||||
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (pdf);
|
|
||||||
|
|
||||||
// Common error handling code...
|
|
||||||
error:
|
|
||||||
|
|
||||||
pdfioFileClose(pdf);
|
|
||||||
|
|
||||||
return (NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -705,13 +516,11 @@ pdfioFileCreateTemporary(
|
|||||||
pdfio_rect_t *media_box, // I - Default MediaBox for pages
|
pdfio_rect_t *media_box, // I - Default MediaBox for pages
|
||||||
pdfio_rect_t *crop_box, // I - Default CropBox for pages
|
pdfio_rect_t *crop_box, // I - Default CropBox for pages
|
||||||
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
|
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
|
||||||
void *error_data) // I - Error callback data, if any
|
void *error_cbdata) // I - Error callback data, if any
|
||||||
{
|
{
|
||||||
pdfio_file_t *pdf; // PDF file
|
pdfio_file_t *pdf; // PDF file
|
||||||
pdfio_dict_t *dict; // Dictionary for pages object
|
int i, // Looping var
|
||||||
pdfio_dict_t *info_dict; // Dictionary for information object
|
fd; // File descriptor
|
||||||
unsigned char id_value[16]; // File ID value
|
|
||||||
int i; // Looping var
|
|
||||||
const char *tmpdir; // Temporary directory
|
const char *tmpdir; // Temporary directory
|
||||||
#if _WIN32 || defined(__APPLE__)
|
#if _WIN32 || defined(__APPLE__)
|
||||||
char tmppath[256]; // Temporary directory path
|
char tmppath[256]; // Temporary directory path
|
||||||
@ -719,6 +528,8 @@ pdfioFileCreateTemporary(
|
|||||||
unsigned tmpnum; // Temporary filename number
|
unsigned tmpnum; // Temporary filename number
|
||||||
|
|
||||||
|
|
||||||
|
PDFIO_DEBUG("pdfioFileCreate(buffer=%p, bufsize=%lu, version=\"%s\", media_box=%p, crop_box=%p, error_cb=%p, error_cbdata=%p)\n", (void *)buffer, (unsigned long)bufsize, version, (void *)media_box, (void *)crop_box, (void *)error_cb, (void *)error_cbdata);
|
||||||
|
|
||||||
// Range check input...
|
// Range check input...
|
||||||
if (!buffer || bufsize < 32)
|
if (!buffer || bufsize < 32)
|
||||||
{
|
{
|
||||||
@ -727,31 +538,7 @@ pdfioFileCreateTemporary(
|
|||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!version)
|
// Create the temporary PDF file...
|
||||||
version = "2.0";
|
|
||||||
|
|
||||||
if (!error_cb)
|
|
||||||
{
|
|
||||||
error_cb = _pdfioFileDefaultError;
|
|
||||||
error_data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate a PDF file structure...
|
|
||||||
if ((pdf = (pdfio_file_t *)calloc(1, sizeof(pdfio_file_t))) == NULL)
|
|
||||||
{
|
|
||||||
pdfio_file_t temp; // Dummy file
|
|
||||||
char message[8192]; // Message string
|
|
||||||
|
|
||||||
temp.filename = (char *)"temporary.pdf";
|
|
||||||
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno));
|
|
||||||
(error_cb)(&temp, message, error_data);
|
|
||||||
|
|
||||||
*buffer = '\0';
|
|
||||||
|
|
||||||
return (NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the file...
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
if ((tmpdir = getenv("TEMP")) == NULL)
|
if ((tmpdir = getenv("TEMP")) == NULL)
|
||||||
{
|
{
|
||||||
@ -779,98 +566,35 @@ pdfioFileCreateTemporary(
|
|||||||
tmpdir = "/tmp";
|
tmpdir = "/tmp";
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
for (i = 0; i < 1000; i ++)
|
for (i = 0, fd = -1; i < 1000; i ++)
|
||||||
{
|
{
|
||||||
_pdfioCryptoMakeRandom((uint8_t *)&tmpnum, sizeof(tmpnum));
|
_pdfioCryptoMakeRandom((uint8_t *)&tmpnum, sizeof(tmpnum));
|
||||||
snprintf(buffer, bufsize, "%s/%08x.pdf", tmpdir, tmpnum);
|
snprintf(buffer, bufsize, "%s/%08x.pdf", tmpdir, tmpnum);
|
||||||
if ((pdf->fd = open(buffer, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC | O_EXCL, 0666)) >= 0)
|
if ((fd = open(buffer, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC | O_EXCL, 0666)) >= 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdf->filename = strdup(buffer);
|
if (fd < 0)
|
||||||
|
|
||||||
if (i >= 1000)
|
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Unable to create file - %s", strerror(errno));
|
pdfio_file_t temp; // Dummy file
|
||||||
free(pdf->filename);
|
char message[8192]; // Message string
|
||||||
free(pdf);
|
|
||||||
*buffer = '\0';
|
temp.filename = (char *)"<temporary>";
|
||||||
|
snprintf(message, sizeof(message), "Unable to create temporary PDF file: %s", strerror(errno));
|
||||||
|
(error_cb)(&temp, message, error_cbdata);
|
||||||
|
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
pdf->version = strdup(version);
|
if ((pdf = create_common(buffer, fd, /*output_cb*/NULL, /*output_cbdata*/NULL, version, media_box, crop_box, error_cb, error_cbdata)) == NULL)
|
||||||
pdf->mode = _PDFIO_MODE_WRITE;
|
|
||||||
pdf->error_cb = error_cb;
|
|
||||||
pdf->error_data = error_data;
|
|
||||||
pdf->permissions = PDFIO_PERMISSION_ALL;
|
|
||||||
pdf->bufptr = pdf->buffer;
|
|
||||||
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
|
|
||||||
|
|
||||||
if (media_box)
|
|
||||||
{
|
{
|
||||||
pdf->media_box = *media_box;
|
// Remove the temporary file if we can't create the PDF file object...
|
||||||
}
|
close(fd);
|
||||||
else
|
unlink(buffer);
|
||||||
{
|
*buffer = '\0';
|
||||||
// Default to "universal" size (intersection of A4 and US Letter)
|
|
||||||
pdf->media_box.x2 = 210.0 * 72.0f / 25.4f;
|
|
||||||
pdf->media_box.y2 = 11.0f * 72.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crop_box)
|
|
||||||
{
|
|
||||||
pdf->crop_box = *crop_box;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Default to "universal" size (intersection of A4 and US Letter)
|
|
||||||
pdf->crop_box.x2 = 210.0 * 72.0f / 25.4f;
|
|
||||||
pdf->crop_box.y2 = 11.0f * 72.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a standard PDF header...
|
|
||||||
if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", version))
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
// Create the pages object...
|
|
||||||
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
pdfioDictSetName(dict, "Type", "Pages");
|
|
||||||
|
|
||||||
if ((pdf->pages_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
// Create the info object...
|
|
||||||
if ((info_dict = pdfioDictCreate(pdf)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
pdfioDictSetDate(info_dict, "CreationDate", time(NULL));
|
|
||||||
pdfioDictSetString(info_dict, "Producer", "pdfio/" PDFIO_VERSION);
|
|
||||||
|
|
||||||
if ((pdf->info_obj = pdfioFileCreateObj(pdf, info_dict)) == NULL)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
// Create random file ID values...
|
|
||||||
_pdfioCryptoMakeRandom(id_value, sizeof(id_value));
|
|
||||||
|
|
||||||
if ((pdf->id_array = pdfioArrayCreate(pdf)) != NULL)
|
|
||||||
{
|
|
||||||
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
|
|
||||||
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (pdf);
|
return (pdf);
|
||||||
|
|
||||||
// Common error handling code...
|
|
||||||
error:
|
|
||||||
|
|
||||||
pdfioFileClose(pdf);
|
|
||||||
|
|
||||||
unlink(buffer);
|
|
||||||
*buffer = '\0';
|
|
||||||
|
|
||||||
return (NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -930,11 +654,12 @@ pdfioFileFindObj(
|
|||||||
if ((current = number - 1) >= pdf->num_objs)
|
if ((current = number - 1) >= pdf->num_objs)
|
||||||
current = pdf->num_objs / 2;
|
current = pdf->num_objs / 2;
|
||||||
|
|
||||||
PDFIO_DEBUG("pdfioFileFindObj: objs[current=%lu]=%p\n", (unsigned long)current, (void *)pdf->objs[current]);
|
PDFIO_DEBUG("pdfioFileFindObj: objs[current=%lu]=%p(%lu)\n", (unsigned long)current, (void *)pdf->objs[current], (unsigned long)(pdf->objs[current] ? pdf->objs[current]->number : 0));
|
||||||
|
|
||||||
if (number == pdf->objs[current]->number)
|
if (number == pdf->objs[current]->number)
|
||||||
{
|
{
|
||||||
// Fast match...
|
// Fast match...
|
||||||
|
PDFIO_DEBUG("pdfioFileFindObj: Returning %lu (%p)\n", (unsigned long)current, pdf->objs[current]);
|
||||||
return (pdf->objs[current]);
|
return (pdf->objs[current]);
|
||||||
}
|
}
|
||||||
else if (number < pdf->objs[current]->number)
|
else if (number < pdf->objs[current]->number)
|
||||||
@ -961,11 +686,20 @@ pdfioFileFindObj(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (number == pdf->objs[left]->number)
|
if (number == pdf->objs[left]->number)
|
||||||
|
{
|
||||||
|
PDFIO_DEBUG("pdfioFileFindObj: Returning %lu (%p)\n", (unsigned long)left, pdf->objs[left]);
|
||||||
return (pdf->objs[left]);
|
return (pdf->objs[left]);
|
||||||
|
}
|
||||||
else if (number == pdf->objs[right]->number)
|
else if (number == pdf->objs[right]->number)
|
||||||
|
{
|
||||||
|
PDFIO_DEBUG("pdfioFileFindObj: Returning %lu (%p)\n", (unsigned long)right, pdf->objs[right]);
|
||||||
return (pdf->objs[right]);
|
return (pdf->objs[right]);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
PDFIO_DEBUG("pdfioFileFindObj: Returning NULL\n");
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -980,6 +714,19 @@ pdfioFileGetAuthor(pdfio_file_t *pdf) // I - PDF file
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'pdfioFileGetCatalog()' - Get the document catalog dictionary.
|
||||||
|
//
|
||||||
|
// @since PDFio 1.3@
|
||||||
|
//
|
||||||
|
|
||||||
|
pdfio_dict_t * // O - Catalog dictionary
|
||||||
|
pdfioFileGetCatalog(pdfio_file_t *pdf) // I - PDF file
|
||||||
|
{
|
||||||
|
return (pdf ? pdfioObjGetDict(pdf->root_obj) : NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 'pdfioFileGetCreationDate()' - Get the creation date for a PDF file.
|
// 'pdfioFileGetCreationDate()' - Get the creation date for a PDF file.
|
||||||
//
|
//
|
||||||
@ -1169,13 +916,13 @@ pdfioFileGetVersion(
|
|||||||
// This function opens an existing PDF file. The "filename" argument specifies
|
// This function opens an existing PDF file. The "filename" argument specifies
|
||||||
// the name of the PDF file to create.
|
// the name of the PDF file to create.
|
||||||
//
|
//
|
||||||
// The "password_cb" and "password_data" arguments specify a password callback
|
// The "password_cb" and "password_cbdata" arguments specify a password callback
|
||||||
// and its data pointer for PDF files that use one of the standard Adobe
|
// and its data pointer for PDF files that use one of the standard Adobe
|
||||||
// "security" handlers. The callback returns a password string or `NULL` to
|
// "security" handlers. The callback returns a password string or `NULL` to
|
||||||
// cancel the open. If `NULL` is specified for the callback function and the
|
// cancel the open. If `NULL` is specified for the callback function and the
|
||||||
// PDF file requires a password, the open will always fail.
|
// PDF file requires a password, the open will always fail.
|
||||||
//
|
//
|
||||||
// The "error_cb" and "error_data" arguments specify an error handler callback
|
// The "error_cb" and "error_cbdata" arguments specify an error handler callback
|
||||||
// and its data pointer - if `NULL` the default error handler is used that
|
// and its data pointer - if `NULL` the default error handler is used that
|
||||||
// writes error messages to `stderr`.
|
// writes error messages to `stderr`.
|
||||||
//
|
//
|
||||||
@ -1184,9 +931,10 @@ pdfio_file_t * // O - PDF file
|
|||||||
pdfioFileOpen(
|
pdfioFileOpen(
|
||||||
const char *filename, // I - Filename
|
const char *filename, // I - Filename
|
||||||
pdfio_password_cb_t password_cb, // I - Password callback or `NULL` for none
|
pdfio_password_cb_t password_cb, // I - Password callback or `NULL` for none
|
||||||
void *password_data, // I - Password callback data, if any
|
void *password_cbdata,
|
||||||
|
// I - Password callback data, if any
|
||||||
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
|
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
|
||||||
void *error_data) // I - Error callback data, if any
|
void *error_cbdata) // I - Error callback data, if any
|
||||||
{
|
{
|
||||||
pdfio_file_t *pdf; // PDF file
|
pdfio_file_t *pdf; // PDF file
|
||||||
char line[1025], // Line from file
|
char line[1025], // Line from file
|
||||||
@ -1196,14 +944,16 @@ pdfioFileOpen(
|
|||||||
off_t xref_offset; // Offset to xref table
|
off_t xref_offset; // Offset to xref table
|
||||||
|
|
||||||
|
|
||||||
|
PDFIO_DEBUG("pdfioFileOpen(filename=\"%s\", password_cb=%p, password_cbdata=%p, error_cb=%p, error_cbdata=%p)\n", filename, (void *)password_cb, (void *)password_cbdata, (void *)error_cb, (void *)error_cbdata);
|
||||||
|
|
||||||
// Range check input...
|
// Range check input...
|
||||||
if (!filename)
|
if (!filename)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
if (!error_cb)
|
if (!error_cb)
|
||||||
{
|
{
|
||||||
error_cb = _pdfioFileDefaultError;
|
error_cb = _pdfioFileDefaultError;
|
||||||
error_data = NULL;
|
error_cbdata = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate a PDF file structure...
|
// Allocate a PDF file structure...
|
||||||
@ -1214,14 +964,15 @@ pdfioFileOpen(
|
|||||||
|
|
||||||
temp.filename = (char *)filename;
|
temp.filename = (char *)filename;
|
||||||
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno));
|
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno));
|
||||||
(error_cb)(&temp, message, error_data);
|
(error_cb)(&temp, message, error_cbdata);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pdf->loc = get_lconv();
|
||||||
pdf->filename = strdup(filename);
|
pdf->filename = strdup(filename);
|
||||||
pdf->mode = _PDFIO_MODE_READ;
|
pdf->mode = _PDFIO_MODE_READ;
|
||||||
pdf->error_cb = error_cb;
|
pdf->error_cb = error_cb;
|
||||||
pdf->error_data = error_data;
|
pdf->error_data = error_cbdata;
|
||||||
pdf->permissions = PDFIO_PERMISSION_ALL;
|
pdf->permissions = PDFIO_PERMISSION_ALL;
|
||||||
|
|
||||||
// Open the file...
|
// Open the file...
|
||||||
@ -1277,7 +1028,7 @@ pdfioFileOpen(
|
|||||||
|
|
||||||
xref_offset = (off_t)strtol(ptr + 9, NULL, 10);
|
xref_offset = (off_t)strtol(ptr + 9, NULL, 10);
|
||||||
|
|
||||||
if (!load_xref(pdf, xref_offset, password_cb, password_data))
|
if (!load_xref(pdf, xref_offset, password_cb, password_cbdata))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
return (pdf);
|
return (pdf);
|
||||||
@ -1369,7 +1120,7 @@ pdfioFileSetPermissions(
|
|||||||
if (!pdf)
|
if (!pdf)
|
||||||
return (false);
|
return (false);
|
||||||
|
|
||||||
if (pdf->num_objs > 2) // First two objects are pages and info
|
if (pdf->num_objs > 3) // First three objects are pages, info, and root
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "You must call pdfioFileSetPermissions before adding any objects.");
|
_pdfioFileError(pdf, "You must call pdfioFileSetPermissions before adding any objects.");
|
||||||
return (false);
|
return (false);
|
||||||
@ -1531,6 +1282,144 @@ compare_objmaps(_pdfio_objmap_t *a, // I - First object map
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'create_common()' - Allocate and initialize a pdfio_file_t object for writing.
|
||||||
|
//
|
||||||
|
|
||||||
|
static pdfio_file_t * // O - New PDF file
|
||||||
|
create_common(
|
||||||
|
const char *filename, // I - Filename
|
||||||
|
int fd, // I - File descriptor, if any
|
||||||
|
pdfio_output_cb_t output_cb, // I - Output callback function, if any
|
||||||
|
void *output_cbdata, // I - Output callback data, if any
|
||||||
|
const char *version, // I - PDF version
|
||||||
|
pdfio_rect_t *media_box, // I - Media box or `NULL` for default
|
||||||
|
pdfio_rect_t *crop_box, // I - Crop box of `NULL` for default
|
||||||
|
pdfio_error_cb_t error_cb, // I - Error callback function
|
||||||
|
void *error_cbdata) // I - Error callback data
|
||||||
|
{
|
||||||
|
pdfio_file_t *pdf; // New PDF file
|
||||||
|
pdfio_dict_t *dict; // Dictionary
|
||||||
|
unsigned char id_value[16]; // File ID value
|
||||||
|
|
||||||
|
|
||||||
|
PDFIO_DEBUG("create_common(filename=\"%s\", fd=%d, output_cb=%p, output_cbdata=%p, version=\"%s\", media_box=%p, crop_box=%p, error_cb=%p, error_cbdata=%p)\n", filename, fd, (void *)output_cb, (void *)output_cbdata, version, (void *)media_box, (void *)crop_box, (void *)error_cb, (void *)error_cbdata);
|
||||||
|
|
||||||
|
// Range check input...
|
||||||
|
if (!filename || (fd < 0 && !output_cb))
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
if (!version)
|
||||||
|
version = "2.0";
|
||||||
|
|
||||||
|
if (!error_cb)
|
||||||
|
{
|
||||||
|
error_cb = _pdfioFileDefaultError;
|
||||||
|
error_cbdata = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a PDF file structure...
|
||||||
|
if ((pdf = (pdfio_file_t *)calloc(1, sizeof(pdfio_file_t))) == NULL)
|
||||||
|
{
|
||||||
|
pdfio_file_t temp; // Dummy file
|
||||||
|
char message[8192]; // Message string
|
||||||
|
|
||||||
|
temp.filename = (char *)filename;
|
||||||
|
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file: %s", strerror(errno));
|
||||||
|
(error_cb)(&temp, message, error_cbdata);
|
||||||
|
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize PDF object...
|
||||||
|
pdf->loc = get_lconv();
|
||||||
|
pdf->fd = fd;
|
||||||
|
pdf->output_cb = output_cb;
|
||||||
|
pdf->output_ctx = output_cbdata;
|
||||||
|
pdf->filename = strdup(filename);
|
||||||
|
pdf->version = strdup(version);
|
||||||
|
pdf->mode = _PDFIO_MODE_WRITE;
|
||||||
|
pdf->error_cb = error_cb;
|
||||||
|
pdf->error_data = error_cbdata;
|
||||||
|
pdf->permissions = PDFIO_PERMISSION_ALL;
|
||||||
|
pdf->bufptr = pdf->buffer;
|
||||||
|
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
|
||||||
|
|
||||||
|
if (media_box)
|
||||||
|
{
|
||||||
|
pdf->media_box = *media_box;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default to "universal" size (intersection of A4 and US Letter)
|
||||||
|
pdf->media_box.x2 = 210.0 * 72.0f / 25.4f;
|
||||||
|
pdf->media_box.y2 = 11.0f * 72.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crop_box)
|
||||||
|
{
|
||||||
|
pdf->crop_box = *crop_box;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default to "universal" size (intersection of A4 and US Letter)
|
||||||
|
pdf->crop_box.x2 = 210.0 * 72.0f / 25.4f;
|
||||||
|
pdf->crop_box.y2 = 11.0f * 72.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a standard PDF header...
|
||||||
|
if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", version))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
// Create the pages object...
|
||||||
|
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
pdfioDictSetName(dict, "Type", "Pages");
|
||||||
|
|
||||||
|
if ((pdf->pages_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
// Create the info object...
|
||||||
|
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
pdfioDictSetDate(dict, "CreationDate", time(NULL));
|
||||||
|
pdfioDictSetString(dict, "Producer", "pdfio/" PDFIO_VERSION);
|
||||||
|
|
||||||
|
if ((pdf->info_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
// Create the root object...
|
||||||
|
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
pdfioDictSetName(dict, "Type", "Catalog");
|
||||||
|
pdfioDictSetObj(dict, "Pages", pdf->pages_obj);
|
||||||
|
|
||||||
|
if ((pdf->root_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
// Create random file ID values...
|
||||||
|
_pdfioCryptoMakeRandom(id_value, sizeof(id_value));
|
||||||
|
|
||||||
|
if ((pdf->id_array = pdfioArrayCreate(pdf)) != NULL)
|
||||||
|
{
|
||||||
|
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
|
||||||
|
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (pdf);
|
||||||
|
|
||||||
|
// Common error handling code...
|
||||||
|
error:
|
||||||
|
|
||||||
|
pdfioFileClose(pdf);
|
||||||
|
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 'get_info_string()' - Get a string value from the Info dictionary.
|
// 'get_info_string()' - Get a string value from the Info dictionary.
|
||||||
//
|
//
|
||||||
@ -1543,36 +1432,35 @@ get_info_string(pdfio_file_t *pdf, // I - PDF file
|
|||||||
const char *key) // I - Dictionary key
|
const char *key) // I - Dictionary key
|
||||||
{
|
{
|
||||||
pdfio_dict_t *dict; // Info dictionary
|
pdfio_dict_t *dict; // Info dictionary
|
||||||
_pdfio_value_t *value; // Value
|
|
||||||
|
|
||||||
// Range check input...
|
// Range check input...
|
||||||
if (!pdf || !pdf->info_obj || (dict = pdfioObjGetDict(pdf->info_obj)) == NULL || (value = _pdfioDictGetValue(dict, key)) == NULL)
|
if (!pdf || !pdf->info_obj || (dict = pdfioObjGetDict(pdf->info_obj)) == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
// If we already have a value, return it...
|
|
||||||
if (value->type == PDFIO_VALTYPE_NAME || value->type == PDFIO_VALTYPE_STRING)
|
|
||||||
{
|
|
||||||
return (value->value.string);
|
|
||||||
}
|
|
||||||
else if (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(pdf, temp);
|
|
||||||
|
|
||||||
return (value->value.string);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
|
return (pdfioDictGetString(dict, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'get_lconv()' - Get any locale-specific numeric information.
|
||||||
|
//
|
||||||
|
|
||||||
|
static struct lconv * // O - Locale information or `NULL`
|
||||||
|
get_lconv(void)
|
||||||
|
{
|
||||||
|
struct lconv *loc; // Locale information
|
||||||
|
|
||||||
|
|
||||||
|
if ((loc = localeconv()) != NULL)
|
||||||
{
|
{
|
||||||
// Something else that is not a string...
|
PDFIO_DEBUG("get_lconv: loc=%p, loc->decimal_point=\"%s\"\n", loc, loc->decimal_point);
|
||||||
return (NULL);
|
|
||||||
|
if (!loc->decimal_point || !strcmp(loc->decimal_point, "."))
|
||||||
|
loc = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2218,30 +2106,6 @@ load_xref(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// 'write_catalog()' - Write the PDF root object/catalog.
|
|
||||||
//
|
|
||||||
|
|
||||||
static bool // O - `true` on success, `false` on failure
|
|
||||||
write_catalog(pdfio_file_t *pdf) // I - PDF file
|
|
||||||
{
|
|
||||||
pdfio_dict_t *dict; // Dictionary for catalog...
|
|
||||||
|
|
||||||
|
|
||||||
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
|
||||||
return (false);
|
|
||||||
|
|
||||||
pdfioDictSetName(dict, "Type", "Catalog");
|
|
||||||
pdfioDictSetObj(dict, "Pages", pdf->pages_obj);
|
|
||||||
// TODO: Add support for all of the root object dictionary keys
|
|
||||||
|
|
||||||
if ((pdf->root_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
|
|
||||||
return (false);
|
|
||||||
else
|
|
||||||
return (pdfioObjClose(pdf->root_obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 'write_pages()' - Write the PDF pages objects.
|
// 'write_pages()' - Write the PDF pages objects.
|
||||||
//
|
//
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// Private header file for PDFio.
|
// Private header file for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2023 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.
|
||||||
@ -20,6 +20,7 @@
|
|||||||
# 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>
|
||||||
@ -37,9 +38,11 @@
|
|||||||
# define unlink _unlink
|
# define unlink _unlink
|
||||||
# define vsnprintf _vsnprintf
|
# define vsnprintf _vsnprintf
|
||||||
# define write _write
|
# define write _write
|
||||||
# define F_OK 00 // POSIX parameters/flags
|
# ifndef F_OK
|
||||||
# define W_OK 02
|
# define F_OK 00 // POSIX parameters/flags
|
||||||
# define R_OK 04
|
# define W_OK 02
|
||||||
|
# define R_OK 04
|
||||||
|
# endif // !F_OK
|
||||||
# define O_RDONLY _O_RDONLY // Map standard POSIX open flags
|
# define O_RDONLY _O_RDONLY // Map standard POSIX open flags
|
||||||
# define O_WRONLY _O_WRONLY
|
# define O_WRONLY _O_WRONLY
|
||||||
# define O_CREAT _O_CREAT
|
# define O_CREAT _O_CREAT
|
||||||
@ -224,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
|
||||||
@ -322,6 +326,9 @@ 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 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;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// PDF stream functions for PDFio.
|
// PDF stream functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2023 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.
|
||||||
@ -408,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");
|
||||||
@ -434,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
|
||||||
@ -687,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...
|
||||||
@ -1069,11 +1072,6 @@ stream_read(pdfio_stream_t *st, // I - Stream
|
|||||||
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, 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 (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out)
|
|
||||||
{
|
|
||||||
_pdfioFileError(st->pdf, "Corrupt stream data.");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (st->flate.next_out - (Bytef *)buffer);
|
return (st->flate.next_out - (Bytef *)buffer);
|
||||||
}
|
}
|
||||||
|
453
pdfio-string.c
453
pdfio-string.c
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// 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.
|
||||||
@ -14,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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -32,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);
|
||||||
@ -43,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)
|
||||||
@ -65,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);
|
||||||
|
|
||||||
@ -120,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);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// PDF value functions for PDFio.
|
// PDF value functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2023 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.
|
||||||
@ -215,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 :
|
||||||
@ -497,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"))
|
||||||
{
|
{
|
||||||
|
5
pdfio.h
5
pdfio.h
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// Public header file for PDFio.
|
// Public header file for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2023 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.
|
||||||
@ -23,7 +23,7 @@ extern "C" {
|
|||||||
// Version number...
|
// Version number...
|
||||||
//
|
//
|
||||||
|
|
||||||
# define PDFIO_VERSION "1.2.0"
|
# define PDFIO_VERSION "1.3.1"
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -191,6 +191,7 @@ extern pdfio_obj_t *pdfioFileCreateStringObj(pdfio_file_t *pdf, const char *s) _
|
|||||||
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_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;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
LIBRARY pdfio1
|
LIBRARY pdfio1
|
||||||
VERSION 1.2
|
VERSION 1.3
|
||||||
EXPORTS
|
EXPORTS
|
||||||
_pdfioArrayDebug
|
_pdfioArrayDebug
|
||||||
|
_pdfioArrayDecrypt
|
||||||
_pdfioArrayDelete
|
_pdfioArrayDelete
|
||||||
_pdfioArrayGetValue
|
_pdfioArrayGetValue
|
||||||
_pdfioArrayRead
|
_pdfioArrayRead
|
||||||
@ -24,6 +25,7 @@ _pdfioCryptoSHA256Init
|
|||||||
_pdfioCryptoUnlock
|
_pdfioCryptoUnlock
|
||||||
_pdfioDictClear
|
_pdfioDictClear
|
||||||
_pdfioDictDebug
|
_pdfioDictDebug
|
||||||
|
_pdfioDictDecrypt
|
||||||
_pdfioDictDelete
|
_pdfioDictDelete
|
||||||
_pdfioDictGetValue
|
_pdfioDictGetValue
|
||||||
_pdfioDictRead
|
_pdfioDictRead
|
||||||
@ -61,9 +63,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
|
||||||
@ -190,6 +195,7 @@ pdfioFileCreateStringObj
|
|||||||
pdfioFileCreateTemporary
|
pdfioFileCreateTemporary
|
||||||
pdfioFileFindObj
|
pdfioFileFindObj
|
||||||
pdfioFileGetAuthor
|
pdfioFileGetAuthor
|
||||||
|
pdfioFileGetCatalog
|
||||||
pdfioFileGetCreationDate
|
pdfioFileGetCreationDate
|
||||||
pdfioFileGetCreator
|
pdfioFileGetCreator
|
||||||
pdfioFileGetID
|
pdfioFileGetID
|
||||||
|
@ -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.2.0</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>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
<copyright>Copyright © 2019-2024 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.2.0" />
|
<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>
|
||||||
|
@ -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.2.0</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>
|
||||||
|
16
runtests.bat
Normal file
16
runtests.bat
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
:: Script to run unit test program
|
||||||
|
::
|
||||||
|
:: Usage:
|
||||||
|
::
|
||||||
|
:: .\runtests.bat x64\{Debug|Release}
|
||||||
|
::
|
||||||
|
|
||||||
|
:: Copy dependent DLLs to the named build directory
|
||||||
|
echo Copying DLLs
|
||||||
|
copy packages\zlib_native.redist.1.2.11\build\native\bin\x64\Debug\*.dll %1
|
||||||
|
copy packages\zlib_native.redist.1.2.11\build\native\bin\x64\Release\*.dll %1
|
||||||
|
|
||||||
|
:: Run unit test program
|
||||||
|
echo Running %1\testpdfio.exe
|
||||||
|
cd %1
|
||||||
|
testpdfio.exe
|
173
testpdfio.c
173
testpdfio.c
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// Test program for PDFio.
|
// Test program for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2023 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.
|
||||||
@ -16,6 +16,7 @@
|
|||||||
#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
|
||||||
@ -26,7 +27,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
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);
|
||||||
@ -36,6 +37,7 @@ 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);
|
||||||
@ -47,7 +49,7 @@ 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);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -58,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"))
|
||||||
{
|
{
|
||||||
@ -79,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))
|
||||||
@ -360,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?
|
||||||
@ -378,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)
|
||||||
{
|
{
|
||||||
@ -1041,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))
|
||||||
@ -1060,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);
|
||||||
@ -1081,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))
|
||||||
@ -1100,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))
|
||||||
@ -1118,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))
|
||||||
@ -1136,7 +1155,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-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))
|
||||||
@ -1148,7 +1167,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, "<temporary>", outpdf, &num_pages, &first_image))
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
if (read_unit_file(temppdf, num_pages, first_image, false))
|
if (read_unit_file(temppdf, num_pages, first_image, false))
|
||||||
@ -1305,6 +1324,7 @@ 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
|
const char *s; // String
|
||||||
bool error = false; // Error callback data
|
bool error = false; // Error callback data
|
||||||
@ -1317,6 +1337,83 @@ 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...
|
// Verify metadata...
|
||||||
fputs("pdfioFileGetAuthor: ", stdout);
|
fputs("pdfioFileGetAuthor: ", stdout);
|
||||||
if ((s = pdfioFileGetAuthor(pdf)) != NULL && !strcmp(s, "Michael R Sweet"))
|
if ((s = pdfioFileGetAuthor(pdf)) != NULL && !strcmp(s, "Michael R Sweet"))
|
||||||
@ -1478,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.
|
||||||
//
|
//
|
||||||
@ -3243,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
|
||||||
@ -3252,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");
|
||||||
@ -3437,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
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// https://github.com/michaelrsweet/ttf
|
// https://github.com/michaelrsweet/ttf
|
||||||
//
|
//
|
||||||
// Copyright © 2018-2023 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.
|
||||||
@ -120,6 +120,7 @@ test_font(const char *filename) // I - Font filename
|
|||||||
|
|
||||||
|
|
||||||
printf("ttfCreate(\"%s\"): ", filename);
|
printf("ttfCreate(\"%s\"): ", filename);
|
||||||
|
fflush(stdout);
|
||||||
if ((font = ttfCreate(filename, 0, error_cb, NULL)) != NULL)
|
if ((font = ttfCreate(filename, 0, error_cb, NULL)) != NULL)
|
||||||
puts("PASS");
|
puts("PASS");
|
||||||
else
|
else
|
||||||
|
119
ttf.c
119
ttf.c
@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// https://github.com/michaelrsweet/ttf
|
// https://github.com/michaelrsweet/ttf
|
||||||
//
|
//
|
||||||
// Copyright © 2018-2023 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.
|
||||||
@ -62,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>
|
||||||
@ -99,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
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -254,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
|
||||||
@ -297,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
|
||||||
@ -550,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
|
||||||
@ -631,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
|
||||||
@ -1272,20 +1302,34 @@ 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);
|
||||||
|
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);
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
@ -1356,6 +1400,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.");
|
||||||
@ -1369,6 +1419,12 @@ 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);
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
@ -1376,6 +1432,13 @@ 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);
|
||||||
|
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)
|
||||||
@ -1426,6 +1489,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.");
|
||||||
@ -1439,6 +1508,12 @@ 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);
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
@ -1446,6 +1521,13 @@ 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);
|
||||||
|
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)
|
||||||
@ -1565,7 +1647,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
|
||||||
|
|
||||||
|
|
||||||
@ -1644,8 +1726,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 ++)
|
||||||
|
16
ttf.h
16
ttf.h
@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// https://github.com/michaelrsweet/ttf
|
// https://github.com/michaelrsweet/ttf
|
||||||
//
|
//
|
||||||
// Copyright © 2018-2023 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.
|
||||||
@ -22,12 +22,12 @@ extern "C" {
|
|||||||
// 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
|
||||||
@ -40,20 +40,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)
|
||||||
@ -66,7 +66,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
|
||||||
|
Reference in New Issue
Block a user