Save work on libpng PNG loader (Issue #90)

This commit is contained in:
Michael R Sweet 2025-02-10 21:25:59 -05:00
parent 1e5cc6ffd5
commit e686669b9d
No known key found for this signature in database
GPG Key ID: BE67C75EC81F3244
4 changed files with 290 additions and 20 deletions

View File

@ -1,9 +1,10 @@
Changes in PDFio Changes in PDFio
================ ================
v1.?.? - YYYY-MM-DD v1.5.0 - YYYY-MM-DD
------------------- -------------------
- Added support for using libpng to embed PNG images in PDF output (Issue #90)
- Updated the pdf2txt example to support font encodings. - Updated the pdf2txt example to support font encodings.

77
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for pdfio 1.4.1. # Generated by GNU Autoconf 2.71 for pdfio 1.5.0.
# #
# Report bugs to <https://github.com/michaelrsweet/pdfio/issues>. # Report bugs to <https://github.com/michaelrsweet/pdfio/issues>.
# #
@ -610,8 +610,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='pdfio' PACKAGE_NAME='pdfio'
PACKAGE_TARNAME='pdfio' PACKAGE_TARNAME='pdfio'
PACKAGE_VERSION='1.4.1' PACKAGE_VERSION='1.5.0'
PACKAGE_STRING='pdfio 1.4.1' PACKAGE_STRING='pdfio 1.5.0'
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues' PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
PACKAGE_URL='https://www.msweet.org/pdfio' PACKAGE_URL='https://www.msweet.org/pdfio'
@ -653,6 +653,7 @@ WARNINGS
CSFLAGS CSFLAGS
LIBPDFIO_STATIC LIBPDFIO_STATIC
LIBPDFIO LIBPDFIO
PKGCONFIG_LIBPNG
PKGCONFIG_REQUIRES PKGCONFIG_REQUIRES
PKGCONFIG_LIBS_PRIVATE PKGCONFIG_LIBS_PRIVATE
PKGCONFIG_LIBS PKGCONFIG_LIBS
@ -729,6 +730,7 @@ SHELL'
ac_subst_files='' ac_subst_files=''
ac_user_opts=' ac_user_opts='
enable_option_checking enable_option_checking
enable_libpng
enable_static enable_static
enable_shared enable_shared
enable_debug enable_debug
@ -1293,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.4.1 to adapt to many kinds of systems. \`configure' configures pdfio 1.5.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1359,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.4.1:";; short | recursive ) echo "Configuration of pdfio 1.5.0:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1367,6 +1369,8 @@ Optional Features:
--disable-option-checking ignore unrecognized --enable/--with options --disable-option-checking ignore unrecognized --enable/--with options
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--enable-libpng use libpng for pdfioFileCreateImageObjFromFile,
default=auto
--disable-static do not install static library --disable-static do not install static library
--enable-shared install shared library --enable-shared install shared library
--enable-debug turn on debugging, default=no --enable-debug turn on debugging, default=no
@ -1456,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.4.1 pdfio configure 1.5.0
generated by GNU Autoconf 2.71 generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc. Copyright (C) 2021 Free Software Foundation, Inc.
@ -1612,7 +1616,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.4.1, which was It was created by pdfio $as_me 1.5.0, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw $ $0$ac_configure_args_raw
@ -2368,9 +2372,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
PDFIO_VERSION="1.4.1" PDFIO_VERSION="1.5.0"
PDFIO_VERSION_MAJOR="`echo 1.4.1 | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo 1.5.0 | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo 1.4.1 | awk -F. '{printf("%d\n",$2);}'`" PDFIO_VERSION_MINOR="`echo 1.5.0 | awk -F. '{printf("%d\n",$2);}'`"
@ -4099,6 +4103,55 @@ fi
fi fi
# Check whether --enable-libpng was given.
if test ${enable_libpng+y}
then :
enableval=$enable_libpng;
fi
PKGCONFIG_LIBPNG=""
if test "x$PKGCONFIG" != x -a x$enable_libpng != xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libpng-1.6.x" >&5
printf %s "checking for libpng-1.6.x... " >&6; }
if $PKGCONFIG --exists libpng16
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; };
printf "%s\n" "#define HAVE_LIBPNG 1" >>confdefs.h
CPPFLAGS="$($PKGCONFIG --cflags libpng16) -DHAVE_LIBPNG=1 $CPPFLAGS"
LIBS="$($PKGCONFIG --libs libpng16) -lz $LIBS"
PKGCONFIG_LIBPNG="libpng >= 1.6,"
PKGCONFIG_LIBS_PRIVATE="$($PKGCONFIG --libs libpng16) $PKGCONFIG_LIBS_PRIVATE"
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; };
if test x$enable_libpng = xyes
then :
as_fn_error $? "libpng-dev 1.6 or later required for --enable-libpng." "$LINENO" 5
fi
fi
elif test x$enable_libpng = xyes
then :
as_fn_error $? "libpng-dev 1.6 or later required for --enable-libpng." "$LINENO" 5
fi
# Check whether --enable-static was given. # Check whether --enable-static was given.
if test ${enable_static+y} if test ${enable_static+y}
then : then :
@ -4935,7 +4988,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.4.1, which was This file was extended by pdfio $as_me 1.5.0, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -4991,7 +5044,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.4.1 pdfio config.status 1.5.0
configured by $0, generated by GNU Autoconf 2.71, configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@ -1,7 +1,7 @@
dnl dnl
dnl Configuration script for PDFio dnl Configuration script for PDFio
dnl dnl
dnl Copyright © 2023-2024 by Michael R Sweet dnl Copyright © 2023-2025 by Michael R Sweet
dnl dnl
dnl Licensed under Apache License v2.0. See the file "LICENSE" for more dnl Licensed under Apache License v2.0. See the file "LICENSE" for more
dnl information. dnl information.
@ -21,7 +21,7 @@ AC_PREREQ([2.70])
dnl Package name and version... dnl Package name and version...
AC_INIT([pdfio], [1.4.1], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio]) AC_INIT([pdfio], [1.5.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
PDFIO_VERSION="AC_PACKAGE_VERSION" PDFIO_VERSION="AC_PACKAGE_VERSION"
PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"
@ -121,6 +121,32 @@ AS_IF([$PKGCONFIG --exists zlib], [
]) ])
dnl libpng...
AC_ARG_ENABLE([libpng], AS_HELP_STRING([--enable-libpng], [use libpng for pdfioFileCreateImageObjFromFile, default=auto]))
PKGCONFIG_LIBPNG=""
AC_SUBST([PKGCONFIG_LIBPNG])
AS_IF([test "x$PKGCONFIG" != x -a x$enable_libpng != xno], [
AC_MSG_CHECKING([for libpng-1.6.x])
AS_IF([$PKGCONFIG --exists libpng16], [
AC_MSG_RESULT([yes]);
AC_DEFINE([HAVE_LIBPNG], 1, [Have PNG library?])
CPPFLAGS="$($PKGCONFIG --cflags libpng16) -DHAVE_LIBPNG=1 $CPPFLAGS"
LIBS="$($PKGCONFIG --libs libpng16) -lz $LIBS"
PKGCONFIG_LIBPNG="libpng >= 1.6,"
PKGCONFIG_LIBS_PRIVATE="$($PKGCONFIG --libs libpng16) $PKGCONFIG_LIBS_PRIVATE"
], [
AC_MSG_RESULT([no]);
AS_IF([test x$enable_libpng = xyes], [
AC_MSG_ERROR([libpng-dev 1.6 or later required for --enable-libpng.])
])
])
], [test x$enable_libpng = xyes], [
AC_MSG_ERROR([libpng-dev 1.6 or later required for --enable-libpng.])
])
dnl Library target... dnl Library target...
AC_ARG_ENABLE([static], AS_HELP_STRING([--disable-static], [do not install static library])) AC_ARG_ENABLE([static], AS_HELP_STRING([--disable-static], [do not install static library]))
AC_ARG_ENABLE([shared], AS_HELP_STRING([--enable-shared], [install shared library])) AC_ARG_ENABLE([shared], AS_HELP_STRING([--enable-shared], [install shared library]))

View File

@ -1,7 +1,7 @@
// //
// Content helper functions for PDFio. // Content helper 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.
@ -11,6 +11,9 @@
#include "pdfio-content.h" #include "pdfio-content.h"
#include "pdfio-base-font-widths.h" #include "pdfio-base-font-widths.h"
#include "ttf.h" #include "ttf.h"
#ifdef HAVE_LIBPNG
# include <png.h>
#endif // HAVE_LIBPNG
#include <math.h> #include <math.h>
#ifndef M_PI #ifndef M_PI
# define M_PI 3.14159265358979323846264338327950288 # define M_PI 3.14159265358979323846264338327950288
@ -57,9 +60,15 @@ typedef pdfio_obj_t *(*_pdfio_image_func_t)(pdfio_dict_t *dict, int fd);
static pdfio_obj_t *copy_jpeg(pdfio_dict_t *dict, int fd); static pdfio_obj_t *copy_jpeg(pdfio_dict_t *dict, int fd);
static pdfio_obj_t *copy_png(pdfio_dict_t *dict, int fd); static pdfio_obj_t *copy_png(pdfio_dict_t *dict, int fd);
static bool create_cp1252(pdfio_file_t *pdf); static bool create_cp1252(pdfio_file_t *pdf);
static pdfio_obj_t *create_image(pdfio_file_t *pdf, pdfio_dict_t *dict, const unsigned char *data, size_t width, size_t height, size_t num_colors, bool alpha); static pdfio_obj_t *create_image(pdfio_dict_t *dict, const unsigned char *data, size_t width, size_t height, size_t num_colors, bool alpha);
#ifdef HAVE_LIBPNG
static void png_error_func(png_structp pp, png_const_charp message);
static void png_read_func(png_structp png_ptr, png_bytep data, size_t length);
#endif // HAVE_LIBPNG
static void ttf_error_cb(pdfio_file_t *pdf, const char *message); static void ttf_error_cb(pdfio_file_t *pdf, const char *message);
#ifndef HAVE_LIBPNG
static unsigned update_png_crc(unsigned crc, const unsigned char *buffer, size_t length); static unsigned update_png_crc(unsigned crc, const unsigned char *buffer, size_t length);
#endif // !HAVE_LIBPNG
static bool write_string(pdfio_stream_t *st, bool unicode, const char *s, bool *newline); static bool write_string(pdfio_stream_t *st, bool unicode, const char *s, bool *newline);
@ -103,6 +112,7 @@ static int _pdfio_cp1252[] = // CP1252-specific character mapping
0x0178 0x0178
}; };
#ifndef HAVE_LIBPNG
static unsigned png_crc_table[256] = // CRC-32 table for PNG files static unsigned png_crc_table[256] = // CRC-32 table for PNG files
{ {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
@ -149,6 +159,7 @@ static unsigned png_crc_table[256] = // CRC-32 table for PNG files
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
}; };
#endif // !HAVE_LIBPNG
// //
@ -2016,7 +2027,7 @@ pdfioFileCreateImageObjFromData(
pdfioDictSetName(dict, "ColorSpace", defcolors[num_colors]); pdfioDictSetName(dict, "ColorSpace", defcolors[num_colors]);
// Create the image object(s)... // Create the image object(s)...
return (create_image(pdf, dict, data, width, height, num_colors, alpha)); return (create_image(dict, data, width, height, num_colors, alpha));
} }
@ -2469,8 +2480,145 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
int fd) // I - File descriptor int fd) // I - File descriptor
{ {
pdfio_obj_t *obj = NULL; // Object pdfio_obj_t *obj = NULL; // Object
pdfio_stream_t *st = NULL; // Stream for PNG data #ifdef HAVE_LIBPNG
png_structp pp = NULL; // PNG read pointer
png_infop info = NULL; // PNG info pointers
png_bytep *rows = NULL; // PNG row pointers
unsigned char *pixels = NULL; // PNG image data
unsigned i, // Looping var
color_type, // PNG color mode
width, // Width in columns
height, // Height in lines
num_colors = 0, // Number of colors
bpp; // Bytes per pixel
bool alpha; // Alpha transparency?
// Allocate memory for PNG reader structures...
if ((pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)dict->pdf, png_error_func, png_error_func)) == NULL)
{
_pdfioFileError(dict->pdf, "Unable to allocate memory for PNG file: %s", strerror(errno));
goto finish_png;
}
if ((info = png_create_info_struct(pp)) == NULL)
{
_pdfioFileError(dict->pdf, "Unable to allocate memory for PNG file: %s", strerror(errno));
goto finish_png;
}
if (setjmp(png_jmpbuf(pp)))
{
// If we get here, PNG loading failed and any errors/warnings were logged
// via the corresponding callback functions...
goto finish_png;
}
// Set max image size to 16384x16384...
png_set_user_limits(pp, 16384, 16384);
// Read from the file descriptor...
png_set_read_fn(pp, &fd, png_read_func);
// Don't throw errors with "invalid" sRGB profiles produced by Adobe apps.
# if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
png_set_option(pp, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
# endif // PNG_SKIP_sRGB_CHECK_PROFILE && PNG_SET_OPTION_SUPPORTED
// Get the image dimensions and depth...
png_read_info(pp, info);
width = png_get_image_width(pp, info);
height = png_get_image_height(pp, info);
color_type = png_get_color_type(pp, info);
if (color_type & PNG_COLOR_MASK_COLOR)
num_colors = 3;
else
num_colors = 1;
// Set decoding options...
if (png_get_valid(pp, info, PNG_INFO_tRNS))
{
// Map transparency to alpha
png_set_tRNS_to_alpha(pp);
color_type |= PNG_COLOR_MASK_ALPHA;
}
if (png_get_bit_depth(pp, info) > 8)
{
// Strip the bottom bits of 16-bit values
png_set_strip_16(pp);
}
else if (png_get_bit_depth(pp, info) < 8)
{
// Expand 1, 2, and 4-bit values to 8 bits
if (num_colors == 1)
png_set_expand_gray_1_2_4_to_8(pp);
else
png_set_packing(pp);
}
#if 1
if (color_type & PNG_COLOR_MASK_PALETTE)
{
// Convert indexed images to RGB...
png_set_palette_to_rgb(pp);
num_colors = 3;
}
#endif // 0
alpha = (color_type & PNG_COLOR_MASK_ALPHA) != 0;
bpp = num_colors + (alpha ? 1 : 0);
// Allocate memory for the image...
if ((pixels = (unsigned char *)calloc(height, width * bpp)) == NULL)
{
_pdfioFileError(dict->pdf, "Unable to allocate memory for PNG image: %s", strerror(errno));
goto finish_png;
}
if ((rows = (png_bytep *)calloc((size_t)height, sizeof(png_bytep))) == NULL)
{
_pdfioFileError(dict->pdf, "Unable to allocate memory for PNG image: %s", strerror(errno));
goto finish_png;
}
for (i = 0; i < height; i ++)
rows[i] = pixels + i * width * bpp;
// Read the image...
for (i = png_set_interlace_handling(pp); i > 0; i --)
png_read_rows(pp, rows, NULL, (png_uint_32)height);
// Grab any color space/palette information...
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromStandard(dict->pdf, num_colors, PDFIO_CS_SRGB));
// Create the image object...
obj = create_image(dict, pixels, width, height, num_colors, alpha);
finish_png:
if (pp && info)
{
png_read_end(pp, info);
png_destroy_read_struct(&pp, &info, NULL);
pp = NULL;
info = NULL;
}
free(pixels);
pixels = NULL;
free(rows);
rows = NULL;
return (obj);
#else
pdfio_dict_t *decode = NULL; // Parameters for PNG decode pdfio_dict_t *decode = NULL; // Parameters for PNG decode
pdfio_stream_t *st = NULL; // Stream for PNG data
ssize_t bytes; // Bytes read ssize_t bytes; // Bytes read
unsigned char buffer[16384]; // Read buffer unsigned char buffer[16384]; // Read buffer
unsigned i, // Looping var unsigned i, // Looping var
@ -2850,6 +2998,7 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
} }
return (NULL); return (NULL);
#endif // HAVE_LIBPNG
} }
@ -3135,7 +3284,6 @@ create_cp1252(pdfio_file_t *pdf) // I - PDF file
static pdfio_obj_t * // O - PDF object or `NULL` on error static pdfio_obj_t * // O - PDF object or `NULL` on error
create_image( create_image(
pdfio_file_t *pdf, // I - PDF file
pdfio_dict_t *dict, // I - Image dictionary pdfio_dict_t *dict, // I - Image dictionary
const unsigned char *data, // I - Image data const unsigned char *data, // I - Image data
size_t width, // I - Width in columns size_t width, // I - Width in columns
@ -3143,6 +3291,8 @@ create_image(
size_t num_colors, // I - Number of colors size_t num_colors, // I - Number of colors
bool alpha) // I - `true` if there is transparency bool alpha) // I - `true` if there is transparency
{ {
pdfio_file_t *pdf = dict->pdf;
// PDF file
pdfio_dict_t *mask_dict, // Mask image dictionary pdfio_dict_t *mask_dict, // Mask image dictionary
*decode; // DecodeParms dictionary *decode; // DecodeParms dictionary
pdfio_obj_t *obj, // Image object pdfio_obj_t *obj, // Image object
@ -3293,6 +3443,44 @@ create_image(
} }
#ifdef HAVE_LIBPNG
//
// 'png_error_func()' - PNG error message function.
//
static void
png_error_func(
png_structp pp, // I - PNG pointer
png_const_charp message) // I - Error message
{
pdfio_file_t *pdf = (pdfio_file_t *)png_get_error_ptr(pp);
// PDF file
_pdfioFileError(pdf, "Unable to create image object from PNG file: %s", message);
}
//
// 'png_read_func()' - Read from a PNG file.
//
static void
png_read_func(png_structp pp, // I - PNG pointer
png_bytep data, // I - Read buffer
size_t length) // I - Number of bytes to read
{
int *fd = (int *)png_get_io_ptr(pp);
// Pointer to file descriptor
ssize_t bytes; // Bytes read
if ((bytes = read(*fd, data, length)) < (ssize_t)length)
png_error(pp, "Unable to read from PNG file.");
}
#endif // HAVE_LIBPNG
// //
// 'ttf_error_cb()' - Relay a message from the TTF functions. // 'ttf_error_cb()' - Relay a message from the TTF functions.
// //
@ -3305,6 +3493,7 @@ ttf_error_cb(pdfio_file_t *pdf, // I - PDF file
} }
#ifndef HAVE_LIBPNG
// //
// 'update_png_crc()' - Update the CRC-32 value for a PNG chunk. // 'update_png_crc()' - Update the CRC-32 value for a PNG chunk.
// //
@ -3324,6 +3513,7 @@ update_png_crc(
return (crc); return (crc);
} }
#endif // !HAVE_LIBPNG
// //