Merge branch 'master' into region-shrink-method

This commit is contained in:
John Cupitt 2018-04-27 15:58:24 +01:00 committed by GitHub
commit b752d2e442
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1509 additions and 275 deletions

View File

@ -14,27 +14,6 @@ matrix:
- os: osx
fast_finish: true
include:
- os: linux
sudo: required
dist: precise
env:
- JPEG=/usr
- JOBS=`nproc`
- LIBTOOLFLAGS=--quiet
cache: ccache
before_install:
- sudo apt-get update -qq
- sudo apt-get install -y
automake gtk-doc-tools
gobject-introspection
libfftw3-dev libjpeg-turbo8-dev
libpng12-dev libwebp-dev libtiff4-dev libexpat1-dev
swig libmagick++-dev bc
libcfitsio3-dev libgsl0-dev libmatio-dev
liborc-0.4-dev liblcms2-dev libpoppler-glib-dev
librsvg2-dev libgif-dev
libpango1.0-dev
- os: linux
sudo: required
dist: trusty
@ -58,18 +37,14 @@ matrix:
- os: osx
osx_image: xcode7.3
env:
- JPEG=/usr/local/opt/mozjpeg
- JOBS="`sysctl -n hw.ncpu`"
- PATH="/usr/local/opt/ccache/libexec:$PATH"
cache: ccache
before_install:
- brew tap homebrew/science
- brew update
- brew install ccache
- brew install --ignore-dependencies gtk-doc
- brew install
gobject-introspection
fftw mozjpeg libexif
fftw libexif
libpng webp
swig imagemagick
cfitsio libmatio

View File

@ -9,9 +9,22 @@
- add vips_rotate() ... a convenience method for vips_similarity()
- svgload was missing is_a [lovell]
- better header sniffing for small files
- drop incompatible ICC profiles before save
- better hasalpha rules
- create funcs always make MULTIBAND (ie. no alpha)
- use O_TMPFILE, if available [Alexander--]
- set "interlaced=1" for interlaced JPG and PNG images
- add PDFium PDF loader
- add @format option to magickload
- jpegload adds a jpeg-chroma-subsample field with eg. 4:4:4 for no
chrominance subsampling.
- tiffload, pdfload, magickload set VIPS_META_N_PAGES "n-pages" metadata item
- add fontfile option to vips_text() [fangqiao]
12/3/18 started 8.6.4
- better fitting of fonts with overhanging edges, thanks Adrià
- better fitting of fonts with overhanging edges [Adrià]
- revise C++ example [fangqiao]
- strict round down on jpeg shrink on load [davidwood]
12/2/18 started 8.6.3
- use pkg-config to find libjpeg, if we can

View File

@ -202,10 +202,17 @@ via imagemagick instead.
The usual SVG loader. If this is not present, vips will try to load SVGs
via imagemagick instead.
### PDFium
If present, libvips will attempt to load PDFs via PDFium. This library must be
packaged by https://github.com/jcupitt/docker-builds/tree/master/pdfium
If PDFium is not detected, libvips will look for poppler-glib instead.
### libpoppler
The usual PDF loader. If this is not present, vips will try to load PDFs
via imagemagick instead.
via imagemagick.
### libgsf-1

View File

@ -482,7 +482,7 @@ PKG_CHECK_MODULES(TYPE_INIT, glib-2.0 < 2.36,
)
# from 2.40 we have g_win32_get_command_line() on win
PKG_CHECK_MODULES(TYPE_INIT, glib-2.0 >= 2.40,
PKG_CHECK_MODULES(WIN32_GET_COMMAND_LINE, glib-2.0 >= 2.40,
[if test x"$vips_os_win32" = x"yes"; then
AC_DEFINE(HAVE_G_WIN32_GET_COMMAND_LINE,1,[define if your glib has g_win32_get_command_line().])
have_g_win32_get_command_line=yes
@ -670,6 +670,46 @@ if test x"$magick6" = x"yes"; then
CFLAGS="$save_CFLAGS"
fi
if test x"$magick6" = x"yes"; then
# more recent magick6s have AcquireImage rather than AllocateImage argh
save_LIBS="$LIBS"
LIBS="$LIBS $MAGICK_LIBS"
AC_CHECK_FUNCS(AcquireImage,
AC_DEFINE(HAVE_ACQUIREIMAGE,1,
[define if your magick has AcquireImage.]))
LIBS="$save_LIBS"
fi
if test x"$magick6" = x"yes"; then
# more recent magick6s have SetImageExtent
save_LIBS="$LIBS"
LIBS="$LIBS $MAGICK_LIBS"
AC_CHECK_FUNCS(SetImageExtent,
AC_DEFINE(HAVE_SETIMAGEEXTENT,1,
[define if your magick has SetImageExtent.]))
LIBS="$save_LIBS"
fi
if test x"$magick6" = x"yes"; then
# GM uses SetImageAttribute(), IM uses SetImageProperty()
save_LIBS="$LIBS"
LIBS="$LIBS $MAGICK_LIBS"
AC_CHECK_FUNCS(SetImageProperty,
AC_DEFINE(HAVE_SETIMAGEPROPERTY,1,
[define if your magick has SetImageProperty.]))
LIBS="$save_LIBS"
fi
if test x"$magick6" = x"yes"; then
# GM is missing InheritException
save_LIBS="$LIBS"
LIBS="$LIBS $MAGICK_LIBS"
AC_CHECK_FUNCS(InheritException,
AC_DEFINE(HAVE_INHERITEXCEPTION,1,
[define if your magick has InheritException.]))
LIBS="$save_LIBS"
fi
# have flags to turn load and save off independently ... some people will want
# save but not load, for example
AC_ARG_ENABLE([magickload],
@ -751,6 +791,23 @@ if test x"$with_OpenEXR" != "xno"; then
)
fi
# pdfium
AC_ARG_WITH([pdfium],
AS_HELP_STRING([--without-pdfium], [build without pdfium (default: test)]))
if test x"$with_pdfium" != x"no"; then
FIND_PDFIUM([
if test x"$with_poppler" != x"no"; then
AC_MSG_WARN([PDFium found, disabling poppler])
with_poppler=no
fi
with_pdfium=yes
],[
with_pdfium=no
]
)
fi
# poppler
AC_ARG_WITH([poppler],
AS_HELP_STRING([--without-poppler], [build without poppler (default: test)]))
@ -1152,14 +1209,14 @@ fi
# Gather all up for VIPS_CFLAGS, VIPS_INCLUDES, VIPS_LIBS
# sort includes to get longer, more specific dirs first
# helps, for example, selecting graphicsmagick over imagemagick
VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $EXPAT_CFLAGS $ZLIB_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $LIBWEBPMUX_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS
VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $EXPAT_CFLAGS $ZLIB_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $LIBWEBPMUX_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $PDFIUM_INCLUDES $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS
do
echo $i
done | sort -ru`
VIPS_CFLAGS=`echo $VIPS_CFLAGS`
VIPS_CFLAGS="$VIPS_DEBUG_FLAGS $VIPS_CFLAGS"
VIPS_INCLUDES="$ZLIB_INCLUDES $PNG_INCLUDES $TIFF_INCLUDES $JPEG_INCLUDES"
VIPS_LIBS="$ZLIB_LIBS $MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $EXPAT_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $LIBWEBPMUX_LIBS $MATIO_LIBS $EXIF_LIBS -lm"
VIPS_LIBS="$ZLIB_LIBS $MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $EXPAT_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $PDFIUM_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $LIBWEBPMUX_LIBS $MATIO_LIBS $EXIF_LIBS -lm"
AC_SUBST(VIPS_LIBDIR)
@ -1253,6 +1310,7 @@ file import with OpenEXR: $with_OpenEXR
file import with OpenSlide: $with_openslide
(requires openslide-3.3.0 or later)
file import with matio: $with_matio
PDF import with PDFium $with_pdfium
PDF import with poppler-glib: $with_poppler
(requires poppler-glib 0.16.0 or later)
SVG import with librsvg-2.0: $with_rsvg

View File

@ -26,7 +26,7 @@
<programlisting language="cpp">
/* compile with:
* g++ -g -Wall try.cc `pkg-config vips-cpp --cflags --libs`
* g++ -g -Wall example.cc `pkg-config vips-cpp --cflags --libs`
*/
#include &lt;vips/vips8&gt;
@ -34,64 +34,47 @@
using namespace vips;
int
main( int argc, char **argv )
{
GOptionContext *context;
GOptionGroup *main_group;
GError *error = NULL;
main (int argc, char **argv)
{
if (VIPS_INIT (argv[0]))
vips_error_exit (NULL);
if (argc != 3)
vips_error_exit ("usage: %s input-file output-file", argv[0]);
if( VIPS_INIT( argv[0] ) )
vips_error_exit( NULL );
context = g_option_context_new( "" );
main_group = g_option_group_new( NULL, NULL, NULL, NULL, NULL );
g_option_context_set_main_group( context, main_group );
g_option_context_add_group( context, vips_get_option_group() );
if( !g_option_context_parse( context, &amp;argc, &amp;argv, &amp;error ) ) {
if( error ) {
fprintf( stderr, "%s\n", error->message );
g_error_free( error );
}
vips_error_exit( NULL );
}
VImage in = VImage::new_from_file( argv[1],
VImage::option()->
set( "access", VIPS_ACCESS_SEQUENTIAL ) );
double avg = in.avg();
printf( "avg = %g\n", avg );
printf( "width = %d\n", in.width() );
VImage in = VImage::new_from_file( argv[1],
VImage::option()->
set( "access", VIPS_ACCESS_SEQUENTIAL ) );
VImage out = in.embed( 10, 10, 1000, 1000,
VImage::option()->
set( "extend", "background" )->
set( "background", 128 ) );
out.write_to_file( argv[2] );
vips_shutdown();
return( 0 );
VImage in = VImage::new_from_file (argv[1],
VImage::option ()-&gt;set ("access", VIPS_ACCESS_SEQUENTIAL));
double avg = in.avg ();
printf ("avg = %g\n", avg);
printf ("width = %d\n", in.width ());
in = VImage::new_from_file (argv[1],
VImage::option ()-&gt;set ("access", VIPS_ACCESS_SEQUENTIAL));
VImage out = in.embed (10, 10, 1000, 1000,
VImage::option ()-&gt;
set ("extend", "background")-&gt;
set ("background", 128));
out.write_to_file (argv[2]);
vips_shutdown ();
return (0);
}
</programlisting>
<para>
Everything before <code>VImage in = VImage::..</code> is exactly
as the C API. This boilerplate gives the example a set of standard
command-line flags.
as the C API. vips_error_exit() just prints the arguments plus the
libvips error log and exits with an error code.
</para>
<para>
This line is the C++ equivalent of vips_image_new_from_file(). It works
<code>VImage in = VImage::..</code> is the C++ equivalent of
vips_image_new_from_file(). It works
in the same way, the differences being:
<itemizedlist>

View File

@ -1226,6 +1226,36 @@ vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
return( 0 );
}
/* TRUE if a profile is compatible with an image.
*/
gboolean
vips_icc_is_compatible_profile( VipsImage *image,
void *data, size_t data_length )
{
cmsHPROFILE profile;
if( !(profile = cmsOpenProfileFromMem( data, data_length )) ) {
g_warning( "%s", _( "corrupt profile" ) );
return( FALSE );
}
if( vips_image_expected_bands( image ) !=
vips_icc_profile_needs_bands( profile ) ) {
VIPS_FREEF( cmsCloseProfile, profile );
g_warning( "%s",
_( "profile incompatible with image" ) );
return( FALSE );
}
if( vips_image_expected_sig( image ) != cmsGetColorSpace( profile ) ) {
VIPS_FREEF( cmsCloseProfile, profile );
g_warning( "%s",
_( "profile colourspace differs from image" ) );
return( FALSE );
}
return( TRUE );
}
#else /*!HAVE_LCMS2*/
#include <vips/vips.h>
@ -1245,6 +1275,13 @@ vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
return( -1 );
}
gboolean
vips_icc_is_compatible_profile( VipsImage *image,
void *data, size_t data_length )
{
return( TRUE );
}
#endif /*HAVE_LCMS*/
/**

View File

@ -499,9 +499,9 @@ vips_bandjoin_const1( VipsImage *in, VipsImage **out, double c, ... )
return( result );
}
/* vips_addalpha:
/* vips_addalpha: (method)
* @in: input image
* @out: output image
* @out: (out): output image
* @...: %NULL-terminated list of optional named arguments
*
* Append an alpha channel.

View File

@ -15,6 +15,9 @@
* - gtkdoc
* 31/10/11
* - redo as a class
* 3/4/18
* - always write MULTIBAND, otherwise when we join up these things it'll
* look like we have an alpha
*/
/*
@ -97,8 +100,7 @@ vips_black_build( VipsObject *object )
vips_image_init_fields( create->out,
black->width, black->height, black->bands,
VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
black->bands == 1 ?
VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_MULTIBAND,
VIPS_INTERPRETATION_MULTIBAND,
1.0, 1.0 );
vips_image_pipelinev( create->out,
VIPS_DEMAND_STYLE_ANY, NULL );

View File

@ -134,7 +134,8 @@ vips_gaussmat_build( VipsObject *object )
vips_image_init_fields( create->out,
width, height, 1,
VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W,
VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE,
VIPS_INTERPRETATION_MULTIBAND,
1.0, 1.0 );
vips_image_pipelinev( create->out,
VIPS_DEMAND_STYLE_ANY, NULL );

View File

@ -134,7 +134,7 @@ vips_gaussnoise_build( VipsObject *object )
vips_image_init_fields( create->out,
gaussnoise->width, gaussnoise->height, 1,
VIPS_FORMAT_FLOAT, VIPS_CODING_NONE,
VIPS_INTERPRETATION_B_W, 1.0, 1.0 );
VIPS_INTERPRETATION_MULTIBAND, 1.0, 1.0 );
vips_image_pipelinev( create->out,
VIPS_DEMAND_STYLE_ANY, NULL );

View File

@ -153,7 +153,8 @@ vips_logmat_build( VipsObject *object )
vips_image_init_fields( create->out,
width, height, 1,
VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W,
VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE,
VIPS_INTERPRETATION_MULTIBAND,
1.0, 1.0 );
vips_image_pipelinev( create->out,
VIPS_DEMAND_STYLE_ANY, NULL );

View File

@ -249,7 +249,7 @@ vips_perlin_build( VipsObject *object )
vips_image_init_fields( create->out,
perlin->width, perlin->height, 1,
perlin->uchar ? VIPS_FORMAT_UCHAR : VIPS_FORMAT_FLOAT,
VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W,
VIPS_CODING_NONE, VIPS_INTERPRETATION_MULTIBAND,
1.0, 1.0 );
vips_image_pipelinev( create->out,
VIPS_DEMAND_STYLE_ANY, NULL );

View File

@ -109,10 +109,9 @@ vips_point_build( VipsObject *object )
return( -1 );
in = t[2];
/* uchar mode always does B_W. We don't want FOURIER or
* whatever in this case.
/* We don't want FOURIER or whatever in this case.
*/
in->Type = VIPS_INTERPRETATION_B_W;
in->Type = VIPS_INTERPRETATION_MULTIBAND;
}
if( vips_image_write( in, create->out ) )
@ -137,7 +136,7 @@ vips_point_class_init( VipsPointClass *class )
class->point = NULL;
class->min = -1.0;
class->max = 1.0;
class->interpretation = VIPS_INTERPRETATION_B_W;
class->interpretation = VIPS_INTERPRETATION_MULTIBAND;
VIPS_ARG_INT( class, "width", 2,
_( "Width" ),

View File

@ -20,6 +20,8 @@
* - implement auto fitting of text inside bounds
* 12/3/18
* - better fitting of fonts with overhanging edges, thanks Adrià
* 26/4/18 fangqiao
* - add fontfile option
*/
/*
@ -80,6 +82,7 @@ typedef struct _VipsText {
int spacing;
VipsAlign align;
int dpi;
char *fontfile;
FT_Bitmap bitmap;
PangoContext *context;
@ -337,6 +340,15 @@ vips_text_build( VipsObject *object )
if( !vips_text_fontmap )
vips_text_fontmap = pango_ft2_font_map_new();
if( text->fontfile &&
!FcConfigAppFontAddFile( NULL,
(const FcChar8 *) text->fontfile ) ) {
vips_error( class->nickname,
_( "unable to load font \"%s\"" ), text->fontfile );
g_mutex_unlock( vips_text_lock );
return( -1 );
}
/* If our caller set height and not dpi, we adjust dpi until
* we get a fit.
*/
@ -379,7 +391,8 @@ vips_text_build( VipsObject *object )
vips_image_init_fields( create->out,
text->bitmap.width, text->bitmap.rows, 1,
VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W,
VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
VIPS_INTERPRETATION_MULTIBAND,
1.0, 1.0 );
vips_image_pipelinev( create->out,
VIPS_DEMAND_STYLE_ANY, NULL );
@ -476,6 +489,13 @@ vips_text_class_init( VipsTextClass *class )
G_STRUCT_OFFSET( VipsText, spacing ),
0, 1000000, 0 );
VIPS_ARG_STRING( class, "fontfile", 12,
_( "Font file" ),
_( "Load this font file" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsText, fontfile ),
NULL );
}
static void
@ -498,6 +518,7 @@ vips_text_init( VipsText *text )
* Optional arguments:
*
* * @font: %gchararray, font to render with
* * @fontfile: %gchararray, load this font file
* * @width: %gint, image should be no wider than this many pixels
* * @height: %gint, image should be no higher than this many pixels
* * @align: #VipsAlign, left/centre/right alignment
@ -515,6 +536,9 @@ vips_text_init( VipsText *text )
* @font is the font to render with, as a fontconfig name. Examples might be
* "sans 12" or perhaps "bitstream charter bold 10".
*
* You can specify a font to load with @fontfile. You'll need to also set the
* name of the font with @font.
*
* @width is the number of pixels to word-wrap at. Lines of text wider than
* this will be broken at word bounaries.
* @align can be used to set the alignment style for multi-line
@ -531,7 +555,7 @@ vips_text_init( VipsText *text )
* @dpi sets the resolution to render at. "sans 12" at 72 dpi draws characters
* approximately 12 pixels high.
*
* @spacing sets the line spacing, in points. It would typicallly be something
* @spacing sets the line spacing, in points. It would typically be something
* like font size times 1.2.
*
* See also: vips_xyz(), vips_text(), vips_gaussnoise().

View File

@ -281,7 +281,8 @@ vips_worley_build( VipsObject *object )
vips_image_init_fields( create->out,
worley->width, worley->height, 1,
VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W,
VIPS_FORMAT_FLOAT, VIPS_CODING_NONE,
VIPS_INTERPRETATION_MULTIBAND,
1.0, 1.0 );
vips_image_pipelinev( create->out,
VIPS_DEMAND_STYLE_ANY, NULL );

View File

@ -54,6 +54,7 @@
static int
tiff2vips( const char *name, IMAGE *out, gboolean header_only )
{
#ifdef HAVE_TIFF
char filename[FILENAME_MAX];
char mode[FILENAME_MAX];
char *p, *q;
@ -84,7 +85,6 @@ tiff2vips( const char *name, IMAGE *out, gboolean header_only )
* malloc if all we are doing is looking at fields.
*/
#ifdef HAVE_TIFF
if( !header_only &&
!seq &&
!vips__istifftiled( filename ) &&

View File

@ -6,6 +6,7 @@ libforeign_la_SOURCES = \
gifload.c \
cairo.c \
pdfload.c \
pdfload_pdfium.c \
svgload.c \
radiance.c \
radload.c \

View File

@ -22,6 +22,8 @@
* - META_SEQ support moved here
* 5/3/18
* - block _start if one start fails, see #893
* 1/4/18
* - drop incompatible ICC profiles before save
*/
/*
@ -1504,6 +1506,21 @@ vips__foreign_convert_saveable( VipsImage *in, VipsImage **ready,
in = out;
}
/* Some format libraries, like libpng, will throw a hard error if the
* profile is inappropriate for this image type. With profiles inherited
* from a source image, this can happen all the time, so we
* want to just drop the profile in this case.
*/
if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) {
void *data;
size_t length;
if( !vips_image_get_blob( in, VIPS_META_ICC_NAME,
&data, &length ) &&
!vips_icc_is_compatible_profile( in, data, length ) )
vips_image_remove( in, VIPS_META_ICC_NAME );
}
*ready = in;
return( 0 );
@ -1849,6 +1866,12 @@ vips_foreign_operation_init( void )
vips_foreign_load_pdf_buffer_get_type();
#endif /*HAVE_POPPLER*/
#ifdef HAVE_PDFIUM
vips_foreign_load_pdf_get_type();
vips_foreign_load_pdf_file_get_type();
vips_foreign_load_pdf_buffer_get_type();
#endif /*HAVE_PDFIUM*/
#ifdef HAVE_RSVG
vips_foreign_load_svg_get_type();
vips_foreign_load_svg_file_get_type();

View File

@ -94,6 +94,10 @@
* - revert previous warning change: libvips reports serious corruption,
* like a truncated file, as a warning and we need to be able to catch
* that
* 9/4/18
* - set interlaced=1 for interlaced images
* 10/4/18
* - strict round down on shrink-on-load
*/
/*
@ -151,8 +155,6 @@
/* Stuff we track during a read.
*/
typedef struct _ReadJpeg {
VipsImage *out;
/* Shrink by this much during load. 1, 2, 4, 8.
*/
int shrink;
@ -177,6 +179,13 @@ typedef struct _ReadJpeg {
* during load.
*/
gboolean autorotate;
/* cinfo->output_width and height can be larger than we want since
* libjpeg rounds up on shrink-on-load. This is the real size we will
* output, as opposed to the size we decompress to.
*/
int output_width;
int output_height;
} ReadJpeg;
/* This can be called many times.
@ -228,7 +237,6 @@ readjpeg_new( VipsImage *out, int shrink, gboolean fail, gboolean autorotate )
if( !(jpeg = VIPS_NEW( out, ReadJpeg )) )
return( NULL );
jpeg->out = out;
jpeg->shrink = shrink;
jpeg->fail = fail;
jpeg->filename = NULL;
@ -271,6 +279,25 @@ readjpeg_file( ReadJpeg *jpeg, const char *filename )
return( 0 );
}
static const char *
find_chroma_subsample( struct jpeg_decompress_struct *cinfo )
{
gboolean has_subsample;
/* libjpeg only uses 4:4:4 and 4:2:0, confusingly.
*
* http://poynton.ca/PDFs/Chroma_subsampling_notation.pdf
*/
has_subsample = cinfo->max_h_samp_factor > 1 ||
cinfo->max_v_samp_factor > 1;
if( cinfo->num_components > 3 )
/* A cmyk image.
*/
return( has_subsample ? "4:2:0:4" : "4:4:4:4" );
else
return( has_subsample ? "4:2:0" : "4:4:4" );
}
static int
attach_blob( VipsImage *im, const char *field, void *data, int data_length )
{
@ -408,12 +435,32 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
/* cinfo->output_width and cinfo->output_height round up with
* shrink-on-load. For example, if the image is 1801 pixels across and
* we shrink by 4, the output will be 450.25 pixels across,
* cinfo->output_width with be 451, and libjpeg will write a black
* column of pixels down the right.
*
* We must strictly round down, since we don't want fractional pixels
* along the bottom and right.
*/
jpeg->output_width = cinfo->image_width / jpeg->shrink;
jpeg->output_height = cinfo->image_height / jpeg->shrink;
/* Interlaced jpegs need lots of memory to read, so our caller needs
* to know.
*/
(void) vips_image_set_int( out, "jpeg-multiscan",
jpeg_has_multiple_scans( cinfo ) );
/* 8.7 adds this for PNG as well, so we have a new format-neutral name.
*/
if( jpeg_has_multiple_scans( cinfo ) )
vips_image_set_int( out, "interlaced", 1 );
(void) vips_image_set_string( out, "jpeg-chroma-subsample",
find_chroma_subsample( cinfo ) );
/* Look for EXIF and ICC profile.
*/
for( p = cinfo->marker_list; p; p = p->next ) {
@ -684,15 +731,20 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out )
printf( "read_jpeg_image: starting decompress\n" );
#endif /*DEBUG*/
/* We must crop after the seq, or our generate may not be asked for
* full lines of pixels and will attempt to write beyond the buffer.
*/
if( vips_image_generate( t[0],
NULL, read_jpeg_generate, NULL,
jpeg, NULL ) ||
vips_sequential( t[0], &t[1],
"tile_height", 8,
NULL ) )
NULL ) ||
vips_extract_area( t[1], &t[2],
0, 0, jpeg->output_width, jpeg->output_height, NULL ) )
return( -1 );
im = t[1];
im = t[2];
if( jpeg->autorotate )
im = read_jpeg_rotate( VIPS_OBJECT( out ), im );
@ -731,6 +783,11 @@ vips__jpeg_read( ReadJpeg *jpeg, VipsImage *out, gboolean header_only )
if( read_jpeg_header( jpeg, out ) )
return( -1 );
/* Patch in the correct size.
*/
out->Xsize = jpeg->output_width;
out->Ysize = jpeg->output_height;
/* Swap width and height if we're going to rotate this image.
*/
if( jpeg->autorotate ) {

View File

@ -374,6 +374,11 @@ vips_foreign_load_jpeg_buffer_init( VipsForeignLoadJpegBuffer *buffer )
* memory to load, so this field gives callers a chance to handle these
* images differently.
*
* The string-valued field "jpeg-chroma-subsample" gives the chroma subsample
* in standard notation. 4:4:4 means no subsample, 4:2:0 means YCbCr with
* Cb and Cr subsampled horizontally and vertically, 4:4:4:4 means a CMYK
* image with no subsampling.
*
* The EXIF thumbnail, if present, is attached to the image as
* "jpeg-thumbnail-data". See vips_image_get_blob().
*

View File

@ -82,13 +82,6 @@ magick_set_property( Image *image, const char *property, const char *value,
(void) SetImageProperty( image, property, value, exception );
}
int
magick_set_image_colorspace( Image *image, const ColorspaceType colorspace,
ExceptionInfo *exception)
{
return( SetImageColorspace( image, colorspace, exception ) );
}
void
magick_inherit_exception( ExceptionInfo *exception, Image *image )
{
@ -104,7 +97,13 @@ Image*
magick_acquire_image(const ImageInfo *image_info, ExceptionInfo *exception)
{
(void) exception;
#ifdef HAVE_ACQUIREIMAGE
return( AcquireImage( image_info ) );
#else /*!HAVE_ACQUIREIMAGE*/
/* IM5-ish and GraphicsMagick use AllocateImage().
*/
return( AllocateImage( image_info ) );
#endif
}
void
@ -112,7 +111,13 @@ magick_acquire_next_image( const ImageInfo *image_info, Image *image,
ExceptionInfo *exception )
{
(void) exception;
#ifdef HAVE_ACQUIREIMAGE
AcquireNextImage( image_info, image );
#else /*!HAVE_ACQUIREIMAGE*/
/* IM5-ish and GraphicsMagick use AllocateNextImage().
*/
AllocateNextImage( image_info, image );
#endif
}
int
@ -120,15 +125,38 @@ magick_set_image_size( Image *image, const size_t width, const size_t height,
ExceptionInfo *exception )
{
(void) exception;
#ifdef HAVE_SETIMAGEEXTENT
return( SetImageExtent( image, width, height ) );
#else /*!HAVE_SETIMAGEEXTENT*/
image->columns = width;
image->rows = height;
/* imagemagick does a SyncImagePixelCache() at the end of
* SetImageExtent(), but GM does not really have an equivalent. Just
* always return True.
*/
return( MagickTrue );
#endif /*HAVE_SETIMAGEEXTENT*/
}
int
magick_import_pixels( Image *image, const ssize_t x, const ssize_t y,
const size_t width, const size_t height, const char *map,
const StorageType type,const void *pixels, ExceptionInfo *exception )
const StorageType type, const void *pixels, ExceptionInfo *exception )
{
(void) exception;
/* GM does not seem to have a simple equivalent, unfortunately.
*
* Looks like we'd need to call
*
* extern MagickExport PixelPacket
* *SetImagePixels(Image *image,const long x,const long y,
* const unsigned long columns,const unsigned
* long rows);
*
* then repack pixels into that area using map and storage_type.
*/
return( ImportImagePixels( image, x, y, width, height, map,
type, pixels ) );
}
@ -138,21 +166,19 @@ magick_set_property( Image *image, const char *property, const char *value,
ExceptionInfo *exception )
{
(void) exception;
#ifdef HAVE_SETIMAGEPROPERTY
(void) SetImageProperty( image, property, value );
}
int
magick_set_image_colorspace( Image *image, const ColorspaceType colorspace,
ExceptionInfo *exception )
{
(void) exception;
return( SetImageColorspace( image, colorspace ) );
#else /*!HAVE_SETIMAGEPROPERTY*/
(void) SetImageAttribute( image, property, value );
#endif /*HAVE_SETIMAGEPROPERTY*/
}
void
magick_inherit_exception( ExceptionInfo *exception, Image *image )
{
#ifdef HAVE_INHERITEXCEPTION
InheritException( exception, &image->exception );
#endif /*HAVE_INHERITEXCEPTION*/
}
#endif /*HAVE_MAGICK6*/
@ -182,7 +208,7 @@ magick_genesis_cb( void *client )
printf( "magick_genesis_cb:\n" );
#endif /*DEBUG*/
#ifdef HAVE_MAGICKCOREGENESIS
#if defined(HAVE_MAGICKCOREGENESIS) || defined(HAVE_MAGICK7)
MagickCoreGenesis( vips_get_argv0(), MagickFalse );
#else /*!HAVE_MAGICKCOREGENESIS*/
InitializeMagick( "" );

View File

@ -46,8 +46,8 @@ Image *magick_acquire_image( const ImageInfo *image_info,
ExceptionInfo *exception );
void magick_acquire_next_image( const ImageInfo *image_info,
Image *image, ExceptionInfo *exception );
int magick_set_image_size( Image *image, const size_t width, const size_t height,
ExceptionInfo *exception );
int magick_set_image_size( Image *image,
const size_t width, const size_t height, ExceptionInfo *exception );
int magick_import_pixels( Image *image, const ssize_t x, const ssize_t y,
const size_t width, const size_t height, const char *map,
const StorageType type,const void *pixels, ExceptionInfo *exception );

View File

@ -55,6 +55,8 @@
* - remove @all_frames, add @n
* 23/2/17
* - try using GetImageChannelDepth() instead of ->depth
* 24/4/18
* - add format hint
*/
/*
@ -132,7 +134,14 @@ typedef struct _Read {
ImageInfo *image_info;
ExceptionInfo exception;
/* Number of pages in image.
*/
int n_pages;
/* Number of pages we will read.
*/
int n_frames;
Image **frames;
int frame_height;
@ -172,7 +181,7 @@ read_close( VipsImage *im, Read *read )
static Read *
read_new( const char *filename, VipsImage *im,
const char *density, int page, int n )
const char *format, const char *density, int page, int n )
{
Read *read;
static int inited = 0;
@ -201,6 +210,7 @@ read_new( const char *filename, VipsImage *im,
read->image = NULL;
read->image_info = CloneImageInfo( NULL );
GetExceptionInfo( &read->exception );
read->n_pages = 0;
read->n_frames = 0;
read->frames = NULL;
read->frame_height = 0;
@ -215,6 +225,12 @@ read_new( const char *filename, VipsImage *im,
vips_strncpy( read->image_info->filename,
filename, MaxTextExtent );
/* The file format hint, eg. "ICO".
*/
if( format )
vips_strncpy( read->image_info->magick,
format, MaxTextExtent );
/* Canvas resolution for rendering vector formats like SVG.
*/
VIPS_SETSTR( read->image_info->density, density );
@ -480,11 +496,16 @@ parse_header( Read *read )
which says this is a volumetric image
*/
read->n_pages = GetImageListLength( image );
read->n_frames = 0;
for( p = image; p; (p = GetNextImageInList( p )) ) {
int p_depth =
GetImageChannelDepth( p, AllChannels, &p->exception );
if( p->columns != (unsigned int) im->Xsize ||
p->rows != (unsigned int) im->Ysize ||
get_bands( p ) != im->Bands ) {
get_bands( p ) != im->Bands ||
p_depth != depth ) {
#ifdef DEBUG
printf( "frame %d differs\n", read->n_frames );
printf( "%zdx%zd, %d bands\n",
@ -504,7 +525,7 @@ parse_header( Read *read )
read->n_frames = 1;
#ifdef DEBUG
printf( "image has %d frames\n", read->n_frames );
printf( "will read %d frames\n", read->n_frames );
#endif /*DEBUG*/
if( read->n != -1 )
@ -525,6 +546,8 @@ parse_header( Read *read )
im->Ysize *= read->n_frames;
}
vips_image_set_int( im, VIPS_META_N_PAGES, read->n_pages );
return( 0 );
}
@ -743,7 +766,8 @@ magick_fill_region( VipsRegion *out,
int
vips__magick_read( const char *filename,
VipsImage *out, const char *density, int page, int n )
VipsImage *out, const char *format, const char *density,
int page, int n )
{
Read *read;
@ -751,7 +775,7 @@ vips__magick_read( const char *filename,
printf( "magick2vips: vips__magick_read: %s\n", filename );
#endif /*DEBUG*/
if( !(read = read_new( filename, out, density, page, n )) )
if( !(read = read_new( filename, out, format, density, page, n )) )
return( -1 );
#ifdef DEBUG
@ -782,7 +806,8 @@ vips__magick_read( const char *filename,
*/
int
vips__magick_read_header( const char *filename,
VipsImage *out, const char *density, int page, int n )
VipsImage *out, const char *format, const char *density,
int page, int n )
{
Read *read;
@ -790,7 +815,7 @@ vips__magick_read_header( const char *filename,
printf( "vips__magick_read_header: %s\n", filename );
#endif /*DEBUG*/
if( !(read = read_new( filename, out, density, page, n )) )
if( !(read = read_new( filename, out, format, density, page, n )) )
return( -1 );
#ifdef DEBUG
@ -824,7 +849,8 @@ vips__magick_read_header( const char *filename,
int
vips__magick_read_buffer( const void *buf, const size_t len,
VipsImage *out, const char *density, int page, int n )
VipsImage *out, const char *format, const char *density,
int page, int n )
{
Read *read;
@ -832,7 +858,7 @@ vips__magick_read_buffer( const void *buf, const size_t len,
printf( "magick2vips: vips__magick_read_buffer: %p %zu\n", buf, len );
#endif /*DEBUG*/
if( !(read = read_new( NULL, out, density, page, n )) )
if( !(read = read_new( NULL, out, format, density, page, n )) )
return( -1 );
#ifdef DEBUG
@ -859,7 +885,8 @@ vips__magick_read_buffer( const void *buf, const size_t len,
int
vips__magick_read_buffer_header( const void *buf, const size_t len,
VipsImage *out, const char *density, int page, int n )
VipsImage *out, const char *format, const char *density,
int page, int n )
{
Read *read;
@ -867,7 +894,7 @@ vips__magick_read_buffer_header( const void *buf, const size_t len,
printf( "vips__magick_read_buffer_header: %p %zu\n", buf, len );
#endif /*DEBUG*/
if( !(read = read_new( NULL, out, density, page, n )) )
if( !(read = read_new( NULL, out, format, density, page, n )) )
return( -1 );
#ifdef DEBUG

View File

@ -4,6 +4,8 @@
* - from magickload
* 25/11/16
* - add @n, deprecate @all_frames (just sets n = -1)
* 24/4/18
* - add format hint
*/
/*
@ -56,6 +58,8 @@
#include <MagickCore/MagickCore.h>
#include "magick.h"
typedef struct _VipsForeignLoadMagick7 {
VipsForeignLoad parent_object;
@ -63,6 +67,7 @@ typedef struct _VipsForeignLoadMagick7 {
*/
gboolean all_frames;
char *format; /* Format hint */
char *density; /* Load at this resolution */
int page; /* Load this page (frame) */
int n; /* Load this many pages */
@ -71,7 +76,11 @@ typedef struct _VipsForeignLoadMagick7 {
ImageInfo *image_info;
ExceptionInfo *exception;
int n_frames; /* Number of frames in file */
/* Number of pages in image.
*/
int n_pages;
int n_frames; /* Number of frames we will read */
Image **frames; /* An Image* for each frame */
CacheView **cache_view; /* A CacheView for each frame */
int frame_height;
@ -278,26 +287,6 @@ vips_foreign_load_magick7_dispose( GObject *gobject )
dispose( gobject );
}
static void *
vips_foreign_load_magick7_genesis_cb( void *client )
{
#ifdef DEBUG
printf( "vips_foreign_load_magick7_genesis:\n" );
#endif /*DEBUG*/
MagickCoreGenesis( vips_get_argv0(), MagickFalse );
return( NULL );
}
static void
vips_foreign_load_magick7_genesis( void )
{
static GOnce once = G_ONCE_INIT;
VIPS_ONCE( &once, vips_foreign_load_magick7_genesis_cb, NULL );
}
static int
vips_foreign_load_magick7_build( VipsObject *object )
{
@ -307,7 +296,7 @@ vips_foreign_load_magick7_build( VipsObject *object )
printf( "vips_foreign_load_magick7_build: %p\n", object );
#endif /*DEBUG*/
vips_foreign_load_magick7_genesis();
magick_genesis();
magick7->image_info = CloneImageInfo( NULL );
magick7->exception = AcquireExceptionInfo();
@ -319,6 +308,12 @@ vips_foreign_load_magick7_build( VipsObject *object )
if( magick7->all_frames )
magick7->n = -1;
/* The file format hint, eg. "ICO".
*/
if( magick7->format )
vips_strncpy( magick7->image_info->magick,
magick7->format, MaxTextExtent );
/* Canvas resolution for rendering vector formats like SVG.
*/
VIPS_SETSTR( magick7->image_info->density, magick7->density );
@ -377,12 +372,12 @@ vips_foreign_load_magick7_class_init( VipsForeignLoadMagick7Class *class )
vips_foreign_load_magick7_get_flags_filename;
load_class->get_flags = vips_foreign_load_magick7_get_flags;
VIPS_ARG_BOOL( class, "all_frames", 3,
_( "all_frames" ),
_( "Read all frames from an image" ),
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsForeignLoadMagick7, all_frames ),
FALSE );
VIPS_ARG_STRING( class, "format", 3,
_( "Format" ),
_( "Image format hint" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadMagick7, format ),
NULL );
VIPS_ARG_STRING( class, "density", 4,
_( "Density" ),
@ -405,6 +400,13 @@ vips_foreign_load_magick7_class_init( VipsForeignLoadMagick7Class *class )
G_STRUCT_OFFSET( VipsForeignLoadMagick7, n ),
-1, 100000, 1 );
VIPS_ARG_BOOL( class, "all_frames", 7,
_( "all_frames" ),
_( "Read all frames from an image" ),
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsForeignLoadMagick7, all_frames ),
FALSE );
}
static void
@ -423,6 +425,26 @@ vips_foreign_load_magick7_error( VipsForeignLoadMagick7 *magick7 )
magick7->exception->description );
}
static int
magick7_get_bands( Image *image )
{
int bands;
int i;
/* We skip all index channels. Lots of images can have these, it's not
* just the palette ones.
*/
bands = 0;
for( i = 0; i < GetPixelChannels( image ); i++ ) {
PixelChannel channel = GetPixelChannelChannel( image, i );
if( channel != IndexPixelChannel )
bands += 1;
}
return( bands );
}
static int
vips_foreign_load_magick7_parse( VipsForeignLoadMagick7 *magick7,
Image *image, VipsImage *out )
@ -430,7 +452,7 @@ vips_foreign_load_magick7_parse( VipsForeignLoadMagick7 *magick7,
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( magick7 );
const char *key;
int i;
Image *p;
#ifdef DEBUG
printf( "image->depth = %zd\n", image->depth );
@ -446,17 +468,7 @@ vips_foreign_load_magick7_parse( VipsForeignLoadMagick7 *magick7,
out->Xsize = image->columns;
out->Ysize = image->rows;
magick7->frame_height = image->rows;
/* We skip all index channels. Lots of images can have these, it's not
* just the palette ones.
*/
out->Bands = 0;
for( i = 0; i < GetPixelChannels( image ); i++ ) {
PixelChannel channel = GetPixelChannelChannel( image, i );
if( channel != IndexPixelChannel )
out->Bands += 1;
}
out->Bands = magick7_get_bands( image );
/* Depth can be 'fractional'. You'd think we should use
* GetImageDepth() but that seems to compute something very complex.
@ -554,16 +566,43 @@ vips_foreign_load_magick7_parse( VipsForeignLoadMagick7 *magick7,
vips_image_set_string( out, vips_buf_all( &name ), value );
}
magick7->n_pages = GetImageListLength( GetFirstImageInList( image ) );
#ifdef DEBUG
printf( "image has %d pages\n", magick7->n_pages );
#endif /*DEBUG*/
/* Do we have a set of equal-sized frames? Append them.
FIXME ... there must be an attribute somewhere from dicom read
which says this is a volumetric image
*/
magick7->n_frames = GetImageListLength( GetFirstImageInList( image ) );
magick7->n_frames = 0;
for( p = image; p; (p = GetNextImageInList( p )) ) {
if( p->columns != (unsigned int) out->Xsize ||
p->rows != (unsigned int) out->Ysize ||
magick7_get_bands( p ) != out->Bands ||
p->depth != image->depth ) {
#ifdef DEBUG
printf( "frame %d differs\n", magick7->n_frames );
printf( "%zdx%zd, %d bands\n",
p->columns, p->rows, magick7_get_bands( p ) );
printf( "first frame is %dx%d, %d bands\n",
out->Xsize, out->Ysize, out->Bands );
#endif /*DEBUG*/
break;
}
magick7->n_frames += 1;
}
if( p )
/* Nope ... just do the first image in the list.
*/
magick7->n_frames = 1;
#ifdef DEBUG
printf( "image has %d frames\n", magick7->n_frames );
printf( "will read %d frames\n", magick7->n_frames );
#endif /*DEBUG*/
if( magick7->n != -1 )
@ -576,11 +615,13 @@ vips_foreign_load_magick7_parse( VipsForeignLoadMagick7 *magick7,
out->Ysize *= magick7->n_frames;
}
vips_image_set_int( out, VIPS_META_N_PAGES, magick7->n_pages );
return( 0 );
}
/* We don't bother with GetPixelReadMask((), assume it's everywhere. Don't
* bother with traits, assume taht's always update.
/* We don't bother with GetPixelReadMask(), assume it's everywhere. Don't
* bother with traits, assume that's always updated.
*
* We do skip index channels. Palette images add extra index channels
* containing the index value from the file before colourmap lookup.
@ -681,7 +722,8 @@ vips_foreign_load_magick7_load( VipsForeignLoadMagick7 *magick7 )
/* Record frame pointers.
*/
g_assert( !magick7->frames );
if( !(magick7->frames = VIPS_ARRAY( NULL, magick7->n_frames, Image * )) )
if( !(magick7->frames =
VIPS_ARRAY( NULL, magick7->n_frames, Image * )) )
return( -1 );
p = magick7->image;
for( i = 0; i < magick7->n_frames; i++ ) {
@ -735,7 +777,7 @@ ismagick7( const char *filename )
ExceptionInfo *exception;
int result;
vips_foreign_load_magick7_genesis();
magick_genesis();
/* Horribly slow :-(
*/
@ -835,7 +877,7 @@ vips_foreign_load_magick7_buffer_is_a_buffer( const void *buf, size_t len )
ExceptionInfo *exception;
int result;
vips_foreign_load_magick7_genesis();
magick_genesis();
/* Horribly slow :-(
*/

View File

@ -12,6 +12,8 @@
* - add @n, deprecate @all_frames (just sets n = -1)
* 8/9/17
* - don't cache magickload
* 24/4/18
* - add format hint
*/
/*
@ -72,6 +74,7 @@ typedef struct _VipsForeignLoadMagick {
gboolean all_frames;
char *density; /* Load at this resolution */
char *format; /* Load format hint */
int page; /* Load this page (frame) */
int n; /* Load this many pages */
@ -122,28 +125,35 @@ vips_foreign_load_magick_class_init( VipsForeignLoadMagickClass *class )
vips_foreign_load_magick_get_flags_filename;
load_class->get_flags = vips_foreign_load_magick_get_flags;
VIPS_ARG_BOOL( class, "all_frames", 3,
VIPS_ARG_STRING( class, "format", 3,
_( "Format" ),
_( "Image format hint" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadMagick, format ),
NULL );
VIPS_ARG_BOOL( class, "all_frames", 4,
_( "all_frames" ),
_( "Read all frames from an image" ),
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsForeignLoadMagick, all_frames ),
FALSE );
VIPS_ARG_STRING( class, "density", 4,
VIPS_ARG_STRING( class, "density", 5,
_( "Density" ),
_( "Canvas resolution for rendering vector formats like SVG" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadMagick, density ),
NULL );
VIPS_ARG_INT( class, "page", 5,
VIPS_ARG_INT( class, "page", 6,
_( "Page" ),
_( "Load this page from the file" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadMagick, page ),
0, 100000, 0 );
VIPS_ARG_INT( class, "n", 6,
VIPS_ARG_INT( class, "n", 7,
_( "n" ),
_( "Load this many pages" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
@ -177,7 +187,7 @@ ismagick( const char *filename )
t = vips_image_new();
vips_error_freeze();
result = vips__magick_read_header( filename, t, NULL, 0, 1 );
result = vips__magick_read_header( filename, t, NULL, NULL, 0, 1 );
g_object_unref( t );
vips_error_thaw();
@ -202,7 +212,8 @@ vips_foreign_load_magick_file_header( VipsForeignLoad *load )
magick->n = -1;
if( vips__magick_read( magick_file->filename,
load->out, magick->density, magick->page, magick->n ) )
load->out, magick->format, magick->density,
magick->page, magick->n ) )
return( -1 );
VIPS_SETSTR( load->out->filename, magick_file->filename );
@ -262,7 +273,8 @@ vips_foreign_load_magick_buffer_is_a_buffer( const void *buf, size_t len )
t = vips_image_new();
vips_error_freeze();
result = vips__magick_read_buffer_header( buf, len, t, NULL, 0, 1 );
result = vips__magick_read_buffer_header( buf, len, t,
NULL, NULL, 0, 1 );
g_object_unref( t );
vips_error_thaw();
@ -288,7 +300,8 @@ vips_foreign_load_magick_buffer_header( VipsForeignLoad *load )
if( vips__magick_read_buffer(
magick_buffer->buf->data, magick_buffer->buf->length,
load->out, magick->density, magick->page, magick->n ) )
load->out, magick->format, magick->density, magick->page,
magick->n ) )
return( -1 );
return( 0 );
@ -338,6 +351,7 @@ vips_foreign_load_magick_buffer_init( VipsForeignLoadMagickBuffer *buffer )
*
* Optional arguments:
*
* * @format: string, format hint, eg. "JPG"
* * @page: %gint, load from this page
* * @n: %gint, load this many pages
* * @density: string, canvas resolution for rendering vector formats like SVG
@ -352,6 +366,10 @@ vips_foreign_load_magick_buffer_init( VipsForeignLoadMagickBuffer *buffer )
* The reader should also work with most versions of GraphicsMagick. See the
* "--with-magickpackage" configure option.
*
* The file format is usually guessed from the filename suffix. You can
* override this with @format -- for example `"ICO"` selects Windows icon
* format. See the ImageMagick documentation for a list of format names.
*
* Normally it will only load the first image in a many-image sequence (such
* as a GIF or a PDF). Use @page and @n to set the start page and number of
* pages to load. Set @n to -1 to load all pages from @page onwards.
@ -387,6 +405,7 @@ vips_magickload( const char *filename, VipsImage **out, ... )
*
* Optional arguments:
*
* * @format: string, format hint, eg. "JPG"
* * @page: %gint, load from this page
* * @n: %gint, load this many pages
* * @density: string, canvas resolution for rendering vector formats like SVG
@ -394,6 +413,12 @@ vips_magickload( const char *filename, VipsImage **out, ... )
* Read an image memory block using libMagick into a VIPS image. Exactly as
* vips_magickload(), but read from a memory source.
*
* The file format is usually guessed from the buffer contents, but this does
* not work for all image formats. You can
* set the format explicitly with @format -- for example `"ICO"` selects
* Windows icon
* format. See the ImageMagick documentation for a list of format names.
*
* You must not free the buffer while @out is active. The
* #VipsObject::postclose signal on @out is a good place to free.
*

View File

@ -55,6 +55,8 @@
#include <vips/buf.h>
#include <vips/internal.h>
#include "pforeign.h"
#ifdef HAVE_POPPLER
#include <cairo.h>
@ -91,7 +93,7 @@ typedef struct _VipsForeignLoadPdf {
*/
int n_pages;
/* We need to read out the side of each page we will render, and lay
/* We need to read out the size of each page we will render, and lay
* them out in the final image.
*/
VipsRect image;
@ -145,33 +147,6 @@ vips_foreign_load_pdf_get_flags( VipsForeignLoad *load )
return( VIPS_FOREIGN_PARTIAL );
}
static gboolean
vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len )
{
const guchar *str = (const guchar *) buf;
if( len >= 4 &&
str[0] == '%' &&
str[1] == 'P' &&
str[2] == 'D' &&
str[3] == 'F' )
return( 1 );
return( 0 );
}
static gboolean
vips_foreign_load_pdf_is_a( const char *filename )
{
unsigned char buf[4];
if( vips__get_bytes( filename, buf, 4 ) == 4 &&
vips_foreign_load_pdf_is_a_buffer( buf, 4 ) )
return( 1 );
return( 0 );
}
static int
vips_foreign_load_pdf_get_page( VipsForeignLoadPdf *pdf, int page_no )
{
@ -229,9 +204,10 @@ vips_foreign_load_pdf_set_image( VipsForeignLoadPdf *pdf, VipsImage *out )
*/
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
/* Extract and attach metadata.
/* Extract and attach metadata. Set the old name too for compat.
*/
vips_image_set_int( out, "pdf-n_pages", pdf->n_pages );
vips_image_set_int( out, VIPS_META_N_PAGES, pdf->n_pages );
for( i = 0; i < n_metadata; i++ ) {
VipsForeignLoadPdfMetadata *metadata =
@ -429,7 +405,7 @@ vips_foreign_load_pdf_load( VipsForeignLoad *load )
* (again) keep the number of calls to page_render low.
*/
if( vips_linecache( t[0], &t[1],
"tile_height", 5000,
"tile_height", VIPS_MIN( 5000, pdf->pages[0].height ),
NULL ) )
return( -1 );
if( vips_image_write( t[1], load->real ) )
@ -658,6 +634,37 @@ vips_foreign_load_pdf_buffer_init( VipsForeignLoadPdfBuffer *buffer )
#endif /*HAVE_POPPLER*/
/* Also used by the pdfium loader.
*/
gboolean
vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len )
{
const guchar *str = (const guchar *) buf;
if( len >= 4 &&
str[0] == '%' &&
str[1] == 'P' &&
str[2] == 'D' &&
str[3] == 'F' )
return( 1 );
return( 0 );
}
/* Also used by the pdfium loader.
*/
gboolean
vips_foreign_load_pdf_is_a( const char *filename )
{
unsigned char buf[4];
if( vips__get_bytes( filename, buf, 4 ) == 4 &&
vips_foreign_load_pdf_is_a_buffer( buf, 4 ) )
return( 1 );
return( 0 );
}
/**
* vips_pdfload:
* @filename: file to load
@ -678,18 +685,6 @@ vips_foreign_load_pdf_buffer_init( VipsForeignLoadPdfBuffer *buffer )
* converted. If you need CMYK bitmaps, you should use vips_magickload()
* instead.
*
* Rendering is progressive, that is, the image is rendered in strips equal in
* height to the tile height. If your PDF contains large image files and
* they span several strips in the output image, they will be decoded multiple
* times. To fix this, increase the the tile height, for example:
*
* |[
* vips copy huge.pdf x.png --vips-tile-height=1024
* ]|
*
* Will process images in 1024-pixel high strips, potentially much faster,
* though of course also using a lot more memory.
*
* Use @page to select a page to render, numbering from zero.
*
* Use @n to select the number of pages to render. The default is 1. Pages are

View File

@ -0,0 +1,651 @@
/* load PDF with PDFium
*
* 5/4/18
* - from pdfload.c
*/
/*
This file is part of VIPS.
VIPS is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
/* TODO
*
* - what about filename encodings
* - need to test on Windows
*/
/*
#define DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <vips/vips.h>
#include <vips/buf.h>
#include <vips/internal.h>
#include "pforeign.h"
#ifdef HAVE_PDFIUM
#include <fpdfview.h>
#include <fpdf_doc.h>
typedef struct _VipsForeignLoadPdf {
VipsForeignLoad parent_object;
/* Load this page.
*/
int page_no;
/* Load this many pages.
*/
int n;
/* Render at this DPI.
*/
double dpi;
/* Calculate this from DPI. At 72 DPI, we render 1:1.
*/
double scale;
FPDF_DOCUMENT *doc;
FPDF_PAGE *page;
int current_page;
/* Doc has this many pages.
*/
int n_pages;
/* We need to read out the size of each page we will render, and lay
* them out in the final image.
*/
VipsRect image;
VipsRect *pages;
} VipsForeignLoadPdf;
typedef VipsForeignLoadClass VipsForeignLoadPdfClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadPdf, vips_foreign_load_pdf,
VIPS_TYPE_FOREIGN_LOAD );
static char *vips_pdfium_errors[] = {
"no error",
"unknown error",
"file not found or could not be opened",
"file not in PDF format or corrupted",
"password required or incorrect password",
"unsupported security scheme",
"page not found or content error"
};
static void
vips_pdfium_error( void )
{
int err = FPDF_GetLastError();
if( err >= 0 &&
err < VIPS_NUMBER( vips_pdfium_errors ) )
vips_error( "pdfload", "%s", _( vips_pdfium_errors[err] ) );
else
vips_error( "pdfload", "%s", _( "unknown error" ) );
}
static void
vips_foreign_load_pdf_dispose( GObject *gobject )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) gobject;
VIPS_FREEF( FPDF_ClosePage, pdf->page );
VIPS_FREEF( FPDF_CloseDocument, pdf->doc );
G_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )->dispose( gobject );
}
static void *
vips_pdfium_init_cb( void *dummy )
{
FPDF_LIBRARY_CONFIG config;
config.version = 2;
config.m_pUserFontPaths = NULL;
config.m_pIsolate = NULL;
config.m_v8EmbedderSlot = 0;
FPDF_InitLibraryWithConfig( &config );
return( NULL );
}
static int
vips_foreign_load_pdf_build( VipsObject *object )
{
static GOnce once = G_ONCE_INIT;
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) object;
VIPS_ONCE( &once, vips_pdfium_init_cb, NULL );
if( !vips_object_argument_isset( object, "scale" ) )
pdf->scale = pdf->dpi / 72.0;
if( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static VipsForeignFlags
vips_foreign_load_pdf_get_flags_filename( const char *filename )
{
/* We can't render any part of the page on demand, but we can render
* separate pages. Might as well call ourselves partial.
*/
return( VIPS_FOREIGN_PARTIAL );
}
static VipsForeignFlags
vips_foreign_load_pdf_get_flags( VipsForeignLoad *load )
{
return( VIPS_FOREIGN_PARTIAL );
}
static int
vips_foreign_load_pdf_get_page( VipsForeignLoadPdf *pdf, int page_no )
{
if( pdf->current_page != page_no ) {
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( pdf );
VIPS_FREEF( FPDF_ClosePage, pdf->page );
pdf->current_page = -1;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_get_page: %d\n", page_no );
#endif /*DEBUG*/
if( !(pdf->page = FPDF_LoadPage( pdf->doc, page_no )) ) {
vips_pdfium_error();
vips_error( class->nickname,
_( "unable to load page %d" ), page_no );
return( -1 );
}
pdf->current_page = page_no;
}
return( 0 );
}
/* String-based metadata fields we extract.
*/
typedef struct _VipsForeignLoadPdfMetadata {
char *tag; /* as understood by PDFium */
char *field; /* as understood by libvips */
} VipsForeignLoadPdfMetadata;
static VipsForeignLoadPdfMetadata vips_foreign_load_pdf_metadata[] = {
{ "Title", "pdf-title" },
{ "Author", "pdf-author" },
{ "Subject", "pdf-subject" },
{ "Keywords", "pdf-keywords" },
{ "Creator", "pdf-creator" },
{ "Producer", "pdf-producer" },
/* poppler has "metadata" as well, but pdfium does not support this */
};
static int n_metadata = VIPS_NUMBER( vips_foreign_load_pdf_metadata );
static int
vips_foreign_load_pdf_set_image( VipsForeignLoadPdf *pdf, VipsImage *out )
{
int i;
double res;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_set_image: %p\n", pdf );
#endif /*DEBUG*/
/* We render to a linecache, so fat strips work well.
*/
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
/* Extract and attach metadata. Set the old name too for compat.
*/
vips_image_set_int( out, "pdf-n_pages", pdf->n_pages );
vips_image_set_int( out, VIPS_META_N_PAGES, pdf->n_pages );
for( i = 0; i < n_metadata; i++ ) {
VipsForeignLoadPdfMetadata *metadata =
&vips_foreign_load_pdf_metadata[i];
char text[1024];
int len;
len = FPDF_GetMetaText( pdf->doc, metadata->tag, text, 1024 );
if( len > 0 ) {
char *str;
/* Silently ignore coding errors.
*/
if( (str = g_utf16_to_utf8( (gunichar2 *) text, len,
NULL, NULL, NULL )) ) {
vips_image_set_string( out,
metadata->field, str );
g_free( str );
}
}
}
/* We need pixels/mm for vips.
*/
res = pdf->dpi / 25.4;
vips_image_init_fields( out,
pdf->image.width, pdf->image.height,
4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, res, res );
return( 0 );
}
static int
vips_foreign_load_pdf_header( VipsForeignLoad *load )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
int top;
int i;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_header: %p\n", pdf );
#endif /*DEBUG*/
pdf->n_pages = FPDF_GetPageCount( pdf->doc );
/* @n == -1 means until the end of the doc.
*/
if( pdf->n == -1 )
pdf->n = pdf->n_pages - pdf->page_no;
if( pdf->page_no + pdf->n > pdf->n_pages ||
pdf->page_no < 0 ||
pdf->n <= 0 ) {
vips_error( class->nickname, "%s", _( "pages out of range" ) );
return( -1 );
}
/* Lay out the pages in our output image.
*/
if( !(pdf->pages = VIPS_ARRAY( pdf, pdf->n, VipsRect )) )
return( -1 );
top = 0;
pdf->image.left = 0;
pdf->image.top = 0;
pdf->image.width = 0;
pdf->image.height = 0;
for( i = 0; i < pdf->n; i++ ) {
if( vips_foreign_load_pdf_get_page( pdf, pdf->page_no + i ) )
return( -1 );
pdf->pages[i].left = 0;
pdf->pages[i].top = top;
pdf->pages[i].width =
FPDF_GetPageWidth( pdf->page ) * pdf->scale;
pdf->pages[i].height =
FPDF_GetPageHeight( pdf->page ) * pdf->scale;
if( pdf->pages[i].width > pdf->image.width )
pdf->image.width = pdf->pages[i].width;
pdf->image.height += pdf->pages[i].height;
top += pdf->pages[i].height;
}
/* If all pages are the same size, we can tag this as a toilet roll
* image and tiffsave will be able to save it as a multipage tiff.
*/
for( i = 1; i < pdf->n; i++ )
if( pdf->pages[i].width != pdf->pages[0].width ||
pdf->pages[i].height != pdf->pages[0].height )
break;
if( i == pdf->n )
vips_image_set_int( load->out,
VIPS_META_PAGE_HEIGHT, pdf->pages[0].height );
vips_foreign_load_pdf_set_image( pdf, load->out );
return( 0 );
}
static int
vips_foreign_load_pdf_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) a;
VipsRect *r = &or->valid;
int top;
int i;
int y;
/*
printf( "vips_foreign_load_pdf_generate: "
"left = %d, top = %d, width = %d, height = %d\n",
r->left, r->top, r->width, r->height );
*/
/* Poppler won't always paint the background. Use 255 (white) for the
* bg, PDFs generally assume a paper backgrocund colour.
*/
vips_region_paint( or, r, 255 );
/* Search through the pages we are drawing for the first containing
* this rect. This could be quicker, perhaps a binary search, but who
* cares.
*/
for( i = 0; i < pdf->n; i++ )
if( VIPS_RECT_BOTTOM( &pdf->pages[i] ) > r->top )
break;
top = r->top;
while( top < VIPS_RECT_BOTTOM( r ) ) {
VipsRect rect;
FPDF_BITMAP bitmap;
vips_rect_intersectrect( r, &pdf->pages[i], &rect );
if( vips_foreign_load_pdf_get_page( pdf, pdf->page_no + i ) )
return( -1 );
/* 4 means RGBA.
*/
bitmap = FPDFBitmap_CreateEx( rect.width, rect.height, 4,
VIPS_REGION_ADDR( or, rect.left, rect.top ),
VIPS_REGION_LSKIP( or ) );
FPDF_RenderPageBitmap( bitmap, pdf->page,
0, 0, rect.width, rect.height,
0, 0 );
FPDFBitmap_Destroy( bitmap );
top += rect.height;
i += 1;
}
/* PDFium writes BRGA, we must swap.
*
* FIXME ... this is a bit slow.
*/
for( y = 0; y < r->height; y++ ) {
VipsPel *p;
int x;
p = VIPS_REGION_ADDR( or, r->left, r->top + y );
for( x = 0; x < r->width; x++ ) {
VIPS_SWAP( VipsPel, p[0], p[2] );
p += 4;
}
}
return( 0 );
}
static int
vips_foreign_load_pdf_load( VipsForeignLoad *load )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
VipsImage **t = (VipsImage **)
vips_object_local_array( (VipsObject *) load, 2 );
#ifdef DEBUG
printf( "vips_foreign_load_pdf_load: %p\n", pdf );
#endif /*DEBUG*/
/* Read to this image, then cache to out, see below.
*/
t[0] = vips_image_new();
vips_foreign_load_pdf_set_image( pdf, t[0] );
if( vips_image_generate( t[0],
NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) )
return( -1 );
/* PDFium does not like rendering parts of pages :-( always render
* complete ones.
*/
if( vips_linecache( t[0], &t[1],
"tile_height", pdf->pages[0].height,
NULL ) )
return( -1 );
if( vips_image_write( t[1], load->real ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_load_pdf_class_init( VipsForeignLoadPdfClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->dispose = vips_foreign_load_pdf_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "pdfload_base";
object_class->description = _( "load PDF with libpoppler" );
object_class->build = vips_foreign_load_pdf_build;
load_class->get_flags_filename =
vips_foreign_load_pdf_get_flags_filename;
load_class->get_flags = vips_foreign_load_pdf_get_flags;
load_class->load = vips_foreign_load_pdf_load;
VIPS_ARG_INT( class, "page", 10,
_( "Page" ),
_( "Load this page from the file" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, page_no ),
0, 100000, 0 );
VIPS_ARG_INT( class, "n", 11,
_( "n" ),
_( "Load this many pages" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, n ),
-1, 100000, 1 );
VIPS_ARG_DOUBLE( class, "dpi", 12,
_( "DPI" ),
_( "Render at this DPI" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, dpi ),
0.001, 100000.0, 72.0 );
VIPS_ARG_DOUBLE( class, "scale", 13,
_( "Scale" ),
_( "Scale output by this factor" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, scale ),
0.001, 100000.0, 1.0 );
}
static void
vips_foreign_load_pdf_init( VipsForeignLoadPdf *pdf )
{
pdf->dpi = 72.0;
pdf->scale = 1.0;
pdf->n = 1;
pdf->current_page = -1;
}
typedef struct _VipsForeignLoadPdfFile {
VipsForeignLoadPdf parent_object;
/* Filename for load.
*/
char *filename;
} VipsForeignLoadPdfFile;
typedef VipsForeignLoadPdfClass VipsForeignLoadPdfFileClass;
G_DEFINE_TYPE( VipsForeignLoadPdfFile, vips_foreign_load_pdf_file,
vips_foreign_load_pdf_get_type() );
static int
vips_foreign_load_pdf_file_header( VipsForeignLoad *load )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) load;
if( !(pdf->doc = FPDF_LoadDocument( file->filename, NULL )) ) {
vips_pdfium_error();
vips_error( "pdfload",
_( "unable to load \"%s\"" ), file->filename );
return( -1 );
}
VIPS_SETSTR( load->out->filename, file->filename );
return( vips_foreign_load_pdf_header( load ) );
}
static const char *vips_foreign_pdf_suffs[] = {
".pdf",
NULL
};
static void
vips_foreign_load_pdf_file_class_init(
VipsForeignLoadPdfFileClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "pdfload";
foreign_class->suffs = vips_foreign_pdf_suffs;
load_class->is_a = vips_foreign_load_pdf_is_a;
load_class->header = vips_foreign_load_pdf_file_header;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "Filename to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdfFile, filename ),
NULL );
}
static void
vips_foreign_load_pdf_file_init( VipsForeignLoadPdfFile *file )
{
}
typedef struct _VipsForeignLoadPdfBuffer {
VipsForeignLoadPdf parent_object;
/* Load from a buffer.
*/
VipsArea *buf;
} VipsForeignLoadPdfBuffer;
typedef VipsForeignLoadPdfClass VipsForeignLoadPdfBufferClass;
G_DEFINE_TYPE( VipsForeignLoadPdfBuffer, vips_foreign_load_pdf_buffer,
vips_foreign_load_pdf_get_type() );
static int
vips_foreign_load_pdf_buffer_header( VipsForeignLoad *load )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
VipsForeignLoadPdfBuffer *buffer =
(VipsForeignLoadPdfBuffer *) load;
if( !(pdf->doc = FPDF_LoadMemDocument( buffer->buf->data,
buffer->buf->length, NULL )) ) {
vips_pdfium_error();
vips_error( "pdfload",
"%s", _( "unable to load from buffer" ) );
return( -1 );
}
return( vips_foreign_load_pdf_header( load ) );
}
static void
vips_foreign_load_pdf_buffer_class_init(
VipsForeignLoadPdfBufferClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "pdfload_buffer";
load_class->is_a_buffer = vips_foreign_load_pdf_is_a_buffer;
load_class->header = vips_foreign_load_pdf_buffer_header;
VIPS_ARG_BOXED( class, "buffer", 1,
_( "Buffer" ),
_( "Buffer to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdfBuffer, buf ),
VIPS_TYPE_BLOB );
}
static void
vips_foreign_load_pdf_buffer_init( VipsForeignLoadPdfBuffer *buffer )
{
}
#endif /*HAVE_PDFIUM*/

View File

@ -123,14 +123,18 @@ int vips__fits_read( const char *filename, VipsImage *out );
int vips__fits_write( VipsImage *in, const char *filename );
int vips__magick_read( const char *filename,
VipsImage *out, const char *density, int page, int n );
VipsImage *out, const char *format, const char *density,
int page, int n );
int vips__magick_read_header( const char *filename,
VipsImage *out, const char *density, int page, int n );
VipsImage *out, const char *format, const char *density,
int page, int n );
int vips__magick_read_buffer( const void *buf, const size_t len,
VipsImage *out, const char *density, int page, int n );
VipsImage *out, const char *format, const char *density,
int page, int n );
int vips__magick_read_buffer_header( const void *buf, const size_t len,
VipsImage *out, const char *density, int page, int n );
VipsImage *out, const char *format, const char *density,
int page, int n );
extern const char *vips__mat_suffs[];
@ -162,12 +166,14 @@ int vips__jpeg_write_file( VipsImage *in,
const char *filename, int Q, const char *profile,
gboolean optimize_coding, gboolean progressive, gboolean strip,
gboolean no_subsample, gboolean trellis_quant,
gboolean overshoot_deringing, gboolean optimize_scans, int quant_table );
gboolean overshoot_deringing, gboolean optimize_scans,
int quant_table );
int vips__jpeg_write_buffer( VipsImage *in,
void **obuf, size_t *olen, int Q, const char *profile,
gboolean optimize_coding, gboolean progressive, gboolean strip,
gboolean no_subsample, gboolean trellis_quant,
gboolean overshoot_deringing, gboolean optimize_scans, int quant_table );
gboolean overshoot_deringing, gboolean optimize_scans,
int quant_table );
int vips__isjpeg_buffer( const void *buf, size_t len );
int vips__isjpeg( const char *filename );
@ -239,6 +245,9 @@ int vips__openslide_read( const char *filename, VipsImage *out,
int vips__openslide_read_associated( const char *filename, VipsImage *out,
const char *associated );
gboolean vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len );
gboolean vips_foreign_load_pdf_is_a( const char *filename );
#ifdef __cplusplus
}
#endif /*__cplusplus*/

View File

@ -19,6 +19,8 @@
* - add buffer save functions
* 28/2/17
* - use dbuf for buffer output
* 4/4/17
* - reduce stack use to help musl
*/
/*
@ -885,10 +887,12 @@ rle_scanline_write( COLR *scanline, int width,
}
}
/* Write a single scanline.
/* Write a single scanline. buffer is at least MAX_LINE bytes and is used to
* construct the RLE scanline. Don't allocate this on the stack so we don't
* die too horribly on small-stack libc.
*/
static int
scanline_write( COLR *scanline, int width, FILE *fp )
scanline_write( unsigned char *buffer, COLR *scanline, int width, FILE *fp )
{
if( width < MINELEN ||
width > MAXELEN )
@ -898,11 +902,12 @@ scanline_write( COLR *scanline, int width, FILE *fp )
else {
/* An RLE scanline.
*/
unsigned char buffer[MAX_LINE];
int length;
rle_scanline_write( scanline, width, buffer, &length );
g_assert( length <= MAX_LINE );
return( fwrite( buffer, 1, length, fp ) - length );
}
}
@ -1290,12 +1295,23 @@ static int
vips2rad_put_data_block( VipsRegion *region, VipsRect *area, void *a )
{
Write *write = (Write *) a;
size_t size;
unsigned char *buffer;
int i;
/* You have to seek back after a write.
*/
buffer = vips_dbuf_get_write( &write->dbuf, &size );
vips_dbuf_seek( &write->dbuf, 0, SEEK_SET );
g_assert( size >= MAX_LINE );
for( i = 0; i < area->height; i++ ) {
VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + i );
if( scanline_write( (COLR *) p, area->width, write->fout ) )
if( scanline_write( buffer,
(COLR *) p, area->width, write->fout ) )
return( -1 );
}
@ -1329,6 +1345,11 @@ vips__rad_save( VipsImage *in, const char *filename )
write->filename = vips_strdup( NULL, filename );
write->fout = vips__file_open_write( filename, FALSE );
/* scanline_write() needs a buffer to write compressed scanlines to.
* We use the dbuf ... why not.
*/
vips_dbuf_allocate( &write->dbuf, MAX_LINE );
if( !write->filename ||
!write->fout ||
vips2rad_put_header( write ) ||

View File

@ -177,6 +177,8 @@
* - remove missing res warning
* 19/5/17
* - page > 0 could break edge tiles or strips
* 26/4/18
* - add n-pages metadata item
*/
/*
@ -283,6 +285,10 @@ typedef struct _Rtiff {
*/
TIFF *tiff;
/* Number of pages (directories) in image.
*/
int n_pages;
/* The current page we have set.
*/
int current_page;
@ -1230,6 +1236,8 @@ rtiff_set_header( Rtiff *rtiff, VipsImage *out )
vips_image_set_int( out,
VIPS_META_PAGE_HEIGHT, rtiff->header.height );
vips_image_set_int( out, VIPS_META_N_PAGES, rtiff->n_pages );
/* Even though we could end up serving tiled data, always hint
* THINSTRIP, since we're quite happy doing that too, and it could need
* a lot less memory.
@ -1984,6 +1992,7 @@ rtiff_new( VipsImage *out, int page, int n, gboolean autorotate )
rtiff->n = n;
rtiff->autorotate = autorotate;
rtiff->tiff = NULL;
rtiff->n_pages = 0;
rtiff->current_page = -1;
rtiff->sfn = NULL;
rtiff->client = NULL;
@ -2167,8 +2176,9 @@ rtiff_header_read_all( Rtiff *rtiff )
/* -1 means "to the end".
*/
rtiff->n_pages = rtiff_n_pages( rtiff );
if( rtiff->n == -1 )
rtiff->n = rtiff_n_pages( rtiff ) - rtiff->page;
rtiff->n = rtiff->n_pages - rtiff->page;
/* If we're to read many pages, verify that they are all identical.
*/

View File

@ -65,6 +65,8 @@
* - better behaviour for truncated png files, thanks Yury
* 26/4/17
* - better @fail handling with truncated PNGs
* 9/4/18
* - set interlaced=1 for interlaced images
*/
/*
@ -438,6 +440,11 @@ png2vips_header( Read *read, VipsImage *out )
return( -1 );
}
/* Let our caller know. These are very expensive to decode.
*/
if( interlace_type != PNG_INTERLACE_NONE )
vips_image_set_int( out, "interlaced", 1 );
return( 0 );
}
@ -992,7 +999,8 @@ write_vips( Write *write,
/* If we're an intel byte order CPU and this is a 16bit image, we need
* to swap bytes.
*/
if( bit_depth > 8 && !vips_amiMSBfirst() )
if( bit_depth > 8 &&
!vips_amiMSBfirst() )
png_set_swap( write->pPng );
if( interlace )

View File

@ -177,6 +177,8 @@ int vips_icc_export( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));
int vips_icc_ac2rc( VipsImage *in, VipsImage **out,
const char *profile_filename );
gboolean vips_icc_is_compatible_profile( VipsImage *image,
void *data, size_t data_length );
int vips_dE76( VipsImage *left, VipsImage *right, VipsImage **out, ... )
__attribute__((sentinel));

View File

@ -5,6 +5,32 @@
#define VIPS_ENUM_TYPES_H
G_BEGIN_DECLS
/* enumerations from "../../../libvips/include/vips/resample.h" */
GType vips_kernel_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_KERNEL (vips_kernel_get_type())
GType vips_size_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_SIZE (vips_size_get_type())
/* enumerations from "../../../libvips/include/vips/foreign.h" */
GType vips_foreign_flags_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_FOREIGN_FLAGS (vips_foreign_flags_get_type())
GType vips_saveable_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_SAVEABLE (vips_saveable_get_type())
GType vips_foreign_webp_preset_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_FOREIGN_WEBP_PRESET (vips_foreign_webp_preset_get_type())
GType vips_foreign_tiff_compression_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_FOREIGN_TIFF_COMPRESSION (vips_foreign_tiff_compression_get_type())
GType vips_foreign_tiff_predictor_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_FOREIGN_TIFF_PREDICTOR (vips_foreign_tiff_predictor_get_type())
GType vips_foreign_tiff_resunit_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_FOREIGN_TIFF_RESUNIT (vips_foreign_tiff_resunit_get_type())
GType vips_foreign_png_filter_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_FOREIGN_PNG_FILTER (vips_foreign_png_filter_get_type())
GType vips_foreign_dz_layout_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_FOREIGN_DZ_LAYOUT (vips_foreign_dz_layout_get_type())
GType vips_foreign_dz_depth_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_FOREIGN_DZ_DEPTH (vips_foreign_dz_depth_get_type())
GType vips_foreign_dz_container_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_FOREIGN_DZ_CONTAINER (vips_foreign_dz_container_get_type())
/* enumerations from "../../../libvips/include/vips/arithmetic.h" */
GType vips_operation_math_get_type (void) G_GNUC_CONST;
#define VIPS_TYPE_OPERATION_MATH (vips_operation_math_get_type())

View File

@ -143,6 +143,13 @@ extern "C" {
*/
#define VIPS_META_PAGE_HEIGHT "page-height"
/**
* VIPS_META_N_PAGES:
*
* If set, the number of pages in the original file.
*/
#define VIPS_META_N_PAGES "n-pages"
guint64 vips_format_sizeof( VipsBandFormat format );
guint64 vips_format_sizeof_unsafe( VipsBandFormat format );

View File

@ -12,8 +12,10 @@
* - vips_image_write() does not ref input for non-partial images
* 29/10/16
* - add vips_image_hasalpha()
* 11/10/1
* 11/10/17
* - more severing for vips_image_write()
* 3/4/18
* - better rules for hasalpha
*/
/*
@ -2449,9 +2451,6 @@ vips_get_disc_threshold( void )
* will default to /tmp. On Windows, vips uses GetTempPath() to find the
* temporary directory.
*
* vips uses g_mkstemp() to make the temporary filename. They generally look
* something like "vips-12-EJKJFGH.v".
*
* See also: vips_image_new().
*
* Returns: the new #VipsImage, or %NULL on error.
@ -2658,7 +2657,7 @@ vips_image_write_to_buffer( VipsImage *in,
*
* Writes @in to memory as a simple, unformatted C-style array.
*
* The caller is responsible for freeing this memory.
* The caller is responsible for freeing this memory with g_free().
*
* See also: vips_image_write_to_buffer().
*
@ -2866,18 +2865,47 @@ vips_image_ispartial( VipsImage *image )
* vips_image_hasalpha: (method)
* @image: image to check
*
* libvips assumes an image has an alpha if it has two bands (ie. it is a
* monochrome image with an extra band), if it has four bands (unless it's been
* tagged as CMYK), or if it has more than four bands.
* Look at an image's interpretation and see if it has extra alpha bands. For
* example, a 4-band #VIPS_INTERPRETATION_RGB would, but a six-band
* #VIPS_INTERPRETATION_MULTIBAND would not.
*
* Return %TRUE if @image has an alpha channel.
*/
gboolean
vips_image_hasalpha( VipsImage *image )
{
return( image->Bands == 2 ||
(image->Bands == 4 && image->Type != VIPS_INTERPRETATION_CMYK) ||
image->Bands > 4 );
/* The result of hasalpha is used to turn on things like
* premultiplication, so we are rather conservative about when we
* signal this. We don't want to premultiply things that should not be
* premultiplied.
*/
switch( image->Type ) {
case VIPS_INTERPRETATION_B_W:
case VIPS_INTERPRETATION_GREY16:
return( image->Bands > 1 );
case VIPS_INTERPRETATION_RGB:
case VIPS_INTERPRETATION_CMC:
case VIPS_INTERPRETATION_LCH:
case VIPS_INTERPRETATION_LABS:
case VIPS_INTERPRETATION_sRGB:
case VIPS_INTERPRETATION_YXY:
case VIPS_INTERPRETATION_XYZ:
case VIPS_INTERPRETATION_LAB:
case VIPS_INTERPRETATION_RGB16:
case VIPS_INTERPRETATION_scRGB:
case VIPS_INTERPRETATION_HSV:
return( image->Bands > 3 );
case VIPS_INTERPRETATION_CMYK:
return( image->Bands > 4 );
default:
/* We can't really infer anything about bands from things like
* HISTOGRAM or FOURIER.
*/
return( FALSE );
}
}
/**

View File

@ -1118,9 +1118,9 @@ vips__ftruncate( int fd, gint64 pos )
return( 0 );
}
/* Test for file exists.
/* TRUE if file exists.
*/
int
gboolean
vips_existsf( const char *name, ... )
{
va_list ap;
@ -1135,7 +1135,12 @@ vips_existsf( const char *name, ... )
g_free( path );
return( !result );
/* access() can fail for various reasons, especially under things
* like selinux. Only return FALSE if we are certain the file does not
* exist.
*/
return( result == 0 ||
errno != ENOENT );
}
#ifdef OS_WIN32
@ -1624,32 +1629,30 @@ vips__temp_dir( void )
/* Make a temporary file name. The format parameter is something like "%s.jpg"
* and will be expanded to something like "/tmp/vips-12-34587.jpg".
*
* You need to free the result. A real file will also be created, though we
* delete it for you.
* You need to free the result.
*/
char *
vips__temp_name( const char *format )
{
static int serial = 1;
static int serial = 0;
char file[FILENAME_MAX];
char file2[FILENAME_MAX];
char *name;
int fd;
vips_snprintf( file, FILENAME_MAX, "vips-%d-XXXXXX", serial++ );
vips_snprintf( file, FILENAME_MAX, "vips-%d-%u",
serial++, g_random_int() );
vips_snprintf( file2, FILENAME_MAX, format, file );
name = g_build_filename( vips__temp_dir(), file2, NULL );
if( (fd = g_mkstemp( name )) == -1 ) {
vips_error( "tempfile",
_( "unable to make temporary file %s" ), name );
g_free( name );
return( NULL );
}
close( fd );
g_unlink( name );
/* We could use something like g_mkstemp() to guarantee uniqueness
* across processes, but the extra FS calls can be difficult for
* selinux.
*
* g_random_int() should be safe enough -- it's seeded from time(), so
* it ought not to collide often -- and on linux at least we never
* actually use these filenames in the filesystem anyway.
*/
return( name );
}

View File

@ -21,6 +21,8 @@
* - use expat for xml read, printf for xml write
* 16/8/17
* - validate strs as being utf-8 before we write
* 9/4/18 Alexander--
* - use O_TMPFILE, if available
*/
/*
@ -55,6 +57,10 @@
#define DEBUG
*/
/* Enable linux extensions like O_TMPFILE, if available.
*/
#define _GNU_SOURCE
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
@ -69,12 +75,12 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h>
#endif /*HAVE_SYS_FILE_H*/
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /*HAVE_UNISTD_H*/
@ -181,19 +187,37 @@ vips__open_image_write( const char *filename, gboolean temp )
flags = MODE_WRITE;
#ifdef O_TMPFILE
/* Linux-only extension creates an unlinked file. CREAT and TRUNC must
* be clear. The filename arg to open() must name a directory.
*/
if( temp ) {
char *dirname;
flags |= O_TMPFILE;
flags &= ~O_CREAT;
flags &= ~O_TRUNC;
dirname = g_path_get_dirname( filename );
fd = vips_tracked_open( dirname, flags, 0666 );
g_free( dirname );
}
else
fd = vips_tracked_open( filename, flags, 0666 );
#else /*!O_TMPFILE*/
#ifdef _O_TEMPORARY
/* On Windows, setting O_TEMP gets the file automatically
/* On Windows, setting _O_TEMPORARY gets the file automatically
* deleted on process exit, even if the processes crashes. See
* vips_image_rewind() for what we do to help on *nix.
*/
if( temp )
flags |= _O_TEMPORARY;
#endif /*_O_TEMPORARY*/
#endif /*O_TMPFILE*/
if( (fd = vips_tracked_open( filename, flags, 0666 )) < 0 ) {
if( fd < 0 ) {
vips_error_system( errno, "VipsImage",
_( "unable to write to \"%s\"" ),
filename );
_( "unable to write to \"%s\"" ), filename );
return( -1 );
}

143
m4/pdfium.m4 Normal file
View File

@ -0,0 +1,143 @@
dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding
dnl
dnl FIND_PDFIUM[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]
dnl ---------------------------------------------------
dnl
dnl Find pdfium libraries and headers
dnl
dnl Put -I stuff in PDFIUM_INCLUDES
dnl Put PDFium objects in PDFIUM_LIBS (add this to the link line untouched!)
dnl Define HAVE_PDFIUM if found
dnl
AC_DEFUN([FIND_PDFIUM], [
AC_REQUIRE([AC_PATH_XTRA])
ZLIB_INCLUDES=""
ZLIB_LIBS=""
# PDFium has a set of object archives that must be linked firectly into your
# app, and a couple of true libraries. The objects must be linked in this
# order.
pdfium_objects="\
libpdfium.a \
libfpdfapi.a \
libfxge.a \
libfpdfdoc.a \
libfxcrt.a \
libfx_agg.a \
libfxcodec.a \
libfx_lpng.a \
libfx_libopenjpeg.a \
libfx_lcms2.a \
libfx_freetype.a \
libjpeg.a \
libfdrm.a \
libpwl.a \
libbigint.a \
libformfiller.a"
AC_ARG_WITH(pdfium,
AS_HELP_STRING([--without-pdfium], [build without pdfium (default: test)]))
# Treat --without-pdfium like --without-pdfium-includes
# --without-pdfium-libraries
if test "$with_pdfium" = "no"; then
PDFIUM_INCLUDES=no
PDFIUM_LIBS=no
fi
AC_ARG_WITH(pdfium-includes,
AS_HELP_STRING([--with-pdfium-includes=DIR], [pdfium includes are in DIR]),
PDFIUM_INCLUDES="-I$withval")
AC_ARG_WITH(pdfium-libraries,
AS_HELP_STRING([--with-pdfium-libraries=DIR], [pdfium libraries are in DIR]),
PDFIUM_LIBS="$withval")
AC_MSG_CHECKING(for PDFIUM)
# Look for fpdfview.h ... this is a documented header, so it should be a good
# target
#
# it won't be in the standard search path, but try $PREFIX
if test "$PDFIUM_INCLUDES" = ""; then
pdfium_save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="-I${prefix}/include $CPPFLAGS"
AC_TRY_COMPILE([#include <fpdfview.h>],[int a;],[
PDFIUM_INCLUDES="-I${prefix}/include"
], [
PDFIUM_INCLUDES="no"
]
)
CPPFLAGS="$pdfium_save_CPPFLAGS"
fi
# Now for the libraries ... if there's nothing set, try $PREFIX/lib
if test "$PDFIUM_LIBS" = ""; then
pdfium_save_LIBS="$LIBS"
pdfium_save_CPPFLAGS="$CPPFLAGS"
LIBS=""
for i in $pdfium_objects; do
LIBS="$LIBS $prefix/lib/pdfium-obj/$i"
done
LIBS="$LIBS -L$prefix/lib -lm -lpthread"
CPPFLAGS="$PDFIUM_INCLUDES $CPPFLAGS"
AC_TRY_LINK([#include <fpdfview.h>],
[FPDF_DOCUMENT doc; doc = FPDF_LoadDocument("", "")], [
PDFIUM_LIBS="${prefix}/lib"
], [
PDFIUM_LIBS=no
]
)
LIBS="$pdfium_save_LIBS"
CPPFLAGS="$pdfium_save_CPPFLAGS"
fi
# Print a helpful message
pdfium_libraries_result="$PDFIUM_LIBS"
pdfium_includes_result="$PDFIUM_INCLUDES"
if test x"$pdfium_libraries_result" = x""; then
pdfium_libraries_result="in default path"
fi
if test x"$pdfium_includes_result" = x""; then
pdfium_includes_result="in default path"
fi
if test "$pdfium_libraries_result" = "no"; then
pdfium_libraries_result="(none)"
fi
if test "$pdfium_includes_result" = "no"; then
pdfium_includes_result="(none)"
fi
AC_MSG_RESULT([libraries $pdfium_libraries_result, headers $pdfium_includes_result])
if test x"$PDFIUM_LIBS" != x"no"; then
dir="$PDFIUM_LIBS"
PDFIUM_LIBS=""
for i in $pdfium_objects; do
PDFIUM_LIBS="$PDFIUM_LIBS $prefix/lib/pdfium-obj/$i"
done
PDFIUM_LIBS="$PDFIUM_LIBS -L$dir -lm -lpthread"
fi
AC_SUBST(PDFIUM_LIBS)
AC_SUBST(PDFIUM_INCLUDES)
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test "$PDFIUM_INCLUDES" != "no" && test "$PDFIUM_LIBS" != "no"; then
AC_DEFINE(HAVE_PDFIUM,1,
[Define if you have pdfium libraries and header files.])
$1
else
PDFIUM_INCLUDES=""
PDFIUM_LIBS=""
$2
fi
])dnl