mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2025-04-06 17:06:45 +02:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
cbea3ecc2a | ||
|
130cef8702 | ||
|
0bd9edc845 | ||
|
fe755eac3d | ||
|
8cca645835 | ||
|
b8ea9ea064 | ||
|
2874022aa4 | ||
|
3befcf2fd5 | ||
|
3b2f7e21d9 | ||
|
7e01069c5a | ||
|
88839ccb56 | ||
|
ebd5aab39b | ||
|
71d33c03ff | ||
|
cfe91b4ea2 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,6 +16,7 @@
|
|||||||
/examples/md2pdf
|
/examples/md2pdf
|
||||||
/examples/pdf2text
|
/examples/pdf2text
|
||||||
/examples/pdfioinfo
|
/examples/pdfioinfo
|
||||||
|
/examples/pdfiomerge
|
||||||
/Makefile
|
/Makefile
|
||||||
/packages
|
/packages
|
||||||
/pdfio.pc
|
/pdfio.pc
|
||||||
|
20
CHANGES.md
20
CHANGES.md
@ -1,6 +1,26 @@
|
|||||||
Changes in PDFio
|
Changes in PDFio
|
||||||
================
|
================
|
||||||
|
|
||||||
|
|
||||||
|
v1.5.2 - YYYY-MM-DD
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- Updated maximum allowed PDF string size to 64k (Issue #117)
|
||||||
|
- Fixed form detection in `pdfioinfo` example code (Issue #114)
|
||||||
|
- Fixed parsing of certain date/time values (Issue #115)
|
||||||
|
- Fixed support for empty name values (Issue #116)
|
||||||
|
|
||||||
|
|
||||||
|
v1.5.1 - 2025-03-28
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- Fixed output of special characters in name values (Issue #106)
|
||||||
|
- Fixed output of special characters in string values (Issue #107)
|
||||||
|
- Fixed output of large integers in dictionaries (Issue #108)
|
||||||
|
- Fixed handling of 0-length streams (Issue #111)
|
||||||
|
- Fixed detection of UTF-16 Big-Endian strings (Issue #112)
|
||||||
|
|
||||||
|
|
||||||
v1.5.0 - 2025-03-06
|
v1.5.0 - 2025-03-06
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
203
configure
vendored
203
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.5.0.
|
# Generated by GNU Autoconf 2.71 for pdfio 1.5.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.5.0'
|
PACKAGE_VERSION='1.5.2'
|
||||||
PACKAGE_STRING='pdfio 1.5.0'
|
PACKAGE_STRING='pdfio 1.5.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'
|
||||||
|
|
||||||
@ -1295,7 +1295,7 @@ if test "$ac_init_help" = "long"; then
|
|||||||
# Omit some internal or obsolete options to make the list less imposing.
|
# Omit some internal or obsolete options to make the list less imposing.
|
||||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||||
cat <<_ACEOF
|
cat <<_ACEOF
|
||||||
\`configure' configures pdfio 1.5.0 to adapt to many kinds of systems.
|
\`configure' configures pdfio 1.5.2 to adapt to many kinds of systems.
|
||||||
|
|
||||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||||
|
|
||||||
@ -1361,7 +1361,7 @@ fi
|
|||||||
|
|
||||||
if test -n "$ac_init_help"; then
|
if test -n "$ac_init_help"; then
|
||||||
case $ac_init_help in
|
case $ac_init_help in
|
||||||
short | recursive ) echo "Configuration of pdfio 1.5.0:";;
|
short | recursive ) echo "Configuration of pdfio 1.5.2:";;
|
||||||
esac
|
esac
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
|
|
||||||
@ -1460,7 +1460,7 @@ fi
|
|||||||
test -n "$ac_init_help" && exit $ac_status
|
test -n "$ac_init_help" && exit $ac_status
|
||||||
if $ac_init_version; then
|
if $ac_init_version; then
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
pdfio configure 1.5.0
|
pdfio configure 1.5.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.
|
||||||
@ -1513,39 +1513,6 @@ fi
|
|||||||
|
|
||||||
} # ac_fn_c_try_compile
|
} # ac_fn_c_try_compile
|
||||||
|
|
||||||
# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
|
|
||||||
# -------------------------------------------------------
|
|
||||||
# Tests whether HEADER exists and can be compiled using the include files in
|
|
||||||
# INCLUDES, setting the cache variable VAR accordingly.
|
|
||||||
ac_fn_c_check_header_compile ()
|
|
||||||
{
|
|
||||||
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
|
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
|
|
||||||
printf %s "checking for $2... " >&6; }
|
|
||||||
if eval test \${$3+y}
|
|
||||||
then :
|
|
||||||
printf %s "(cached) " >&6
|
|
||||||
else $as_nop
|
|
||||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
|
||||||
/* end confdefs.h. */
|
|
||||||
$4
|
|
||||||
#include <$2>
|
|
||||||
_ACEOF
|
|
||||||
if ac_fn_c_try_compile "$LINENO"
|
|
||||||
then :
|
|
||||||
eval "$3=yes"
|
|
||||||
else $as_nop
|
|
||||||
eval "$3=no"
|
|
||||||
fi
|
|
||||||
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
|
|
||||||
fi
|
|
||||||
eval ac_res=\$$3
|
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
|
||||||
printf "%s\n" "$ac_res" >&6; }
|
|
||||||
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
|
|
||||||
|
|
||||||
} # ac_fn_c_check_header_compile
|
|
||||||
|
|
||||||
# ac_fn_c_try_link LINENO
|
# ac_fn_c_try_link LINENO
|
||||||
# -----------------------
|
# -----------------------
|
||||||
# Try to link conftest.$ac_ext, and return whether this succeeded.
|
# Try to link conftest.$ac_ext, and return whether this succeeded.
|
||||||
@ -1592,6 +1559,101 @@ fi
|
|||||||
as_fn_set_status $ac_retval
|
as_fn_set_status $ac_retval
|
||||||
|
|
||||||
} # ac_fn_c_try_link
|
} # ac_fn_c_try_link
|
||||||
|
|
||||||
|
# ac_fn_c_check_func LINENO FUNC VAR
|
||||||
|
# ----------------------------------
|
||||||
|
# Tests whether FUNC exists, setting the cache variable VAR accordingly
|
||||||
|
ac_fn_c_check_func ()
|
||||||
|
{
|
||||||
|
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
|
||||||
|
printf %s "checking for $2... " >&6; }
|
||||||
|
if eval test \${$3+y}
|
||||||
|
then :
|
||||||
|
printf %s "(cached) " >&6
|
||||||
|
else $as_nop
|
||||||
|
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
|
||||||
|
For example, HP-UX 11i <limits.h> declares gettimeofday. */
|
||||||
|
#define $2 innocuous_$2
|
||||||
|
|
||||||
|
/* System header to define __stub macros and hopefully few prototypes,
|
||||||
|
which can conflict with char $2 (); below. */
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#undef $2
|
||||||
|
|
||||||
|
/* Override any GCC internal prototype to avoid an error.
|
||||||
|
Use char because int might match the return type of a GCC
|
||||||
|
builtin and then its argument prototype would still apply. */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
#endif
|
||||||
|
char $2 ();
|
||||||
|
/* The GNU C library defines this for functions which it implements
|
||||||
|
to always fail with ENOSYS. Some functions are actually named
|
||||||
|
something starting with __ and the normal name is an alias. */
|
||||||
|
#if defined __stub_$2 || defined __stub___$2
|
||||||
|
choke me
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
return $2 ();
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ACEOF
|
||||||
|
if ac_fn_c_try_link "$LINENO"
|
||||||
|
then :
|
||||||
|
eval "$3=yes"
|
||||||
|
else $as_nop
|
||||||
|
eval "$3=no"
|
||||||
|
fi
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||||
|
conftest$ac_exeext conftest.$ac_ext
|
||||||
|
fi
|
||||||
|
eval ac_res=\$$3
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||||
|
printf "%s\n" "$ac_res" >&6; }
|
||||||
|
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
|
||||||
|
|
||||||
|
} # ac_fn_c_check_func
|
||||||
|
|
||||||
|
# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
|
||||||
|
# -------------------------------------------------------
|
||||||
|
# Tests whether HEADER exists and can be compiled using the include files in
|
||||||
|
# INCLUDES, setting the cache variable VAR accordingly.
|
||||||
|
ac_fn_c_check_header_compile ()
|
||||||
|
{
|
||||||
|
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
|
||||||
|
printf %s "checking for $2... " >&6; }
|
||||||
|
if eval test \${$3+y}
|
||||||
|
then :
|
||||||
|
printf %s "(cached) " >&6
|
||||||
|
else $as_nop
|
||||||
|
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
$4
|
||||||
|
#include <$2>
|
||||||
|
_ACEOF
|
||||||
|
if ac_fn_c_try_compile "$LINENO"
|
||||||
|
then :
|
||||||
|
eval "$3=yes"
|
||||||
|
else $as_nop
|
||||||
|
eval "$3=no"
|
||||||
|
fi
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
|
||||||
|
fi
|
||||||
|
eval ac_res=\$$3
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||||
|
printf "%s\n" "$ac_res" >&6; }
|
||||||
|
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
|
||||||
|
|
||||||
|
} # ac_fn_c_check_header_compile
|
||||||
ac_configure_args_raw=
|
ac_configure_args_raw=
|
||||||
for ac_arg
|
for ac_arg
|
||||||
do
|
do
|
||||||
@ -1616,7 +1678,7 @@ cat >config.log <<_ACEOF
|
|||||||
This file contains any messages produced by compilers while
|
This file contains any messages produced by compilers while
|
||||||
running configure, to aid debugging if configure makes a mistake.
|
running configure, to aid debugging if configure makes a mistake.
|
||||||
|
|
||||||
It was created by pdfio $as_me 1.5.0, which was
|
It was created by pdfio $as_me 1.5.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
|
||||||
@ -2372,9 +2434,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
PDFIO_VERSION="1.5.0"
|
PDFIO_VERSION="1.5.2"
|
||||||
PDFIO_VERSION_MAJOR="`echo 1.5.0 | awk -F. '{print $1}'`"
|
PDFIO_VERSION_MAJOR="`echo 1.5.2 | awk -F. '{print $1}'`"
|
||||||
PDFIO_VERSION_MINOR="`echo 1.5.0 | awk -F. '{printf("%d\n",$2);}'`"
|
PDFIO_VERSION_MINOR="`echo 1.5.2 | awk -F. '{printf("%d\n",$2);}'`"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -3877,6 +3939,56 @@ INSTALL="$(pwd)/install-sh"
|
|||||||
printf "%s\n" "using $INSTALL" >&6; }
|
printf "%s\n" "using $INSTALL" >&6; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ac_fn_c_check_func "$LINENO" "timegm" "ac_cv_func_timegm"
|
||||||
|
if test "x$ac_cv_func_timegm" = xyes
|
||||||
|
then :
|
||||||
|
|
||||||
|
|
||||||
|
printf "%s\n" "#define HAVE_TIMEGM 1" >>confdefs.h
|
||||||
|
|
||||||
|
CPPFLAGS="-DHAVE_TIMEGM=1 $CPPFLAGS"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for tm_gmtoff member in tm structure" >&5
|
||||||
|
printf %s "checking for tm_gmtoff member in tm structure... " >&6; }
|
||||||
|
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
|
||||||
|
struct tm t;
|
||||||
|
int o = t.tm_gmtoff;
|
||||||
|
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ACEOF
|
||||||
|
if ac_fn_c_try_compile "$LINENO"
|
||||||
|
then :
|
||||||
|
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||||
|
printf "%s\n" "yes" >&6; }
|
||||||
|
|
||||||
|
printf "%s\n" "#define HAVE_TM_GMTOFF 1" >>confdefs.h
|
||||||
|
|
||||||
|
CPPFLAGS="-DHAVE_TM_GMTOFF=1 $CPPFLAGS"
|
||||||
|
|
||||||
|
else $as_nop
|
||||||
|
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||||
|
printf "%s\n" "no" >&6; }
|
||||||
|
|
||||||
|
fi
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
|
||||||
|
|
||||||
|
|
||||||
if test -n "$ac_tool_prefix"; then
|
if test -n "$ac_tool_prefix"; then
|
||||||
# Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
|
# Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
|
||||||
set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
|
set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
|
||||||
@ -3998,7 +4110,6 @@ PKGCONFIG_REQUIRES="zlib"
|
|||||||
|
|
||||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib via pkg-config" >&5
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib via pkg-config" >&5
|
||||||
printf %s "checking for zlib via pkg-config... " >&6; }
|
printf %s "checking for zlib via pkg-config... " >&6; }
|
||||||
|
|
||||||
ac_header= ac_cache=
|
ac_header= ac_cache=
|
||||||
for ac_item in $ac_header_c_list
|
for ac_item in $ac_header_c_list
|
||||||
do
|
do
|
||||||
@ -4988,7 +5099,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
|||||||
# report actual input values of CONFIG_FILES etc. instead of their
|
# report actual input values of CONFIG_FILES etc. instead of their
|
||||||
# values after options handling.
|
# values after options handling.
|
||||||
ac_log="
|
ac_log="
|
||||||
This file was extended by pdfio $as_me 1.5.0, which was
|
This file was extended by pdfio $as_me 1.5.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
|
||||||
@ -5044,7 +5155,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
|
|||||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||||
ac_cs_config='$ac_cs_config_escaped'
|
ac_cs_config='$ac_cs_config_escaped'
|
||||||
ac_cs_version="\\
|
ac_cs_version="\\
|
||||||
pdfio config.status 1.5.0
|
pdfio config.status 1.5.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\\"
|
||||||
|
|
||||||
|
23
configure.ac
23
configure.ac
@ -21,7 +21,7 @@ AC_PREREQ([2.70])
|
|||||||
|
|
||||||
|
|
||||||
dnl Package name and version...
|
dnl Package name and version...
|
||||||
AC_INIT([pdfio], [1.5.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
|
AC_INIT([pdfio], [1.5.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}'`"
|
||||||
@ -88,6 +88,27 @@ AC_SUBST([INSTALL])
|
|||||||
AC_MSG_RESULT([using $INSTALL])
|
AC_MSG_RESULT([using $INSTALL])
|
||||||
|
|
||||||
|
|
||||||
|
dnl Check for date/time functionality...
|
||||||
|
AC_CHECK_FUNC([timegm], [
|
||||||
|
AC_DEFINE([HAVE_TIMEGM], [1], [Do we have the timegm function?])
|
||||||
|
CPPFLAGS="-DHAVE_TIMEGM=1 $CPPFLAGS"
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([for tm_gmtoff member in tm structure])
|
||||||
|
AC_COMPILE_IFELSE([
|
||||||
|
AC_LANG_PROGRAM([[#include <time.h>]], [[
|
||||||
|
struct tm t;
|
||||||
|
int o = t.tm_gmtoff;
|
||||||
|
]])
|
||||||
|
], [
|
||||||
|
AC_MSG_RESULT([yes])
|
||||||
|
AC_DEFINE([HAVE_TM_GMTOFF], [1], [Have tm_gmtoff member in struct tm?])
|
||||||
|
CPPFLAGS="-DHAVE_TM_GMTOFF=1 $CPPFLAGS"
|
||||||
|
], [
|
||||||
|
AC_MSG_RESULT([no])
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
dnl Check for pkg-config, which is used for some other tests later on...
|
dnl Check for pkg-config, which is used for some other tests later on...
|
||||||
AC_PATH_TOOL([PKGCONFIG], [pkg-config])
|
AC_PATH_TOOL([PKGCONFIG], [pkg-config])
|
||||||
|
|
||||||
|
119
doc/pdfio.3
119
doc/pdfio.3
@ -1,4 +1,4 @@
|
|||||||
.TH pdfio 3 "pdf read/write library" "2025-03-06" "pdf read/write library"
|
.TH pdfio 3 "pdf read/write library" "2025-04-04" "pdf read/write library"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
pdfio \- pdf read/write library
|
pdfio \- pdf read/write library
|
||||||
.SH Introduction
|
.SH Introduction
|
||||||
@ -1047,11 +1047,26 @@ The pdfioinfo.c example program opens a PDF file and prints the title, author, c
|
|||||||
{
|
{
|
||||||
const char *filename; // PDF filename
|
const char *filename; // PDF filename
|
||||||
pdfio_file_t *pdf; // PDF file
|
pdfio_file_t *pdf; // PDF file
|
||||||
const char *author; // Author name
|
pdfio_dict_t *catalog; // Catalog dictionary
|
||||||
time_t creation_date; // Creation date
|
const char *author, // Author name
|
||||||
struct tm *creation_tm; // Creation date/time information
|
*creator, // Creator name
|
||||||
char creation_text[256]; // Creation date/time as a string
|
*producer, // Producer name
|
||||||
const char *title; // Title
|
*title; // Title
|
||||||
|
time_t creation_date, // Creation date
|
||||||
|
modification_date; // Modification date
|
||||||
|
struct tm *creation_tm, // Creation date/time information
|
||||||
|
*modification_tm; // Modification date/time information
|
||||||
|
char creation_text[256], // Creation date/time as a string
|
||||||
|
modification_text[256], // Modification date/time human fmt string
|
||||||
|
range_text[255]; // Page range text
|
||||||
|
size_t num_pages; // PDF number of pages
|
||||||
|
bool has_acroform; // Does the file have an AcroForm?
|
||||||
|
pdfio_obj_t *page; // Object
|
||||||
|
pdfio_dict_t *page_dict; // Object dictionary
|
||||||
|
size_t cur, // Current page index
|
||||||
|
prev; // Previous page index
|
||||||
|
pdfio_rect_t cur_box, // Current MediaBox
|
||||||
|
prev_box; // Previous MediaBox
|
||||||
|
|
||||||
|
|
||||||
// Get the filename from the command\-line...
|
// Get the filename from the command\-line...
|
||||||
@ -1064,14 +1079,20 @@ The pdfioinfo.c example program opens a PDF file and prints the title, author, c
|
|||||||
filename = argv[1];
|
filename = argv[1];
|
||||||
|
|
||||||
// Open the PDF file with the default callbacks...
|
// Open the PDF file with the default callbacks...
|
||||||
pdf = pdfioFileOpen(filename, /*password_cb*/NULL, /*password_cbdata*/NULL,
|
pdf = pdfioFileOpen(filename, /*password_cb*/NULL,
|
||||||
/*error_cb*/NULL, /*error_cbdata*/NULL);
|
/*password_cbdata*/NULL, /*error_cb*/NULL,
|
||||||
|
/*error_cbdata*/NULL);
|
||||||
if (pdf == NULL)
|
if (pdf == NULL)
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
// Get the title and author...
|
// Get the title, author, etc...
|
||||||
author = pdfioFileGetAuthor(pdf);
|
catalog = pdfioFileGetCatalog(pdf);
|
||||||
title = pdfioFileGetTitle(pdf);
|
author = pdfioFileGetAuthor(pdf);
|
||||||
|
creator = pdfioFileGetCreator(pdf);
|
||||||
|
has_acroform = pdfioDictGetType(catalog, "AcroForm") != PDFIO_VALTYPE_NONE;
|
||||||
|
num_pages = pdfioFileGetNumPages(pdf);
|
||||||
|
producer = pdfioFileGetProducer(pdf);
|
||||||
|
title = pdfioFileGetTitle(pdf);
|
||||||
|
|
||||||
// Get the creation date and convert to a string...
|
// Get the creation date and convert to a string...
|
||||||
if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0)
|
if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0)
|
||||||
@ -1084,12 +1105,76 @@ The pdfioinfo.c example program opens a PDF file and prints the title, author, c
|
|||||||
snprintf(creation_text, sizeof(creation_text), "\-\- not set \-\-");
|
snprintf(creation_text, sizeof(creation_text), "\-\- not set \-\-");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the modification date and convert to a string...
|
||||||
|
if ((modification_date = pdfioFileGetModificationDate(pdf)) > 0)
|
||||||
|
{
|
||||||
|
modification_tm = localtime(&modification_date);
|
||||||
|
strftime(modification_text, sizeof(modification_text), "%c", modification_tm);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(modification_text, sizeof(modification_text), "\-\- not set \-\-");
|
||||||
|
}
|
||||||
|
|
||||||
// Print file information to stdout...
|
// Print file information to stdout...
|
||||||
printf("%s:\\n", filename);
|
printf("%s:\\n", filename);
|
||||||
printf(" Title: %s\\n", title ? title : "\-\- not set \-\-");
|
printf(" Title: %s\\n", title ? title : "\-\- not set \-\-");
|
||||||
printf(" Author: %s\\n", author ? author : "\-\- not set \-\-");
|
printf(" Author: %s\\n", author ? author : "\-\- not set \-\-");
|
||||||
printf(" Created On: %s\\n", creation_text);
|
printf(" Creator: %s\\n", creator ? creator : "\-\- not set \-\-");
|
||||||
printf(" Number Pages: %u\\n", (unsigned)pdfioFileGetNumPages(pdf));
|
printf(" Producer: %s\\n", producer ? producer : "\-\- not set \-\-");
|
||||||
|
printf(" Created On: %s\\n", creation_text);
|
||||||
|
printf(" Modified On: %s\\n", modification_text);
|
||||||
|
printf(" Version: %s\\n", pdfioFileGetVersion(pdf));
|
||||||
|
printf(" AcroForm: %s\\n", has_acroform ? "Yes" : "No");
|
||||||
|
printf(" Number of Pages: %u\\n", (unsigned)num_pages);
|
||||||
|
|
||||||
|
// Report the MediaBox for all of the pages
|
||||||
|
prev_box.x1 = prev_box.x2 = prev_box.y1 = prev_box.y2 = 0.0;
|
||||||
|
|
||||||
|
for (cur = 0, prev = 0; cur < num_pages; cur ++)
|
||||||
|
{
|
||||||
|
// Find the MediaBox for this page in the page tree...
|
||||||
|
for (page = pdfioFileGetPage(pdf, cur);
|
||||||
|
page != NULL;
|
||||||
|
page = pdfioDictGetObj(page_dict, "Parent"))
|
||||||
|
{
|
||||||
|
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
|
||||||
|
page_dict = pdfioObjGetDict(page);
|
||||||
|
|
||||||
|
if (pdfioDictGetRect(page_dict, "MediaBox", &cur_box))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this MediaBox is different from the previous one, show the range of
|
||||||
|
// pages that have that size...
|
||||||
|
if (cur == 0 ||
|
||||||
|
fabs(cur_box.x1 \- prev_box.x1) > 0.01 ||
|
||||||
|
fabs(cur_box.y1 \- prev_box.y1) > 0.01 ||
|
||||||
|
fabs(cur_box.x2 \- prev_box.x2) > 0.01 ||
|
||||||
|
fabs(cur_box.y2 \- prev_box.y2) > 0.01)
|
||||||
|
{
|
||||||
|
if (cur > prev)
|
||||||
|
{
|
||||||
|
snprintf(range_text, sizeof(range_text), "Pages %u\-%u",
|
||||||
|
(unsigned)(prev + 1), (unsigned)cur);
|
||||||
|
printf("%16s: [%g %g %g %g]\\n", range_text,
|
||||||
|
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new series of pages with the new size...
|
||||||
|
prev = cur;
|
||||||
|
prev_box = cur_box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the last range as needed...
|
||||||
|
if (cur > prev)
|
||||||
|
{
|
||||||
|
snprintf(range_text, sizeof(range_text), "Pages %u\-%u",
|
||||||
|
(unsigned)(prev + 1), (unsigned)cur);
|
||||||
|
printf("%16s: [%g %g %g %g]\\n", range_text,
|
||||||
|
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
|
||||||
|
}
|
||||||
|
|
||||||
// Close the PDF file...
|
// Close the PDF file...
|
||||||
pdfioFileClose(pdf);
|
pdfioFileClose(pdf);
|
||||||
@ -4590,6 +4675,10 @@ bool pdfioStreamPrintf (
|
|||||||
...
|
...
|
||||||
);
|
);
|
||||||
.fi
|
.fi
|
||||||
|
.PP
|
||||||
|
This function writes a formatted string to a stream. In addition to the
|
||||||
|
standard \fBprintf\fR format characters, you can use "%N" to format a PDF name
|
||||||
|
value ("/Name") and "%S" to format a PDF string ("(String)") value.
|
||||||
.SS pdfioStreamPutChar
|
.SS pdfioStreamPutChar
|
||||||
Write a single character to a stream.
|
Write a single character to a stream.
|
||||||
.PP
|
.PP
|
||||||
|
123
doc/pdfio.html
123
doc/pdfio.html
@ -1,13 +1,13 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en-US">
|
<html lang="en-US">
|
||||||
<head>
|
<head>
|
||||||
<title>PDFio Programming Manual v1.5.0</title>
|
<title>PDFio Programming Manual v1.5.2</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||||
<meta name="generator" content="codedoc v3.8">
|
<meta name="generator" content="codedoc v3.8">
|
||||||
<meta name="author" content="Michael R Sweet">
|
<meta name="author" content="Michael R Sweet">
|
||||||
<meta name="language" content="en-US">
|
<meta name="language" content="en-US">
|
||||||
<meta name="copyright" content="Copyright © 2021-2025 by Michael R Sweet">
|
<meta name="copyright" content="Copyright © 2021-2025 by Michael R Sweet">
|
||||||
<meta name="version" content="1.5.0">
|
<meta name="version" content="1.5.2">
|
||||||
<style type="text/css"><!--
|
<style type="text/css"><!--
|
||||||
body {
|
body {
|
||||||
background: white;
|
background: white;
|
||||||
@ -251,7 +251,7 @@ span.string {
|
|||||||
<body>
|
<body>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<p><img class="title" src="pdfio-512.png"></p>
|
<p><img class="title" src="pdfio-512.png"></p>
|
||||||
<h1 class="title">PDFio Programming Manual v1.5.0</h1>
|
<h1 class="title">PDFio Programming Manual v1.5.2</h1>
|
||||||
<p>Michael R Sweet</p>
|
<p>Michael R Sweet</p>
|
||||||
<p>Copyright © 2021-2025 by Michael R Sweet</p>
|
<p>Copyright © 2021-2025 by Michael R Sweet</p>
|
||||||
</div>
|
</div>
|
||||||
@ -1165,11 +1165,26 @@ main(<span class="reserved">int</span> argc, <span clas
|
|||||||
{
|
{
|
||||||
<span class="reserved">const</span> <span class="reserved">char</span> *filename; <span class="comment">// PDF filename</span>
|
<span class="reserved">const</span> <span class="reserved">char</span> *filename; <span class="comment">// PDF filename</span>
|
||||||
pdfio_file_t *pdf; <span class="comment">// PDF file</span>
|
pdfio_file_t *pdf; <span class="comment">// PDF file</span>
|
||||||
<span class="reserved">const</span> <span class="reserved">char</span> *author; <span class="comment">// Author name</span>
|
pdfio_dict_t *catalog; <span class="comment">// Catalog dictionary</span>
|
||||||
time_t creation_date; <span class="comment">// Creation date</span>
|
<span class="reserved">const</span> <span class="reserved">char</span> *author, <span class="comment">// Author name</span>
|
||||||
<span class="reserved">struct</span> tm *creation_tm; <span class="comment">// Creation date/time information</span>
|
*creator, <span class="comment">// Creator name</span>
|
||||||
<span class="reserved">char</span> creation_text[<span class="number">256</span>]; <span class="comment">// Creation date/time as a string</span>
|
*producer, <span class="comment">// Producer name</span>
|
||||||
<span class="reserved">const</span> <span class="reserved">char</span> *title; <span class="comment">// Title</span>
|
*title; <span class="comment">// Title</span>
|
||||||
|
time_t creation_date, <span class="comment">// Creation date</span>
|
||||||
|
modification_date; <span class="comment">// Modification date</span>
|
||||||
|
<span class="reserved">struct</span> tm *creation_tm, <span class="comment">// Creation date/time information</span>
|
||||||
|
*modification_tm; <span class="comment">// Modification date/time information</span>
|
||||||
|
<span class="reserved">char</span> creation_text[<span class="number">256</span>], <span class="comment">// Creation date/time as a string</span>
|
||||||
|
modification_text[<span class="number">256</span>], <span class="comment">// Modification date/time human fmt string</span>
|
||||||
|
range_text[<span class="number">255</span>]; <span class="comment">// Page range text</span>
|
||||||
|
size_t num_pages; <span class="comment">// PDF number of pages</span>
|
||||||
|
<span class="reserved">bool</span> has_acroform; <span class="comment">// Does the file have an AcroForm?</span>
|
||||||
|
pdfio_obj_t *page; <span class="comment">// Object</span>
|
||||||
|
pdfio_dict_t *page_dict; <span class="comment">// Object dictionary</span>
|
||||||
|
size_t cur, <span class="comment">// Current page index</span>
|
||||||
|
prev; <span class="comment">// Previous page index</span>
|
||||||
|
pdfio_rect_t cur_box, <span class="comment">// Current MediaBox</span>
|
||||||
|
prev_box; <span class="comment">// Previous MediaBox</span>
|
||||||
|
|
||||||
|
|
||||||
<span class="comment">// Get the filename from the command-line...</span>
|
<span class="comment">// Get the filename from the command-line...</span>
|
||||||
@ -1182,14 +1197,20 @@ main(<span class="reserved">int</span> argc, <span clas
|
|||||||
filename = argv[<span class="number">1</span>];
|
filename = argv[<span class="number">1</span>];
|
||||||
|
|
||||||
<span class="comment">// Open the PDF file with the default callbacks...</span>
|
<span class="comment">// Open the PDF file with the default callbacks...</span>
|
||||||
pdf = pdfioFileOpen(filename, <span class="comment">/*password_cb*/</span>NULL, <span class="comment">/*password_cbdata*/</span>NULL,
|
pdf = pdfioFileOpen(filename, <span class="comment">/*password_cb*/</span>NULL,
|
||||||
<span class="comment">/*error_cb*/</span>NULL, <span class="comment">/*error_cbdata*/</span>NULL);
|
<span class="comment">/*password_cbdata*/</span>NULL, <span class="comment">/*error_cb*/</span>NULL,
|
||||||
|
<span class="comment">/*error_cbdata*/</span>NULL);
|
||||||
<span class="reserved">if</span> (pdf == NULL)
|
<span class="reserved">if</span> (pdf == NULL)
|
||||||
<span class="reserved">return</span> (<span class="number">1</span>);
|
<span class="reserved">return</span> (<span class="number">1</span>);
|
||||||
|
|
||||||
<span class="comment">// Get the title and author...</span>
|
<span class="comment">// Get the title, author, etc...</span>
|
||||||
author = pdfioFileGetAuthor(pdf);
|
catalog = pdfioFileGetCatalog(pdf);
|
||||||
title = pdfioFileGetTitle(pdf);
|
author = pdfioFileGetAuthor(pdf);
|
||||||
|
creator = pdfioFileGetCreator(pdf);
|
||||||
|
has_acroform = pdfioDictGetType(catalog, <span class="string">"AcroForm"</span>) != PDFIO_VALTYPE_NONE;
|
||||||
|
num_pages = pdfioFileGetNumPages(pdf);
|
||||||
|
producer = pdfioFileGetProducer(pdf);
|
||||||
|
title = pdfioFileGetTitle(pdf);
|
||||||
|
|
||||||
<span class="comment">// Get the creation date and convert to a string...</span>
|
<span class="comment">// Get the creation date and convert to a string...</span>
|
||||||
<span class="reserved">if</span> ((creation_date = pdfioFileGetCreationDate(pdf)) > <span class="number">0</span>)
|
<span class="reserved">if</span> ((creation_date = pdfioFileGetCreationDate(pdf)) > <span class="number">0</span>)
|
||||||
@ -1202,12 +1223,76 @@ main(<span class="reserved">int</span> argc, <span clas
|
|||||||
snprintf(creation_text, <span class="reserved">sizeof</span>(creation_text), <span class="string">"-- not set --"</span>);
|
snprintf(creation_text, <span class="reserved">sizeof</span>(creation_text), <span class="string">"-- not set --"</span>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<span class="comment">// Get the modification date and convert to a string...</span>
|
||||||
|
<span class="reserved">if</span> ((modification_date = pdfioFileGetModificationDate(pdf)) > <span class="number">0</span>)
|
||||||
|
{
|
||||||
|
modification_tm = localtime(&modification_date);
|
||||||
|
strftime(modification_text, <span class="reserved">sizeof</span>(modification_text), <span class="string">"%c"</span>, modification_tm);
|
||||||
|
}
|
||||||
|
<span class="reserved">else</span>
|
||||||
|
{
|
||||||
|
snprintf(modification_text, <span class="reserved">sizeof</span>(modification_text), <span class="string">"-- not set --"</span>);
|
||||||
|
}
|
||||||
|
|
||||||
<span class="comment">// Print file information to stdout...</span>
|
<span class="comment">// Print file information to stdout...</span>
|
||||||
printf(<span class="string">"%s:\n"</span>, filename);
|
printf(<span class="string">"%s:\n"</span>, filename);
|
||||||
printf(<span class="string">" Title: %s\n"</span>, title ? title : <span class="string">"-- not set --"</span>);
|
printf(<span class="string">" Title: %s\n"</span>, title ? title : <span class="string">"-- not set --"</span>);
|
||||||
printf(<span class="string">" Author: %s\n"</span>, author ? author : <span class="string">"-- not set --"</span>);
|
printf(<span class="string">" Author: %s\n"</span>, author ? author : <span class="string">"-- not set --"</span>);
|
||||||
printf(<span class="string">" Created On: %s\n"</span>, creation_text);
|
printf(<span class="string">" Creator: %s\n"</span>, creator ? creator : <span class="string">"-- not set --"</span>);
|
||||||
printf(<span class="string">" Number Pages: %u\n"</span>, (<span class="reserved">unsigned</span>)pdfioFileGetNumPages(pdf));
|
printf(<span class="string">" Producer: %s\n"</span>, producer ? producer : <span class="string">"-- not set --"</span>);
|
||||||
|
printf(<span class="string">" Created On: %s\n"</span>, creation_text);
|
||||||
|
printf(<span class="string">" Modified On: %s\n"</span>, modification_text);
|
||||||
|
printf(<span class="string">" Version: %s\n"</span>, pdfioFileGetVersion(pdf));
|
||||||
|
printf(<span class="string">" AcroForm: %s\n"</span>, has_acroform ? <span class="string">"Yes"</span> : <span class="string">"No"</span>);
|
||||||
|
printf(<span class="string">" Number of Pages: %u\n"</span>, (<span class="reserved">unsigned</span>)num_pages);
|
||||||
|
|
||||||
|
<span class="comment">// Report the MediaBox for all of the pages</span>
|
||||||
|
prev_box.x1 = prev_box.x2 = prev_box.y1 = prev_box.y2 = <span class="number">0.0</span>;
|
||||||
|
|
||||||
|
<span class="reserved">for</span> (cur = <span class="number">0</span>, prev = <span class="number">0</span>; cur < num_pages; cur ++)
|
||||||
|
{
|
||||||
|
<span class="comment">// Find the MediaBox for this page in the page tree...</span>
|
||||||
|
<span class="reserved">for</span> (page = pdfioFileGetPage(pdf, cur);
|
||||||
|
page != NULL;
|
||||||
|
page = pdfioDictGetObj(page_dict, <span class="string">"Parent"</span>))
|
||||||
|
{
|
||||||
|
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = <span class="number">0.0</span>;
|
||||||
|
page_dict = pdfioObjGetDict(page);
|
||||||
|
|
||||||
|
<span class="reserved">if</span> (pdfioDictGetRect(page_dict, <span class="string">"MediaBox"</span>, &cur_box))
|
||||||
|
<span class="reserved">break</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
<span class="comment">// If this MediaBox is different from the previous one, show the range of</span>
|
||||||
|
<span class="comment">// pages that have that size...</span>
|
||||||
|
<span class="reserved">if</span> (cur == <span class="number">0</span> ||
|
||||||
|
fabs(cur_box.x1 - prev_box.x1) > <span class="number">0.01</span> ||
|
||||||
|
fabs(cur_box.y1 - prev_box.y1) > <span class="number">0.01</span> ||
|
||||||
|
fabs(cur_box.x2 - prev_box.x2) > <span class="number">0.01</span> ||
|
||||||
|
fabs(cur_box.y2 - prev_box.y2) > <span class="number">0.01</span>)
|
||||||
|
{
|
||||||
|
<span class="reserved">if</span> (cur > prev)
|
||||||
|
{
|
||||||
|
snprintf(range_text, <span class="reserved">sizeof</span>(range_text), <span class="string">"Pages %u-%u"</span>,
|
||||||
|
(<span class="reserved">unsigned</span>)(prev + <span class="number">1</span>), (<span class="reserved">unsigned</span>)cur);
|
||||||
|
printf(<span class="string">"%16s: [%g %g %g %g]\n"</span>, range_text,
|
||||||
|
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
<span class="comment">// Start a new series of pages with the new size...</span>
|
||||||
|
prev = cur;
|
||||||
|
prev_box = cur_box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<span class="comment">// Show the last range as needed...</span>
|
||||||
|
<span class="reserved">if</span> (cur > prev)
|
||||||
|
{
|
||||||
|
snprintf(range_text, <span class="reserved">sizeof</span>(range_text), <span class="string">"Pages %u-%u"</span>,
|
||||||
|
(<span class="reserved">unsigned</span>)(prev + <span class="number">1</span>), (<span class="reserved">unsigned</span>)cur);
|
||||||
|
printf(<span class="string">"%16s: [%g %g %g %g]\n"</span>, range_text,
|
||||||
|
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
|
||||||
|
}
|
||||||
|
|
||||||
<span class="comment">// Close the PDF file...</span>
|
<span class="comment">// Close the PDF file...</span>
|
||||||
pdfioFileClose(pdf);
|
pdfioFileClose(pdf);
|
||||||
@ -5081,6 +5166,10 @@ ssize_t pdfioStreamPeek(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, <span
|
|||||||
</tbody></table>
|
</tbody></table>
|
||||||
<h4 class="returnvalue">Return Value</h4>
|
<h4 class="returnvalue">Return Value</h4>
|
||||||
<p class="description"><code>true</code> on success, <code>false</code> on failure</p>
|
<p class="description"><code>true</code> on success, <code>false</code> on failure</p>
|
||||||
|
<h4 class="discussion">Discussion</h4>
|
||||||
|
<p class="discussion">This function writes a formatted string to a stream. In addition to the
|
||||||
|
standard <code>printf</code> format characters, you can use "%N" to format a PDF name
|
||||||
|
value ("/Name") and "%S" to format a PDF string ("(String)") value.</p>
|
||||||
<h3 class="function"><a id="pdfioStreamPutChar">pdfioStreamPutChar</a></h3>
|
<h3 class="function"><a id="pdfioStreamPutChar">pdfioStreamPutChar</a></h3>
|
||||||
<p class="description">Write a single character to a stream.</p>
|
<p class="description">Write a single character to a stream.</p>
|
||||||
<p class="code">
|
<p class="code">
|
||||||
|
113
doc/pdfio.md
113
doc/pdfio.md
@ -889,11 +889,26 @@ main(int argc, // I - Number of command-line arguments
|
|||||||
{
|
{
|
||||||
const char *filename; // PDF filename
|
const char *filename; // PDF filename
|
||||||
pdfio_file_t *pdf; // PDF file
|
pdfio_file_t *pdf; // PDF file
|
||||||
const char *author; // Author name
|
pdfio_dict_t *catalog; // Catalog dictionary
|
||||||
time_t creation_date; // Creation date
|
const char *author, // Author name
|
||||||
struct tm *creation_tm; // Creation date/time information
|
*creator, // Creator name
|
||||||
char creation_text[256]; // Creation date/time as a string
|
*producer, // Producer name
|
||||||
const char *title; // Title
|
*title; // Title
|
||||||
|
time_t creation_date, // Creation date
|
||||||
|
modification_date; // Modification date
|
||||||
|
struct tm *creation_tm, // Creation date/time information
|
||||||
|
*modification_tm; // Modification date/time information
|
||||||
|
char creation_text[256], // Creation date/time as a string
|
||||||
|
modification_text[256], // Modification date/time human fmt string
|
||||||
|
range_text[255]; // Page range text
|
||||||
|
size_t num_pages; // PDF number of pages
|
||||||
|
bool has_acroform; // Does the file have an AcroForm?
|
||||||
|
pdfio_obj_t *page; // Object
|
||||||
|
pdfio_dict_t *page_dict; // Object dictionary
|
||||||
|
size_t cur, // Current page index
|
||||||
|
prev; // Previous page index
|
||||||
|
pdfio_rect_t cur_box, // Current MediaBox
|
||||||
|
prev_box; // Previous MediaBox
|
||||||
|
|
||||||
|
|
||||||
// Get the filename from the command-line...
|
// Get the filename from the command-line...
|
||||||
@ -906,14 +921,20 @@ main(int argc, // I - Number of command-line arguments
|
|||||||
filename = argv[1];
|
filename = argv[1];
|
||||||
|
|
||||||
// Open the PDF file with the default callbacks...
|
// Open the PDF file with the default callbacks...
|
||||||
pdf = pdfioFileOpen(filename, /*password_cb*/NULL, /*password_cbdata*/NULL,
|
pdf = pdfioFileOpen(filename, /*password_cb*/NULL,
|
||||||
/*error_cb*/NULL, /*error_cbdata*/NULL);
|
/*password_cbdata*/NULL, /*error_cb*/NULL,
|
||||||
|
/*error_cbdata*/NULL);
|
||||||
if (pdf == NULL)
|
if (pdf == NULL)
|
||||||
return (1);
|
return (1);
|
||||||
|
|
||||||
// Get the title and author...
|
// Get the title, author, etc...
|
||||||
author = pdfioFileGetAuthor(pdf);
|
catalog = pdfioFileGetCatalog(pdf);
|
||||||
title = pdfioFileGetTitle(pdf);
|
author = pdfioFileGetAuthor(pdf);
|
||||||
|
creator = pdfioFileGetCreator(pdf);
|
||||||
|
has_acroform = pdfioDictGetType(catalog, "AcroForm") != PDFIO_VALTYPE_NONE;
|
||||||
|
num_pages = pdfioFileGetNumPages(pdf);
|
||||||
|
producer = pdfioFileGetProducer(pdf);
|
||||||
|
title = pdfioFileGetTitle(pdf);
|
||||||
|
|
||||||
// Get the creation date and convert to a string...
|
// Get the creation date and convert to a string...
|
||||||
if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0)
|
if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0)
|
||||||
@ -926,12 +947,76 @@ main(int argc, // I - Number of command-line arguments
|
|||||||
snprintf(creation_text, sizeof(creation_text), "-- not set --");
|
snprintf(creation_text, sizeof(creation_text), "-- not set --");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the modification date and convert to a string...
|
||||||
|
if ((modification_date = pdfioFileGetModificationDate(pdf)) > 0)
|
||||||
|
{
|
||||||
|
modification_tm = localtime(&modification_date);
|
||||||
|
strftime(modification_text, sizeof(modification_text), "%c", modification_tm);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(modification_text, sizeof(modification_text), "-- not set --");
|
||||||
|
}
|
||||||
|
|
||||||
// Print file information to stdout...
|
// Print file information to stdout...
|
||||||
printf("%s:\n", filename);
|
printf("%s:\n", filename);
|
||||||
printf(" Title: %s\n", title ? title : "-- not set --");
|
printf(" Title: %s\n", title ? title : "-- not set --");
|
||||||
printf(" Author: %s\n", author ? author : "-- not set --");
|
printf(" Author: %s\n", author ? author : "-- not set --");
|
||||||
printf(" Created On: %s\n", creation_text);
|
printf(" Creator: %s\n", creator ? creator : "-- not set --");
|
||||||
printf(" Number Pages: %u\n", (unsigned)pdfioFileGetNumPages(pdf));
|
printf(" Producer: %s\n", producer ? producer : "-- not set --");
|
||||||
|
printf(" Created On: %s\n", creation_text);
|
||||||
|
printf(" Modified On: %s\n", modification_text);
|
||||||
|
printf(" Version: %s\n", pdfioFileGetVersion(pdf));
|
||||||
|
printf(" AcroForm: %s\n", has_acroform ? "Yes" : "No");
|
||||||
|
printf(" Number of Pages: %u\n", (unsigned)num_pages);
|
||||||
|
|
||||||
|
// Report the MediaBox for all of the pages
|
||||||
|
prev_box.x1 = prev_box.x2 = prev_box.y1 = prev_box.y2 = 0.0;
|
||||||
|
|
||||||
|
for (cur = 0, prev = 0; cur < num_pages; cur ++)
|
||||||
|
{
|
||||||
|
// Find the MediaBox for this page in the page tree...
|
||||||
|
for (page = pdfioFileGetPage(pdf, cur);
|
||||||
|
page != NULL;
|
||||||
|
page = pdfioDictGetObj(page_dict, "Parent"))
|
||||||
|
{
|
||||||
|
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
|
||||||
|
page_dict = pdfioObjGetDict(page);
|
||||||
|
|
||||||
|
if (pdfioDictGetRect(page_dict, "MediaBox", &cur_box))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this MediaBox is different from the previous one, show the range of
|
||||||
|
// pages that have that size...
|
||||||
|
if (cur == 0 ||
|
||||||
|
fabs(cur_box.x1 - prev_box.x1) > 0.01 ||
|
||||||
|
fabs(cur_box.y1 - prev_box.y1) > 0.01 ||
|
||||||
|
fabs(cur_box.x2 - prev_box.x2) > 0.01 ||
|
||||||
|
fabs(cur_box.y2 - prev_box.y2) > 0.01)
|
||||||
|
{
|
||||||
|
if (cur > prev)
|
||||||
|
{
|
||||||
|
snprintf(range_text, sizeof(range_text), "Pages %u-%u",
|
||||||
|
(unsigned)(prev + 1), (unsigned)cur);
|
||||||
|
printf("%16s: [%g %g %g %g]\n", range_text,
|
||||||
|
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new series of pages with the new size...
|
||||||
|
prev = cur;
|
||||||
|
prev_box = cur_box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the last range as needed...
|
||||||
|
if (cur > prev)
|
||||||
|
{
|
||||||
|
snprintf(range_text, sizeof(range_text), "Pages %u-%u",
|
||||||
|
(unsigned)(prev + 1), (unsigned)cur);
|
||||||
|
printf("%16s: [%g %g %g %g]\n", range_text,
|
||||||
|
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
|
||||||
|
}
|
||||||
|
|
||||||
// Close the PDF file...
|
// Close the PDF file...
|
||||||
pdfioFileClose(pdf);
|
pdfioFileClose(pdf);
|
||||||
|
@ -24,7 +24,8 @@ TARGETS = \
|
|||||||
image2pdf \
|
image2pdf \
|
||||||
md2pdf \
|
md2pdf \
|
||||||
pdf2text \
|
pdf2text \
|
||||||
pdfioinfo
|
pdfioinfo \
|
||||||
|
pdfiomerge
|
||||||
|
|
||||||
|
|
||||||
# Make everything
|
# Make everything
|
||||||
@ -61,5 +62,10 @@ pdfioinfo: pdfioinfo.c
|
|||||||
$(CC) $(CFLAGS) -o $@ pdfioinfo.c $(LIBS)
|
$(CC) $(CFLAGS) -o $@ pdfioinfo.c $(LIBS)
|
||||||
|
|
||||||
|
|
||||||
|
# pdfiomerge
|
||||||
|
pdfiomerge: pdfiomerge.c
|
||||||
|
$(CC) $(CFLAGS) -o $@ pdfiomerge.c $(LIBS)
|
||||||
|
|
||||||
|
|
||||||
# Common dependencies...
|
# Common dependencies...
|
||||||
$(TARGETS): Makefile ../pdfio.h ../pdfio-content.h
|
$(TARGETS): Makefile ../pdfio.h ../pdfio-content.h
|
||||||
|
@ -68,7 +68,7 @@ main(int argc, // I - Number of command-line arguments
|
|||||||
catalog = pdfioFileGetCatalog(pdf);
|
catalog = pdfioFileGetCatalog(pdf);
|
||||||
author = pdfioFileGetAuthor(pdf);
|
author = pdfioFileGetAuthor(pdf);
|
||||||
creator = pdfioFileGetCreator(pdf);
|
creator = pdfioFileGetCreator(pdf);
|
||||||
has_acroform = pdfioDictGetObj(catalog, "AcroForm") != NULL ? true : false;
|
has_acroform = pdfioDictGetType(catalog, "AcroForm") != PDFIO_VALTYPE_NONE;
|
||||||
num_pages = pdfioFileGetNumPages(pdf);
|
num_pages = pdfioFileGetNumPages(pdf);
|
||||||
producer = pdfioFileGetProducer(pdf);
|
producer = pdfioFileGetProducer(pdf);
|
||||||
title = pdfioFileGetTitle(pdf);
|
title = pdfioFileGetTitle(pdf);
|
||||||
|
146
examples/pdfiomerge.c
Normal file
146
examples/pdfiomerge.c
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
//
|
||||||
|
// PDF merge program for PDFio.
|
||||||
|
//
|
||||||
|
// Copyright © 2025 by Michael R Sweet.
|
||||||
|
//
|
||||||
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
|
// information.
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// ./pdfmerge [-o OUTPUT.pdf] INPUT.pdf [... INPUT.pdf]
|
||||||
|
// ./pdfmerge INPUT.pdf [... INPUT.pdf] >OUTPUT.pdf
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <pdfio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Local functions...
|
||||||
|
//
|
||||||
|
|
||||||
|
static ssize_t output_cb(void *output_cbdata, const void *buffer, size_t bytes);
|
||||||
|
static int usage(FILE *out);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'main()' - Main entry.
|
||||||
|
//
|
||||||
|
|
||||||
|
int // O - Exit status
|
||||||
|
main(int argc, // I - Number of command-line arguments
|
||||||
|
char *argv[]) // I - Command-line arguments
|
||||||
|
{
|
||||||
|
int i; // Looping var
|
||||||
|
const char *opt; // Current option
|
||||||
|
pdfio_file_t *inpdf, // Input PDF file
|
||||||
|
*outpdf = NULL; // Output PDF file
|
||||||
|
|
||||||
|
|
||||||
|
// Parse command-line...
|
||||||
|
for (i = 1; i < argc; i ++)
|
||||||
|
{
|
||||||
|
if (!strcmp(argv[i], "--help"))
|
||||||
|
{
|
||||||
|
return (usage(stdout));
|
||||||
|
}
|
||||||
|
else if (!strncmp(argv[i], "--", 2))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "pdfmerge: Unknown option '%s'.\n", argv[i]);
|
||||||
|
return (usage(stderr));
|
||||||
|
}
|
||||||
|
else if (argv[i][0] == '-')
|
||||||
|
{
|
||||||
|
for (opt = argv[i] + 1; *opt; opt ++)
|
||||||
|
{
|
||||||
|
switch (*opt)
|
||||||
|
{
|
||||||
|
case 'o' : // -o OUTPUT.pdf
|
||||||
|
if (outpdf)
|
||||||
|
{
|
||||||
|
fputs("pdfmerge: Only one output file can be specified.\n", stderr);
|
||||||
|
return (usage(stderr));
|
||||||
|
}
|
||||||
|
|
||||||
|
i ++;
|
||||||
|
if (i >= argc)
|
||||||
|
{
|
||||||
|
fputs("pdfmerge: Missing output filename after '-o'.\n", stderr);
|
||||||
|
return (usage(stderr));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((outpdf = pdfioFileCreate(argv[i], /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
|
||||||
|
return (1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default :
|
||||||
|
fprintf(stderr, "pdfmerge: Unknown option '-%c'.\n", *opt);
|
||||||
|
return (usage(stderr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((inpdf = pdfioFileOpen(argv[i], /*password_cb*/NULL, /*password_data*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
|
||||||
|
{
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Copy PDF file...
|
||||||
|
size_t p, // Current page
|
||||||
|
nump; // Number of pages
|
||||||
|
|
||||||
|
if (!outpdf)
|
||||||
|
{
|
||||||
|
if ((outpdf = pdfioFileCreateOutput(output_cb, /*output_cbdata*/NULL, /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (p = 0, nump = pdfioFileGetNumPages(inpdf); p < nump; p ++)
|
||||||
|
{
|
||||||
|
if (!pdfioPageCopy(outpdf, pdfioFileGetPage(inpdf, p)))
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfioFileClose(inpdf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!outpdf)
|
||||||
|
return (usage(stderr));
|
||||||
|
|
||||||
|
pdfioFileClose(outpdf);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'output_cb()' - Write PDF data to the standard output...
|
||||||
|
//
|
||||||
|
|
||||||
|
static ssize_t // O - Number of bytes written
|
||||||
|
output_cb(void *output_cbdata, // I - Callback data (not used)
|
||||||
|
const void *buffer, // I - Buffer to write
|
||||||
|
size_t bytes) // I - Number of bytes to write
|
||||||
|
{
|
||||||
|
(void)output_cbdata;
|
||||||
|
|
||||||
|
return ((ssize_t)fwrite(buffer, 1, bytes, stdout));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 'usage()' - Show program usage.
|
||||||
|
//
|
||||||
|
|
||||||
|
static int // O - Exit status
|
||||||
|
usage(FILE *out) // I - stdout or stderr
|
||||||
|
{
|
||||||
|
fputs("Usage: pdfmerge [OPTIONS] INPUT.pdf [... INPUT.pdf] >OUTPUT.pdf\n", out);
|
||||||
|
fputs("Options:\n", out);
|
||||||
|
fputs(" --help Show help.\n", out);
|
||||||
|
fputs(" -o OUTPUT.pdf Send output to filename instead of stdout.\n", out);
|
||||||
|
|
||||||
|
return (out == stdout ? 0 : 1);
|
||||||
|
}
|
@ -476,7 +476,7 @@ pdfioContentDrawImage(
|
|||||||
double width, // I - Width of image
|
double width, // I - Width of image
|
||||||
double height) // I - Height of image
|
double height) // I - Height of image
|
||||||
{
|
{
|
||||||
return (pdfioStreamPrintf(st, "q %.6f 0 0 %.6f %.6f %.6f cm/%s Do Q\n", width, height, x, y, name));
|
return (pdfioStreamPrintf(st, "q %.6f 0 0 %.6f %.6f %.6f cm%N Do Q\n", width, height, x, y, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -811,7 +811,7 @@ pdfioContentSetFillColorSpace(
|
|||||||
pdfio_stream_t *st, // I - Stream
|
pdfio_stream_t *st, // I - Stream
|
||||||
const char *name) // I - Color space name
|
const char *name) // I - Color space name
|
||||||
{
|
{
|
||||||
return (pdfioStreamPrintf(st, "/%s cs\n", name));
|
return (pdfioStreamPrintf(st, "%N cs\n", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -961,7 +961,7 @@ pdfioContentSetStrokeColorSpace(
|
|||||||
pdfio_stream_t *st, // I - Stream
|
pdfio_stream_t *st, // I - Stream
|
||||||
const char *name) // I - Color space name
|
const char *name) // I - Color space name
|
||||||
{
|
{
|
||||||
return (pdfioStreamPrintf(st, "/%s CS\n", name));
|
return (pdfioStreamPrintf(st, "%N CS\n", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -988,7 +988,7 @@ pdfioContentSetTextFont(
|
|||||||
const char *name, // I - Font name
|
const char *name, // I - Font name
|
||||||
double size) // I - Font size
|
double size) // I - Font size
|
||||||
{
|
{
|
||||||
return (pdfioStreamPrintf(st, "/%s %.6f Tf\n", name, size));
|
return (pdfioStreamPrintf(st, "%N %.6f Tf\n", name, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1833,8 +1833,6 @@ pdfioFileCreateFontObjFromFile(
|
|||||||
{
|
{
|
||||||
if ((w1 = ttfGetWidth(font, (int)i)) == w0 && i < 65530)
|
if ((w1 = ttfGetWidth(font, (int)i)) == w0 && i < 65530)
|
||||||
{
|
{
|
||||||
size_t j; // Look-ahead
|
|
||||||
|
|
||||||
for (j = 1; j < 4; j ++)
|
for (j = 1; j < 4; j ++)
|
||||||
{
|
{
|
||||||
if (ttfGetWidth(font, (int)(i + j)) != w0)
|
if (ttfGetWidth(font, (int)(i + j)) != w0)
|
||||||
|
@ -120,10 +120,10 @@ extern bool pdfioContentTextMoveLine(pdfio_stream_t *st, double tx, double ty)
|
|||||||
extern bool pdfioContentTextMoveTo(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
|
extern bool pdfioContentTextMoveTo(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentTextNewLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
|
extern bool pdfioContentTextNewLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentTextNewLineShow(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *s) _PDFIO_PUBLIC;
|
extern bool pdfioContentTextNewLineShow(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *s) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentTextNewLineShowf(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(5,6);
|
extern bool pdfioContentTextNewLineShowf(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *format, ...) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentTextNextLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
|
extern bool pdfioContentTextNextLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentTextShow(pdfio_stream_t *st, bool unicode, const char *s) _PDFIO_PUBLIC;
|
extern bool pdfioContentTextShow(pdfio_stream_t *st, bool unicode, const char *s) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4);
|
extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, size_t num_fragments, const double *offsets, const char * const *fragments) _PDFIO_PUBLIC;
|
extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, size_t num_fragments, const double *offsets, const char * const *fragments) _PDFIO_PUBLIC;
|
||||||
|
|
||||||
// Resource helpers...
|
// Resource helpers...
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// PDF dictionary functions for PDFio.
|
// PDF dictionary functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2024 by Michael R Sweet.
|
// Copyright © 2021-2025 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
@ -469,7 +469,7 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
|
|||||||
*tempptr; // Pointer into temporary string
|
*tempptr; // Pointer into temporary string
|
||||||
unsigned char *dataptr; // Pointer into the data string
|
unsigned char *dataptr; // Pointer into the data string
|
||||||
|
|
||||||
if (!(value->value.binary.datalen & 1) && !memcmp(value->value.binary.data, "\377\376", 2))
|
if (!(value->value.binary.datalen & 1) && !memcmp(value->value.binary.data, "\376\377", 2))
|
||||||
{
|
{
|
||||||
// Copy UTF-16 BE
|
// Copy UTF-16 BE
|
||||||
int ch; // Unicode character
|
int ch; // Unicode character
|
||||||
@ -528,7 +528,7 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
|
|||||||
|
|
||||||
*tempptr = '\0';
|
*tempptr = '\0';
|
||||||
}
|
}
|
||||||
else if (!(value->value.binary.datalen & 1) && !memcmp(value->value.binary.data, "\376\377", 2))
|
else if (!(value->value.binary.datalen & 1) && !memcmp(value->value.binary.data, "\377\376", 2))
|
||||||
{
|
{
|
||||||
// Copy UTF-16 LE
|
// Copy UTF-16 LE
|
||||||
int ch; // Unicode character
|
int ch; // Unicode character
|
||||||
@ -1168,7 +1168,7 @@ _pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary
|
|||||||
// Write all of the key/value pairs...
|
// Write all of the key/value pairs...
|
||||||
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
|
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
|
||||||
{
|
{
|
||||||
if (!_pdfioFilePrintf(pdf, "/%s", pair->key))
|
if (!_pdfioFilePrintf(pdf, "%N", pair->key))
|
||||||
return (false);
|
return (false);
|
||||||
|
|
||||||
if (length && !strcmp(pair->key, "Length") && pair->value.type == PDFIO_VALTYPE_NUMBER && pair->value.value.number <= 0.0)
|
if (length && !strcmp(pair->key, "Length") && pair->value.type == PDFIO_VALTYPE_NUMBER && pair->value.value.number <= 0.0)
|
||||||
|
@ -110,6 +110,8 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
|
|||||||
{
|
{
|
||||||
bool ret = true; // Return value
|
bool ret = true; // Return value
|
||||||
size_t i; // Looping var
|
size_t i; // Looping var
|
||||||
|
_pdfio_strbuf_t *current, // Current string buffer
|
||||||
|
*next; // Next string buffer
|
||||||
|
|
||||||
|
|
||||||
// Range check input
|
// Range check input
|
||||||
@ -152,6 +154,12 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
|
|||||||
free(pdf->strings[i]);
|
free(pdf->strings[i]);
|
||||||
free(pdf->strings);
|
free(pdf->strings);
|
||||||
|
|
||||||
|
for (current = pdf->strbuffers; current; current = next)
|
||||||
|
{
|
||||||
|
next = current->next;
|
||||||
|
free(current);
|
||||||
|
}
|
||||||
|
|
||||||
free(pdf);
|
free(pdf);
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
|
@ -307,7 +307,8 @@ pdfioObjGetLength(pdfio_obj_t *obj) // I - Object
|
|||||||
|
|
||||||
if ((lenobj = pdfioDictGetObj(obj->value.value.dict, "Length")) == NULL)
|
if ((lenobj = pdfioDictGetObj(obj->value.value.dict, "Length")) == NULL)
|
||||||
{
|
{
|
||||||
_pdfioFileError(obj->pdf, "Unable to get length of stream.");
|
if (!_pdfioDictGetValue(obj->value.value.dict, "Length"))
|
||||||
|
_pdfioFileError(obj->pdf, "Unable to get length of stream.");
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +94,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
# define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values
|
# define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values
|
||||||
|
# define PDFIO_MAX_STRING 65536 // Maximum length of string
|
||||||
|
|
||||||
typedef void (*_pdfio_extfree_t)(void *);
|
typedef void (*_pdfio_extfree_t)(void *);
|
||||||
// Extension data free function
|
// Extension data free function
|
||||||
@ -224,6 +225,14 @@ typedef struct _pdfio_objmap_s // PDF object map
|
|||||||
size_t src_number; // Source object number
|
size_t src_number; // Source object number
|
||||||
} _pdfio_objmap_t;
|
} _pdfio_objmap_t;
|
||||||
|
|
||||||
|
typedef struct _pdfio_strbuf_s // PDF string buffer
|
||||||
|
{
|
||||||
|
struct _pdfio_strbuf_s *next; // Next string buffer
|
||||||
|
bool bufused; // Is this string buffer being used?
|
||||||
|
char buffer[PDFIO_MAX_STRING + 32];
|
||||||
|
// String buffer
|
||||||
|
} _pdfio_strbuf_t;
|
||||||
|
|
||||||
struct _pdfio_file_s // PDF file structure
|
struct _pdfio_file_s // PDF file structure
|
||||||
{
|
{
|
||||||
char *filename; // Filename
|
char *filename; // Filename
|
||||||
@ -283,6 +292,7 @@ struct _pdfio_file_s // PDF file structure
|
|||||||
size_t num_strings, // Number of strings
|
size_t num_strings, // Number of strings
|
||||||
alloc_strings; // Allocated strings
|
alloc_strings; // Allocated strings
|
||||||
char **strings; // Nul-terminated strings
|
char **strings; // Nul-terminated strings
|
||||||
|
_pdfio_strbuf_t *strbuffers; // String buffers
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _pdfio_obj_s // Object
|
struct _pdfio_obj_s // Object
|
||||||
@ -327,6 +337,7 @@ struct _pdfio_stream_s // Stream
|
|||||||
// Functions...
|
// Functions...
|
||||||
//
|
//
|
||||||
|
|
||||||
|
extern size_t _pdfio_strlcpy(char *dst, const char *src, size_t dstsize) _PDFIO_INTERNAL;
|
||||||
extern double _pdfio_strtod(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
|
extern 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 ssize_t _pdfio_vsnprintf(pdfio_file_t *pdf, char *buffer, size_t bufsize, const char *format, va_list ap) _PDFIO_INTERNAL;
|
||||||
|
|
||||||
@ -367,13 +378,13 @@ extern bool _pdfioFileAddPage(pdfio_file_t *pdf, pdfio_obj_t *obj) _PDFIO_INTER
|
|||||||
extern bool _pdfioFileConsume(pdfio_file_t *pdf, size_t bytes) _PDFIO_INTERNAL;
|
extern bool _pdfioFileConsume(pdfio_file_t *pdf, size_t bytes) _PDFIO_INTERNAL;
|
||||||
extern pdfio_obj_t *_pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_file_t *srcpdf, _pdfio_value_t *value) _PDFIO_INTERNAL;
|
extern pdfio_obj_t *_pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_file_t *srcpdf, _pdfio_value_t *value) _PDFIO_INTERNAL;
|
||||||
extern bool _pdfioFileDefaultError(pdfio_file_t *pdf, const char *message, void *data) _PDFIO_INTERNAL;
|
extern bool _pdfioFileDefaultError(pdfio_file_t *pdf, const char *message, void *data) _PDFIO_INTERNAL;
|
||||||
extern bool _pdfioFileError(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_INTERNAL;
|
extern bool _pdfioFileError(pdfio_file_t *pdf, const char *format, ...) _PDFIO_INTERNAL;
|
||||||
extern pdfio_obj_t *_pdfioFileFindMappedObj(pdfio_file_t *pdf, pdfio_file_t *src_pdf, size_t src_number) _PDFIO_INTERNAL;
|
extern pdfio_obj_t *_pdfioFileFindMappedObj(pdfio_file_t *pdf, pdfio_file_t *src_pdf, size_t src_number) _PDFIO_INTERNAL;
|
||||||
extern bool _pdfioFileFlush(pdfio_file_t *pdf) _PDFIO_INTERNAL;
|
extern bool _pdfioFileFlush(pdfio_file_t *pdf) _PDFIO_INTERNAL;
|
||||||
extern int _pdfioFileGetChar(pdfio_file_t *pdf) _PDFIO_INTERNAL;
|
extern int _pdfioFileGetChar(pdfio_file_t *pdf) _PDFIO_INTERNAL;
|
||||||
extern bool _pdfioFileGets(pdfio_file_t *pdf, char *buffer, size_t bufsize) _PDFIO_INTERNAL;
|
extern bool _pdfioFileGets(pdfio_file_t *pdf, char *buffer, size_t bufsize) _PDFIO_INTERNAL;
|
||||||
extern ssize_t _pdfioFilePeek(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL;
|
extern ssize_t _pdfioFilePeek(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL;
|
||||||
extern bool _pdfioFilePrintf(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_INTERNAL;
|
extern bool _pdfioFilePrintf(pdfio_file_t *pdf, const char *format, ...) _PDFIO_INTERNAL;
|
||||||
extern bool _pdfioFilePuts(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
|
extern bool _pdfioFilePuts(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
|
||||||
extern ssize_t _pdfioFileRead(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL;
|
extern ssize_t _pdfioFileRead(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL;
|
||||||
extern off_t _pdfioFileSeek(pdfio_file_t *pdf, off_t offset, int whence) _PDFIO_INTERNAL;
|
extern off_t _pdfioFileSeek(pdfio_file_t *pdf, off_t offset, int whence) _PDFIO_INTERNAL;
|
||||||
@ -389,6 +400,8 @@ extern bool _pdfioObjWriteHeader(pdfio_obj_t *obj) _PDFIO_INTERNAL;
|
|||||||
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, size_t cbsize, pdfio_filter_t compression) _PDFIO_INTERNAL;
|
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, size_t cbsize, pdfio_filter_t compression) _PDFIO_INTERNAL;
|
||||||
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
|
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
|
||||||
|
|
||||||
|
extern char *_pdfioStringAllocBuffer(pdfio_file_t *pdf);
|
||||||
|
extern void _pdfioStringFreeBuffer(pdfio_file_t *pdf, char *buffer);
|
||||||
extern bool _pdfioStringIsAllocated(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
|
extern bool _pdfioStringIsAllocated(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
|
||||||
|
|
||||||
extern void _pdfioTokenClear(_pdfio_token_t *tb) _PDFIO_INTERNAL;
|
extern void _pdfioTokenClear(_pdfio_token_t *tb) _PDFIO_INTERNAL;
|
||||||
|
@ -439,7 +439,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
|
|||||||
st->pdf = obj->pdf;
|
st->pdf = obj->pdf;
|
||||||
st->obj = obj;
|
st->obj = obj;
|
||||||
|
|
||||||
if ((st->remaining = pdfioObjGetLength(obj)) == 0)
|
if ((st->remaining = pdfioObjGetLength(obj)) == 0 && !_pdfioDictGetValue(pdfioObjGetDict(obj), "Length"))
|
||||||
{
|
{
|
||||||
_pdfioFileError(obj->pdf, "No stream data.");
|
_pdfioFileError(obj->pdf, "No stream data.");
|
||||||
goto error;
|
goto error;
|
||||||
@ -616,7 +616,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Something else we don't support
|
// Something else we don't support
|
||||||
_pdfioFileError(st->pdf, "Unsupported stream filter '/%s'.", filter);
|
_pdfioFileError(st->pdf, "Unsupported stream filter '%N'.", filter);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -689,6 +689,10 @@ pdfioStreamPeek(pdfio_stream_t *st, // I - Stream
|
|||||||
//
|
//
|
||||||
// 'pdfioStreamPrintf()' - Write a formatted string to a stream.
|
// 'pdfioStreamPrintf()' - Write a formatted string to a stream.
|
||||||
//
|
//
|
||||||
|
// This function writes a formatted string to a stream. In addition to the
|
||||||
|
// standard `printf` format characters, you can use "%N" to format a PDF name
|
||||||
|
// value ("/Name") and "%S" to format a PDF string ("(String)") value.
|
||||||
|
//
|
||||||
|
|
||||||
bool // O - `true` on success, `false` on failure
|
bool // O - `true` on success, `false` on failure
|
||||||
pdfioStreamPrintf(
|
pdfioStreamPrintf(
|
||||||
|
256
pdfio-string.c
256
pdfio-string.c
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// PDF string functions for PDFio.
|
// PDF string functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2024 by Michael R Sweet.
|
// Copyright © 2021-2025 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
@ -17,6 +17,83 @@
|
|||||||
static size_t find_string(pdfio_file_t *pdf, const char *s, int *rdiff);
|
static size_t find_string(pdfio_file_t *pdf, const char *s, int *rdiff);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// '_pdfio_strlcpy()' - Safe string copy.
|
||||||
|
//
|
||||||
|
|
||||||
|
size_t // O - Length of source string
|
||||||
|
_pdfio_strlcpy(char *dst, // I - Destination string buffer
|
||||||
|
const char *src, // I - Source string
|
||||||
|
size_t dstsize) // I - Size of destination
|
||||||
|
{
|
||||||
|
size_t srclen; // Length of source string
|
||||||
|
|
||||||
|
|
||||||
|
// Range check input...
|
||||||
|
if (!dst || !src || dstsize == 0)
|
||||||
|
{
|
||||||
|
if (dst)
|
||||||
|
*dst = '\0';
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out how much room is needed...
|
||||||
|
dstsize --;
|
||||||
|
|
||||||
|
srclen = strlen(src);
|
||||||
|
|
||||||
|
// Copy the appropriate amount...
|
||||||
|
if (srclen <= dstsize)
|
||||||
|
{
|
||||||
|
// Source string will fit...
|
||||||
|
memmove(dst, src, srclen);
|
||||||
|
dst[srclen] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Source string too big, copy what we can and clean up the end...
|
||||||
|
char *ptr = dst + dstsize - 1, // Pointer into string
|
||||||
|
*end = ptr + 1; // Pointer to end of string
|
||||||
|
|
||||||
|
memmove(dst, src, dstsize);
|
||||||
|
dst[dstsize] = '\0';
|
||||||
|
|
||||||
|
// Validate last character in destination buffer...
|
||||||
|
if (ptr > dst && *ptr & 0x80)
|
||||||
|
{
|
||||||
|
while ((*ptr & 0xc0) == 0x80 && ptr > dst)
|
||||||
|
ptr --;
|
||||||
|
|
||||||
|
if ((*ptr & 0xe0) == 0xc0)
|
||||||
|
{
|
||||||
|
// Verify 2-byte UTF-8 sequence...
|
||||||
|
if ((end - ptr) != 2)
|
||||||
|
*ptr = '\0';
|
||||||
|
}
|
||||||
|
else if ((*ptr & 0xf0) == 0xe0)
|
||||||
|
{
|
||||||
|
// Verify 3-byte UTF-8 sequence...
|
||||||
|
if ((end - ptr) != 3)
|
||||||
|
*ptr = '\0';
|
||||||
|
}
|
||||||
|
else if ((*ptr & 0xf8) == 0xf0)
|
||||||
|
{
|
||||||
|
// Verify 4-byte UTF-8 sequence...
|
||||||
|
if ((end - ptr) != 4)
|
||||||
|
*ptr = '\0';
|
||||||
|
}
|
||||||
|
else if (*ptr & 0x80)
|
||||||
|
{
|
||||||
|
// Invalid sequence at end...
|
||||||
|
*ptr = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (srclen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// '_pdfio_strtod()' - Convert a string to a double value.
|
// '_pdfio_strtod()' - Convert a string to a double value.
|
||||||
//
|
//
|
||||||
@ -112,10 +189,9 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
|
|
||||||
// Loop through the format string, formatting as needed...
|
// Loop through the format string, formatting as needed...
|
||||||
bufptr = buffer;
|
bufptr = buffer;
|
||||||
bufend = buffer + bufsize - 1;
|
bufend = buffer + bufsize - 1;
|
||||||
*bufend = '\0';
|
bytes = 0;
|
||||||
bytes = 0;
|
|
||||||
|
|
||||||
while (*format)
|
while (*format)
|
||||||
{
|
{
|
||||||
@ -178,14 +254,12 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
prec = 0;
|
|
||||||
|
|
||||||
while (isdigit(*format & 255))
|
while (isdigit(*format & 255))
|
||||||
{
|
{
|
||||||
if (tptr < (tformat + sizeof(tformat) - 1))
|
if (tptr < (tformat + sizeof(tformat) - 1))
|
||||||
*tptr++ = *format;
|
*tptr++ = *format;
|
||||||
|
|
||||||
prec = prec * 10 + *format++ - '0';
|
format ++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,7 +333,7 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
if (bufptr < bufend)
|
if (bufptr < bufend)
|
||||||
{
|
{
|
||||||
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
|
_pdfio_strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
|
||||||
bufptr += strlen(bufptr);
|
bufptr += strlen(bufptr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -289,7 +363,7 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
if (bufptr < bufend)
|
if (bufptr < bufend)
|
||||||
{
|
{
|
||||||
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
|
_pdfio_strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
|
||||||
bufptr += strlen(bufptr);
|
bufptr += strlen(bufptr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -304,7 +378,7 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
if (bufptr < bufend)
|
if (bufptr < bufend)
|
||||||
{
|
{
|
||||||
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
|
_pdfio_strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
|
||||||
bufptr += strlen(bufptr);
|
bufptr += strlen(bufptr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -329,19 +403,111 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 's' : // String
|
case 'S' : // PDF string
|
||||||
if ((s = va_arg(ap, char *)) == NULL)
|
if ((s = va_arg(ap, char *)) == NULL)
|
||||||
s = "(null)";
|
s = "(null)";
|
||||||
|
|
||||||
|
// PDF strings start with "("...
|
||||||
|
if (bufptr < bufend)
|
||||||
|
*bufptr++ = '(';
|
||||||
|
|
||||||
|
bytes ++;
|
||||||
|
|
||||||
|
// Loop through the literal string...
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
// Escape special characters
|
||||||
|
if (*s == '\\' || *s == '(' || *s == ')')
|
||||||
|
{
|
||||||
|
// Simple escape...
|
||||||
|
if (bufptr < bufend)
|
||||||
|
*bufptr++ = '\\';
|
||||||
|
|
||||||
|
if (bufptr < bufend)
|
||||||
|
*bufptr++ = *s;
|
||||||
|
|
||||||
|
bytes += 2;
|
||||||
|
}
|
||||||
|
else if (*s < ' ')
|
||||||
|
{
|
||||||
|
// Octal escape...
|
||||||
|
snprintf(bufptr, (size_t)(bufend - bufptr + 1), "\\%03o", *s & 255);
|
||||||
|
bufptr += strlen(bufptr);
|
||||||
|
bytes += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Literal character...
|
||||||
|
if (bufptr < bufend)
|
||||||
|
*bufptr++ = *s;
|
||||||
|
bytes ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
s ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDF strings end with ")"...
|
||||||
|
if (bufptr < bufend)
|
||||||
|
*bufptr++ = ')';
|
||||||
|
|
||||||
|
bytes ++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's' : // Literal string
|
||||||
|
if ((s = va_arg(ap, char *)) == NULL)
|
||||||
|
s = "(null)";
|
||||||
|
|
||||||
|
if (width != 0)
|
||||||
|
{
|
||||||
|
// Format string to fit inside the specified width...
|
||||||
|
if ((size_t)(width + 1) > sizeof(temp))
|
||||||
|
break;
|
||||||
|
|
||||||
|
snprintf(temp, sizeof(temp), tformat, s);
|
||||||
|
s = temp;
|
||||||
|
}
|
||||||
|
|
||||||
bytes += strlen(s);
|
bytes += strlen(s);
|
||||||
|
|
||||||
if (bufptr < bufend)
|
if (bufptr < bufend)
|
||||||
{
|
{
|
||||||
strncpy(bufptr, s, (size_t)(bufend - bufptr - 1));
|
_pdfio_strlcpy(bufptr, s, (size_t)(bufend - bufptr + 1));
|
||||||
bufptr += strlen(bufptr);
|
bufptr += strlen(bufptr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'N' : // Output name string with proper escaping
|
||||||
|
if ((s = va_arg(ap, char *)) == NULL)
|
||||||
|
s = "(null)";
|
||||||
|
|
||||||
|
// PDF names start with "/"...
|
||||||
|
if (bufptr < bufend)
|
||||||
|
*bufptr++ = '/';
|
||||||
|
|
||||||
|
bytes ++;
|
||||||
|
|
||||||
|
// Loop through the name string...
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
if (*s < 0x21 || *s > 0x7e || *s == '#')
|
||||||
|
{
|
||||||
|
// Output #XX for character...
|
||||||
|
snprintf(bufptr, (size_t)(bufend - bufptr + 1), "#%02X", *s & 255);
|
||||||
|
bufptr += strlen(bufptr);
|
||||||
|
bytes += 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Output literal character...
|
||||||
|
if (bufptr < bufend)
|
||||||
|
*bufptr++ = *s;
|
||||||
|
bytes ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
s ++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'n' : // Output number of chars so far
|
case 'n' : // Output number of chars so far
|
||||||
*(va_arg(ap, int *)) = (int)bytes;
|
*(va_arg(ap, int *)) = (int)bytes;
|
||||||
break;
|
break;
|
||||||
@ -358,11 +524,7 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nul-terminate the string and return the number of characters needed.
|
// Nul-terminate the string and return the number of characters needed.
|
||||||
if (bufptr < bufend)
|
*bufptr = '\0';
|
||||||
{
|
|
||||||
// Everything fit in the buffer...
|
|
||||||
*bufptr = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
PDFIO_DEBUG("_pdfio_vsnprintf: Returning %ld \"%s\"\n", (long)bytes, buffer);
|
PDFIO_DEBUG("_pdfio_vsnprintf: Returning %ld \"%s\"\n", (long)bytes, buffer);
|
||||||
|
|
||||||
@ -370,6 +532,41 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// '_pdfioStringAllocBuffer()' - Allocate a string buffer.
|
||||||
|
//
|
||||||
|
|
||||||
|
char * // O - Buffer or `NULL` on error
|
||||||
|
_pdfioStringAllocBuffer(
|
||||||
|
pdfio_file_t *pdf) // I - PDF file
|
||||||
|
{
|
||||||
|
_pdfio_strbuf_t *current; // Current string buffer
|
||||||
|
|
||||||
|
|
||||||
|
// See if we have an available string buffer...
|
||||||
|
for (current = pdf->strbuffers; current; current = current->next)
|
||||||
|
{
|
||||||
|
if (!current->bufused)
|
||||||
|
{
|
||||||
|
current->bufused = true;
|
||||||
|
return (current->buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Didn't find one, allocate a new one...
|
||||||
|
if ((current = calloc(1, sizeof(_pdfio_strbuf_t))) == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
// Add to the linked list of string buffers...
|
||||||
|
current->next = pdf->strbuffers;
|
||||||
|
current->bufused = true;
|
||||||
|
|
||||||
|
pdf->strbuffers = current;
|
||||||
|
|
||||||
|
return (current->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 'pdfioStringCreate()' - Create a durable literal string.
|
// 'pdfioStringCreate()' - Create a durable literal string.
|
||||||
//
|
//
|
||||||
@ -480,6 +677,29 @@ pdfioStringCreatef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// '_pdfioStringFreeBuffer()' - Free a string buffer.
|
||||||
|
//
|
||||||
|
|
||||||
|
void
|
||||||
|
_pdfioStringFreeBuffer(
|
||||||
|
pdfio_file_t *pdf, // I - PDF file
|
||||||
|
char *buffer) // I - String buffer
|
||||||
|
{
|
||||||
|
_pdfio_strbuf_t *current; // Current string buffer
|
||||||
|
|
||||||
|
|
||||||
|
for (current = pdf->strbuffers; current; current = current->next)
|
||||||
|
{
|
||||||
|
if (current->buffer == buffer)
|
||||||
|
{
|
||||||
|
current->bufused = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// '_pdfioStringIsAllocated()' - Check whether a string has been allocated.
|
// '_pdfioStringIsAllocated()' - Check whether a string has been allocated.
|
||||||
//
|
//
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// PDF token parsing functions for PDFio.
|
// PDF token parsing functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2023 by Michael R Sweet.
|
// Copyright © 2021-2025 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
@ -528,13 +528,6 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
|||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bufptr == (buffer + 1))
|
|
||||||
{
|
|
||||||
_pdfioFileError(tb->pdf, "Empty name.");
|
|
||||||
*bufptr = '\0';
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '<' : // Potential hex string
|
case '<' : // Potential hex string
|
||||||
|
264
pdfio-value.c
264
pdfio-value.c
@ -1,7 +1,7 @@
|
|||||||
//
|
//
|
||||||
// PDF value functions for PDFio.
|
// PDF value functions for PDFio.
|
||||||
//
|
//
|
||||||
// Copyright © 2021-2024 by Michael R Sweet.
|
// Copyright © 2021-2025 by Michael R Sweet.
|
||||||
//
|
//
|
||||||
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||||
// information.
|
// information.
|
||||||
@ -125,7 +125,7 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
|
|||||||
_pdfio_crypto_ctx_t ctx; // Decryption context
|
_pdfio_crypto_ctx_t ctx; // Decryption context
|
||||||
_pdfio_crypto_cb_t cb; // Decryption callback
|
_pdfio_crypto_cb_t cb; // Decryption callback
|
||||||
size_t ivlen; // Number of initialization vector bytes
|
size_t ivlen; // Number of initialization vector bytes
|
||||||
uint8_t temp[32768]; // Temporary buffer for decryption
|
uint8_t *temp = NULL; // Temporary buffer for decryption
|
||||||
size_t templen; // Number of actual data bytes
|
size_t templen; // Number of actual data bytes
|
||||||
time_t timeval; // Date/time value
|
time_t timeval; // Date/time value
|
||||||
|
|
||||||
@ -152,11 +152,16 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
case PDFIO_VALTYPE_BINARY :
|
case PDFIO_VALTYPE_BINARY :
|
||||||
// Decrypt the binary string...
|
// Decrypt the binary string...
|
||||||
if (v->value.binary.datalen > (sizeof(temp) - 32))
|
if (v->value.binary.datalen > PDFIO_MAX_STRING)
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
|
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf)) == NULL)
|
||||||
|
{
|
||||||
|
_pdfioFileError(pdf, "Unable to read encrypted binary string - out of memory.");
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
ivlen = v->value.binary.datalen;
|
ivlen = v->value.binary.datalen;
|
||||||
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL)
|
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL)
|
||||||
@ -171,6 +176,8 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
|
|||||||
v->value.binary.datalen = templen - temp[templen - 1];
|
v->value.binary.datalen = templen - temp[templen - 1];
|
||||||
else
|
else
|
||||||
v->value.binary.datalen = templen;
|
v->value.binary.datalen = templen;
|
||||||
|
|
||||||
|
_pdfioStringFreeBuffer(pdf, (char *)temp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PDFIO_VALTYPE_STRING :
|
case PDFIO_VALTYPE_STRING :
|
||||||
@ -300,7 +307,9 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
_pdfio_value_t *v, // I - Value
|
_pdfio_value_t *v, // I - Value
|
||||||
size_t depth) // I - Depth of value
|
size_t depth) // I - Depth of value
|
||||||
{
|
{
|
||||||
char token[32768]; // Token buffer
|
_pdfio_value_t *ret = NULL; // Return value
|
||||||
|
char *token = _pdfioStringAllocBuffer(pdf);
|
||||||
|
// Token buffer
|
||||||
time_t timeval; // Date/time value
|
time_t timeval; // Date/time value
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
static const char * const valtypes[] =
|
static const char * const valtypes[] =
|
||||||
@ -322,8 +331,11 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v);
|
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v);
|
||||||
|
|
||||||
if (!_pdfioTokenGet(tb, token, sizeof(token)))
|
if (!token)
|
||||||
return (NULL);
|
goto done;
|
||||||
|
|
||||||
|
if (!_pdfioTokenGet(tb, token, PDFIO_MAX_STRING))
|
||||||
|
goto done;
|
||||||
|
|
||||||
if (!strcmp(token, "["))
|
if (!strcmp(token, "["))
|
||||||
{
|
{
|
||||||
@ -331,12 +343,14 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
if (depth >= PDFIO_MAX_DEPTH)
|
if (depth >= PDFIO_MAX_DEPTH)
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Too many nested arrays.");
|
_pdfioFileError(pdf, "Too many nested arrays.");
|
||||||
return (NULL);
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
v->type = PDFIO_VALTYPE_ARRAY;
|
v->type = PDFIO_VALTYPE_ARRAY;
|
||||||
if ((v->value.array = _pdfioArrayRead(pdf, obj, tb, depth + 1)) == NULL)
|
if ((v->value.array = _pdfioArrayRead(pdf, obj, tb, depth + 1)) == NULL)
|
||||||
return (NULL);
|
goto done;
|
||||||
|
|
||||||
|
ret = v;
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "<<"))
|
else if (!strcmp(token, "<<"))
|
||||||
{
|
{
|
||||||
@ -344,29 +358,34 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
if (depth >= PDFIO_MAX_DEPTH)
|
if (depth >= PDFIO_MAX_DEPTH)
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Too many nested dictionaries.");
|
_pdfioFileError(pdf, "Too many nested dictionaries.");
|
||||||
return (NULL);
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
v->type = PDFIO_VALTYPE_DICT;
|
v->type = PDFIO_VALTYPE_DICT;
|
||||||
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
|
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
|
||||||
return (NULL);
|
goto done;
|
||||||
|
|
||||||
|
ret = v;
|
||||||
}
|
}
|
||||||
else if (!strncmp(token, "(D:", 3) && (timeval = get_date_time(token + 1)) != 0)
|
else if ((timeval = get_date_time(token + 1)) != 0)
|
||||||
{
|
{
|
||||||
v->type = PDFIO_VALTYPE_DATE;
|
v->type = PDFIO_VALTYPE_DATE;
|
||||||
v->value.date = timeval;
|
v->value.date = timeval;
|
||||||
|
ret = v;
|
||||||
}
|
}
|
||||||
else if (token[0] == '(')
|
else if (token[0] == '(')
|
||||||
{
|
{
|
||||||
// String
|
// String
|
||||||
v->type = PDFIO_VALTYPE_STRING;
|
v->type = PDFIO_VALTYPE_STRING;
|
||||||
v->value.string = pdfioStringCreate(pdf, token + 1);
|
v->value.string = pdfioStringCreate(pdf, token + 1);
|
||||||
|
ret = v;
|
||||||
}
|
}
|
||||||
else if (token[0] == '/')
|
else if (token[0] == '/')
|
||||||
{
|
{
|
||||||
// Name
|
// Name
|
||||||
v->type = PDFIO_VALTYPE_NAME;
|
v->type = PDFIO_VALTYPE_NAME;
|
||||||
v->value.name = pdfioStringCreate(pdf, token + 1);
|
v->value.name = pdfioStringCreate(pdf, token + 1);
|
||||||
|
ret = v;
|
||||||
}
|
}
|
||||||
else if (token[0] == '<')
|
else if (token[0] == '<')
|
||||||
{
|
{
|
||||||
@ -379,7 +398,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
if ((v->value.binary.data = (unsigned char *)malloc(v->value.binary.datalen)) == NULL)
|
if ((v->value.binary.data = (unsigned char *)malloc(v->value.binary.datalen)) == NULL)
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Out of memory for hex string.");
|
_pdfioFileError(pdf, "Out of memory for hex string.");
|
||||||
return (NULL);
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert hex to binary...
|
// Convert hex to binary...
|
||||||
@ -406,6 +425,8 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
*dataptr++ = (unsigned char)d;
|
*dataptr++ = (unsigned char)d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = v;
|
||||||
}
|
}
|
||||||
else if (strchr("0123456789-+.", token[0]) != NULL)
|
else if (strchr("0123456789-+.", token[0]) != NULL)
|
||||||
{
|
{
|
||||||
@ -493,7 +514,8 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
|||||||
|
|
||||||
PDFIO_DEBUG("_pdfioValueRead: Returning indirect value %lu %u R.\n", (unsigned long)v->value.indirect.number, v->value.indirect.generation);
|
PDFIO_DEBUG("_pdfioValueRead: Returning indirect value %lu %u R.\n", (unsigned long)v->value.indirect.number, v->value.indirect.generation);
|
||||||
|
|
||||||
return (v);
|
ret = v;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -501,27 +523,41 @@ _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 = _pdfio_strtod(pdf, token);
|
v->value.number = _pdfio_strtod(pdf, token);
|
||||||
|
ret = v;
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "true") || !strcmp(token, "false"))
|
else if (!strcmp(token, "true") || !strcmp(token, "false"))
|
||||||
{
|
{
|
||||||
// Boolean value
|
// Boolean value
|
||||||
v->type = PDFIO_VALTYPE_BOOLEAN;
|
v->type = PDFIO_VALTYPE_BOOLEAN;
|
||||||
v->value.boolean = !strcmp(token, "true");
|
v->value.boolean = !strcmp(token, "true");
|
||||||
|
ret = v;
|
||||||
}
|
}
|
||||||
else if (!strcmp(token, "null"))
|
else if (!strcmp(token, "null"))
|
||||||
{
|
{
|
||||||
// null value
|
// null value
|
||||||
v->type = PDFIO_VALTYPE_NULL;
|
v->type = PDFIO_VALTYPE_NULL;
|
||||||
|
ret = v;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Unexpected '%s' token seen.", token);
|
_pdfioFileError(pdf, "Unexpected '%s' token seen.", token);
|
||||||
return (NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFIO_DEBUG("_pdfioValueRead: Returning %s value.\n", valtypes[v->type]);
|
done:
|
||||||
|
|
||||||
return (v);
|
if (token)
|
||||||
|
_pdfioStringFreeBuffer(pdf, token);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
PDFIO_DEBUG("_pdfioValueRead: Returning %s value.\n", valtypes[ret->type]);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PDFIO_DEBUG("_pdfioValueRead: Returning NULL.\n");
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -546,8 +582,10 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
|||||||
case PDFIO_VALTYPE_BINARY :
|
case PDFIO_VALTYPE_BINARY :
|
||||||
{
|
{
|
||||||
size_t databytes; // Bytes to write
|
size_t databytes; // Bytes to write
|
||||||
uint8_t temp[32768], // Temporary buffer for encryption
|
uint8_t *temp = NULL, // Temporary buffer for encryption
|
||||||
*dataptr; // Pointer into data
|
*dataptr; // Pointer into data
|
||||||
|
bool ret = false; // Return value
|
||||||
|
|
||||||
|
|
||||||
if (obj && pdf->encryption)
|
if (obj && pdf->encryption)
|
||||||
{
|
{
|
||||||
@ -556,11 +594,16 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
|||||||
_pdfio_crypto_cb_t cb; // Encryption callback
|
_pdfio_crypto_cb_t cb; // Encryption callback
|
||||||
size_t ivlen; // Number of initialization vector bytes
|
size_t ivlen; // Number of initialization vector bytes
|
||||||
|
|
||||||
if (v->value.binary.datalen > (sizeof(temp) - 32))
|
if (v->value.binary.datalen > PDFIO_MAX_STRING)
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Unable to write encrypted binary string - too long.");
|
_pdfioFileError(pdf, "Unable to write encrypted binary string - too long.");
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf)) == NULL)
|
||||||
|
{
|
||||||
|
_pdfioFileError(pdf, "Unable to write encrypted binary string - out of memory.");
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
|
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
|
||||||
databytes = (cb)(&ctx, temp + ivlen, v->value.binary.data, v->value.binary.datalen) + ivlen;
|
databytes = (cb)(&ctx, temp + ivlen, v->value.binary.data, v->value.binary.datalen) + ivlen;
|
||||||
@ -573,18 +616,25 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!_pdfioFilePuts(pdf, "<"))
|
if (!_pdfioFilePuts(pdf, "<"))
|
||||||
return (false);
|
goto bindone;
|
||||||
|
|
||||||
for (; databytes > 1; databytes -= 2, dataptr += 2)
|
for (; databytes > 1; databytes -= 2, dataptr += 2)
|
||||||
{
|
{
|
||||||
if (!_pdfioFilePrintf(pdf, "%02X%02X", dataptr[0], dataptr[1]))
|
if (!_pdfioFilePrintf(pdf, "%02X%02X", dataptr[0], dataptr[1]))
|
||||||
return (false);
|
goto bindone;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (databytes > 0)
|
if (databytes > 0 && !_pdfioFilePrintf(pdf, "%02X", dataptr[0]))
|
||||||
return (_pdfioFilePrintf(pdf, "%02X>", dataptr[0]));
|
goto bindone;
|
||||||
else
|
|
||||||
return (_pdfioFilePuts(pdf, ">"));
|
ret = _pdfioFilePuts(pdf, ">");
|
||||||
|
|
||||||
|
bindone:
|
||||||
|
|
||||||
|
if (temp)
|
||||||
|
_pdfioStringFreeBuffer(pdf, (char *)temp);
|
||||||
|
|
||||||
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
case PDFIO_VALTYPE_BOOLEAN :
|
case PDFIO_VALTYPE_BOOLEAN :
|
||||||
@ -609,7 +659,7 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
|||||||
if (obj && pdf->encryption)
|
if (obj && pdf->encryption)
|
||||||
{
|
{
|
||||||
// Write encrypted string...
|
// Write encrypted string...
|
||||||
uint8_t temp[32768], // Encrypted bytes
|
uint8_t temp[64], // Encrypted bytes
|
||||||
*tempptr; // Pointer into encrypted bytes
|
*tempptr; // Pointer into encrypted bytes
|
||||||
_pdfio_crypto_ctx_t ctx; // Encryption context
|
_pdfio_crypto_ctx_t ctx; // Encryption context
|
||||||
_pdfio_crypto_cb_t cb; // Encryption callback
|
_pdfio_crypto_cb_t cb; // Encryption callback
|
||||||
@ -637,7 +687,7 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return (_pdfioFilePrintf(pdf, "(%s)", datestr));
|
return (_pdfioFilePrintf(pdf, "%S", datestr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,19 +698,19 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
|||||||
return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation));
|
return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation));
|
||||||
|
|
||||||
case PDFIO_VALTYPE_NAME :
|
case PDFIO_VALTYPE_NAME :
|
||||||
return (_pdfioFilePrintf(pdf, "/%s", v->value.name));
|
return (_pdfioFilePrintf(pdf, "%N", v->value.name));
|
||||||
|
|
||||||
case PDFIO_VALTYPE_NULL :
|
case PDFIO_VALTYPE_NULL :
|
||||||
return (_pdfioFilePuts(pdf, " null"));
|
return (_pdfioFilePuts(pdf, " null"));
|
||||||
|
|
||||||
case PDFIO_VALTYPE_NUMBER :
|
case PDFIO_VALTYPE_NUMBER :
|
||||||
return (_pdfioFilePrintf(pdf, " %g", v->value.number));
|
return (_pdfioFilePrintf(pdf, " %.6f", v->value.number));
|
||||||
|
|
||||||
case PDFIO_VALTYPE_STRING :
|
case PDFIO_VALTYPE_STRING :
|
||||||
if (obj && pdf->encryption)
|
if (obj && pdf->encryption)
|
||||||
{
|
{
|
||||||
// Write encrypted string...
|
// Write encrypted string...
|
||||||
uint8_t temp[32768], // Encrypted bytes
|
uint8_t *temp = NULL, // Encrypted bytes
|
||||||
*tempptr; // Pointer into encrypted bytes
|
*tempptr; // Pointer into encrypted bytes
|
||||||
_pdfio_crypto_ctx_t ctx; // Encryption context
|
_pdfio_crypto_ctx_t ctx; // Encryption context
|
||||||
_pdfio_crypto_cb_t cb; // Encryption callback
|
_pdfio_crypto_cb_t cb; // Encryption callback
|
||||||
@ -668,74 +718,46 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
|||||||
// Length of value
|
// Length of value
|
||||||
ivlen, // Number of initialization vector bytes
|
ivlen, // Number of initialization vector bytes
|
||||||
tempbytes; // Number of output bytes
|
tempbytes; // Number of output bytes
|
||||||
|
bool ret = false; // Return value
|
||||||
|
|
||||||
if (len > (sizeof(temp) - 32))
|
if (len > PDFIO_MAX_STRING)
|
||||||
{
|
{
|
||||||
_pdfioFileError(pdf, "Unable to write encrypted string - too long.");
|
_pdfioFileError(pdf, "Unable to write encrypted string - too long.");
|
||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf)) == NULL)
|
||||||
|
{
|
||||||
|
_pdfioFileError(pdf, "Unable to write encrypted string - out of memory.");
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
|
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
|
||||||
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)v->value.string, len) + ivlen;
|
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)v->value.string, len) + ivlen;
|
||||||
|
|
||||||
if (!_pdfioFilePuts(pdf, "<"))
|
if (!_pdfioFilePuts(pdf, "<"))
|
||||||
return (false);
|
goto strdone;
|
||||||
|
|
||||||
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
|
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
|
||||||
{
|
{
|
||||||
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1]))
|
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1]))
|
||||||
return (false);
|
goto strdone;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tempbytes > 0)
|
if (tempbytes > 0 && !_pdfioFilePrintf(pdf, "%02X", *tempptr))
|
||||||
return (_pdfioFilePrintf(pdf, "%02X>", *tempptr));
|
goto strdone;
|
||||||
else
|
|
||||||
return (_pdfioFilePuts(pdf, ">"));
|
ret = _pdfioFilePuts(pdf, ">");
|
||||||
|
|
||||||
|
strdone :
|
||||||
|
|
||||||
|
_pdfioStringFreeBuffer(pdf, (char *)temp);
|
||||||
|
|
||||||
|
return (ret);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Write unencrypted string...
|
// Write unencrypted string...
|
||||||
const char *start, // Start of fragment
|
return (_pdfioFilePrintf(pdf, "%S", v->value.string));
|
||||||
*end; // End of fragment
|
|
||||||
|
|
||||||
if (!_pdfioFilePuts(pdf, "("))
|
|
||||||
return (false);
|
|
||||||
|
|
||||||
// Write a quoted string value...
|
|
||||||
for (start = v->value.string; *start; start = end)
|
|
||||||
{
|
|
||||||
// Find the next character that needs to be quoted...
|
|
||||||
for (end = start; *end; end ++)
|
|
||||||
{
|
|
||||||
if (*end == '\\' || *end == ')' || (*end & 255) < ' ')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end > start)
|
|
||||||
{
|
|
||||||
// Write unquoted (safe) characters...
|
|
||||||
if (!_pdfioFileWrite(pdf, start, (size_t)(end - start)))
|
|
||||||
return (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*end)
|
|
||||||
{
|
|
||||||
// Quote this character...
|
|
||||||
bool success; // Did the write work?
|
|
||||||
|
|
||||||
if (*end == '\\' || *end == ')')
|
|
||||||
success = _pdfioFilePrintf(pdf, "\\%c", *end);
|
|
||||||
else
|
|
||||||
success = _pdfioFilePrintf(pdf, "\\%03o", *end);
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
return (false);
|
|
||||||
|
|
||||||
end ++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (_pdfioFilePuts(pdf, ")"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,31 +769,59 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
|||||||
// 'get_date_time()' - Convert PDF date/time value to time_t.
|
// 'get_date_time()' - Convert PDF date/time value to time_t.
|
||||||
//
|
//
|
||||||
|
|
||||||
static time_t // O - Time in seconds
|
static time_t // O - Time in seconds or `0` for none
|
||||||
get_date_time(const char *s) // I - PDF date/time value
|
get_date_time(const char *s) // I - PDF date/time value
|
||||||
{
|
{
|
||||||
int i; // Looping var
|
int i; // Looping var
|
||||||
struct tm dateval; // Date value
|
struct tm dateval; // Date value
|
||||||
int offset; // Date offset
|
int offset = 0; // Date offset in seconds
|
||||||
|
time_t t; // Time value
|
||||||
|
|
||||||
|
|
||||||
PDFIO_DEBUG("get_date_time(s=\"%s\")\n", s);
|
PDFIO_DEBUG("get_date_time(s=\"%s\")\n", s);
|
||||||
|
|
||||||
// Possible date value of the form:
|
// Possible date value of the form:
|
||||||
//
|
//
|
||||||
// (D:YYYYMMDDhhmmssZ)
|
// D:YYYYMMDDhhmmssZ
|
||||||
// (D:YYYYMMDDhhmmss+HH'mm)
|
// D:YYYYMMDDhhmmss+HH'mm
|
||||||
// (D:YYYYMMDDhhmmss-HH'mm)
|
// D:YYYYMMDDhhmmss-HH'mm
|
||||||
//
|
//
|
||||||
|
|
||||||
|
if (strncmp(s, "D:", 2))
|
||||||
|
return (0);
|
||||||
|
|
||||||
for (i = 2; i < 16; i ++)
|
for (i = 2; i < 16; i ++)
|
||||||
{
|
{
|
||||||
|
// Look for date/time digits...
|
||||||
if (!isdigit(s[i] & 255) || !s[i])
|
if (!isdigit(s[i] & 255) || !s[i])
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i >= 16)
|
if (i < 6 || (i & 1))
|
||||||
{
|
{
|
||||||
|
// Short year or missing digit...
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&dateval, 0, sizeof(dateval));
|
||||||
|
|
||||||
|
dateval.tm_year = (s[2] - '0') * 1000 + (s[3] - '0') * 100 + (s[4] - '0') * 10 + s[5] - '0' - 1900;
|
||||||
|
if (i > 6)
|
||||||
|
dateval.tm_mon = (s[6] - '0') * 10 + s[7] - '0' - 1;
|
||||||
|
if (i > 8)
|
||||||
|
dateval.tm_mday = (s[8] - '0') * 10 + s[9] - '0';
|
||||||
|
else
|
||||||
|
dateval.tm_mday = 1;
|
||||||
|
if (i > 10)
|
||||||
|
dateval.tm_hour = (s[10] - '0') * 10 + s[11] - '0';
|
||||||
|
if (i > 12)
|
||||||
|
dateval.tm_min = (s[12] - '0') * 10 + s[13] - '0';
|
||||||
|
if (i > 14)
|
||||||
|
dateval.tm_sec = (s[14] - '0') * 10 + s[15] - '0';
|
||||||
|
|
||||||
|
if (i >= 16 && s[i])
|
||||||
|
{
|
||||||
|
// Get zone info...
|
||||||
if (s[i] == 'Z')
|
if (s[i] == 'Z')
|
||||||
{
|
{
|
||||||
// UTC...
|
// UTC...
|
||||||
@ -782,14 +832,20 @@ get_date_time(const char *s) // I - PDF date/time value
|
|||||||
// Timezone offset from UTC...
|
// Timezone offset from UTC...
|
||||||
if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255))
|
if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255))
|
||||||
{
|
{
|
||||||
|
offset = (s[i + 1] - '0') * 36000 + (s[i + 2] - '0') * 3600 + (s[i + 4] - '0') * 600 + (s[i + 5] - '0') * 60;
|
||||||
|
if (s[i] == '-')
|
||||||
|
offset = -offset;
|
||||||
|
|
||||||
i += 6;
|
i += 6;
|
||||||
|
|
||||||
|
// Accept trailing quote, per PDF spec...
|
||||||
if (s[i] == '\'')
|
if (s[i] == '\'')
|
||||||
i ++;
|
i ++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!s[i])
|
else
|
||||||
{
|
{
|
||||||
// Missing zone info, invalid date string...
|
// Random zone info, invalid date string...
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -800,26 +856,24 @@ get_date_time(const char *s) // I - PDF date/time value
|
|||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Date value...
|
// Convert date value to time_t...
|
||||||
memset(&dateval, 0, sizeof(dateval));
|
#if _WIN32
|
||||||
|
if ((t = _mkgmtime(&dateval)) <= 0)
|
||||||
|
return (0);
|
||||||
|
#elif defined(HAVE_TIMEGM)
|
||||||
|
if ((t = timegm(&dateval)) <= 0)
|
||||||
|
return (0);
|
||||||
|
#else
|
||||||
|
if ((t = mktime(&dateval)) <= 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
dateval.tm_year = (s[2] - '0') * 1000 + (s[3] - '0') * 100 + (s[4] - '0') * 10 + s[5] - '0' - 1900;
|
# if defined(HAVE_TM_GMTOFF)
|
||||||
dateval.tm_mon = (s[6] - '0') * 10 + s[7] - '0' - 1;
|
localtime_r(&t, &dateval);
|
||||||
dateval.tm_mday = (s[8] - '0') * 10 + s[9] - '0';
|
t -= dateval.tm_gmtoff;
|
||||||
dateval.tm_hour = (s[10] - '0') * 10 + s[11] - '0';
|
# else
|
||||||
dateval.tm_min = (s[12] - '0') * 10 + s[13] - '0';
|
t -= timezone;
|
||||||
dateval.tm_sec = (s[14] - '0') * 10 + s[15] - '0';
|
# endif // HAVE_TM_GMTOFF
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
if (s[16] == 'Z')
|
return (t - offset);
|
||||||
{
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
offset = (s[17] - '0') * 600 + (s[18] - '0') * 60 + (s[19] - '0') * 10 + s[20] - '0';
|
|
||||||
if (s[16] == '-')
|
|
||||||
offset = -offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (mktime(&dateval) + offset);
|
|
||||||
}
|
}
|
||||||
|
10
pdfio.h
10
pdfio.h
@ -23,7 +23,7 @@ extern "C" {
|
|||||||
// Version numbers...
|
// Version numbers...
|
||||||
//
|
//
|
||||||
|
|
||||||
# define PDFIO_VERSION "1.5.0"
|
# define PDFIO_VERSION "1.5.2"
|
||||||
# define PDFIO_VERSION_MAJOR 1
|
# define PDFIO_VERSION_MAJOR 1
|
||||||
# define PDFIO_VERSION_MINOR 5
|
# define PDFIO_VERSION_MINOR 5
|
||||||
|
|
||||||
@ -34,11 +34,9 @@ extern "C" {
|
|||||||
|
|
||||||
# if defined(__has_extension) || defined(__GNUC__)
|
# if defined(__has_extension) || defined(__GNUC__)
|
||||||
# define _PDFIO_PUBLIC __attribute__ ((visibility("default")))
|
# define _PDFIO_PUBLIC __attribute__ ((visibility("default")))
|
||||||
# define _PDFIO_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b)))
|
|
||||||
# define _PDFIO_DEPRECATED __attribute__ ((deprecated)) _PDFIO_PUBLIC
|
# define _PDFIO_DEPRECATED __attribute__ ((deprecated)) _PDFIO_PUBLIC
|
||||||
# else
|
# else
|
||||||
# define _PDFIO_PUBLIC
|
# define _PDFIO_PUBLIC
|
||||||
# define _PDFIO_FORMAT(a,b)
|
|
||||||
# define _PDFIO_DEPRECATED
|
# define _PDFIO_DEPRECATED
|
||||||
# endif // __has_extension || __GNUC__
|
# endif // __has_extension || __GNUC__
|
||||||
|
|
||||||
@ -183,7 +181,7 @@ extern bool pdfioDictSetNumber(pdfio_dict_t *dict, const char *key, double valu
|
|||||||
extern bool pdfioDictSetObj(pdfio_dict_t *dict, const char *key, pdfio_obj_t *value) _PDFIO_PUBLIC;
|
extern bool pdfioDictSetObj(pdfio_dict_t *dict, const char *key, pdfio_obj_t *value) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioDictSetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *value) _PDFIO_PUBLIC;
|
extern bool pdfioDictSetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *value) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioDictSetString(pdfio_dict_t *dict, const char *key, const char *value) _PDFIO_PUBLIC;
|
extern bool pdfioDictSetString(pdfio_dict_t *dict, const char *key, const char *value) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4);
|
extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char *format, ...) _PDFIO_PUBLIC;
|
||||||
|
|
||||||
extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
||||||
extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
|
extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
|
||||||
@ -245,14 +243,14 @@ extern bool pdfioStreamClose(pdfio_stream_t *st) _PDFIO_PUBLIC;
|
|||||||
extern bool pdfioStreamConsume(pdfio_stream_t *st, size_t bytes) _PDFIO_PUBLIC;
|
extern bool pdfioStreamConsume(pdfio_stream_t *st, size_t bytes) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioStreamGetToken(pdfio_stream_t *st, char *buffer, size_t bufsize) _PDFIO_PUBLIC;
|
extern bool pdfioStreamGetToken(pdfio_stream_t *st, char *buffer, size_t bufsize) _PDFIO_PUBLIC;
|
||||||
extern ssize_t pdfioStreamPeek(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC;
|
extern ssize_t pdfioStreamPeek(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioStreamPrintf(pdfio_stream_t *st, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(2,3);
|
extern bool pdfioStreamPrintf(pdfio_stream_t *st, const char *format, ...) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioStreamPutChar(pdfio_stream_t *st, int ch) _PDFIO_PUBLIC;
|
extern bool pdfioStreamPutChar(pdfio_stream_t *st, int ch) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioStreamPuts(pdfio_stream_t *st, const char *s) _PDFIO_PUBLIC;
|
extern bool pdfioStreamPuts(pdfio_stream_t *st, const char *s) _PDFIO_PUBLIC;
|
||||||
extern ssize_t pdfioStreamRead(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC;
|
extern ssize_t pdfioStreamRead(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC;
|
||||||
extern bool pdfioStreamWrite(pdfio_stream_t *st, const void *buffer, size_t bytes) _PDFIO_PUBLIC;
|
extern bool pdfioStreamWrite(pdfio_stream_t *st, const void *buffer, size_t bytes) _PDFIO_PUBLIC;
|
||||||
|
|
||||||
extern char *pdfioStringCreate(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC;
|
extern char *pdfioStringCreate(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC;
|
||||||
extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_PUBLIC;
|
extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_PUBLIC;
|
||||||
|
|
||||||
|
|
||||||
# ifdef __cplusplus
|
# ifdef __cplusplus
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<metadata>
|
<metadata>
|
||||||
<id>pdfio_native</id>
|
<id>pdfio_native</id>
|
||||||
<title>PDFio Library for VS2019+</title>
|
<title>PDFio Library for VS2019+</title>
|
||||||
<version>1.5.0</version>
|
<version>1.5.1</version>
|
||||||
<authors>Michael R Sweet</authors>
|
<authors>Michael R Sweet</authors>
|
||||||
<owners>michaelrsweet</owners>
|
<owners>michaelrsweet</owners>
|
||||||
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
|
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
|
||||||
@ -16,7 +16,7 @@
|
|||||||
<copyright>Copyright © 2019-2025 by Michael R Sweet</copyright>
|
<copyright>Copyright © 2019-2025 by Michael R Sweet</copyright>
|
||||||
<tags>pdf file native</tags>
|
<tags>pdf file native</tags>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="pdfio_native.redist" version="1.5.0" />
|
<dependency id="pdfio_native.redist" version="1.5.1" />
|
||||||
<dependency id="libpng_native.redist" version="1.6.30" />
|
<dependency id="libpng_native.redist" version="1.6.30" />
|
||||||
<dependency id="zlib_native.redist" version="1.2.11" />
|
<dependency id="zlib_native.redist" version="1.2.11" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<metadata>
|
<metadata>
|
||||||
<id>pdfio_native.redist</id>
|
<id>pdfio_native.redist</id>
|
||||||
<title>PDFio Library for VS2019+</title>
|
<title>PDFio Library for VS2019+</title>
|
||||||
<version>1.5.0</version>
|
<version>1.5.1</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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user