diff --git a/.travis.yml b/.travis.yml
index 29520296..4b6b0750 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -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
diff --git a/ChangeLog b/ChangeLog
index 2f015aed..bc8aefff 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/README.md b/README.md
index 111f9a07..a586fa75 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/configure.ac b/configure.ac
index cb71bb7e..5fdb7f50 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/doc/using-cpp.xml b/doc/using-cpp.xml
index a5fb9d39..be1cbeae 100644
--- a/doc/using-cpp.xml
+++ b/doc/using-cpp.xml
@@ -26,7 +26,7 @@
/* 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 <vips/vips8>
@@ -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, &argc, &argv, &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 ()->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 ()->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);
}
Everything before VImage in = VImage::..
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.
- This line is the C++ equivalent of vips_image_new_from_file(). It works
+ VImage in = VImage::..
is the C++ equivalent of
+ vips_image_new_from_file(). It works
in the same way, the differences being:
diff --git a/libvips/colour/icc_transform.c b/libvips/colour/icc_transform.c
index 7b9d0d53..67053214 100644
--- a/libvips/colour/icc_transform.c
+++ b/libvips/colour/icc_transform.c
@@ -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
@@ -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*/
/**
diff --git a/libvips/conversion/bandjoin.c b/libvips/conversion/bandjoin.c
index c2ce0505..7f284907 100644
--- a/libvips/conversion/bandjoin.c
+++ b/libvips/conversion/bandjoin.c
@@ -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.
diff --git a/libvips/create/black.c b/libvips/create/black.c
index c613a154..33c4ab1c 100644
--- a/libvips/create/black.c
+++ b/libvips/create/black.c
@@ -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 );
diff --git a/libvips/create/gaussmat.c b/libvips/create/gaussmat.c
index fa67c305..4ac21562 100644
--- a/libvips/create/gaussmat.c
+++ b/libvips/create/gaussmat.c
@@ -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 );
diff --git a/libvips/create/gaussnoise.c b/libvips/create/gaussnoise.c
index 1566e39a..5ccf5510 100644
--- a/libvips/create/gaussnoise.c
+++ b/libvips/create/gaussnoise.c
@@ -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 );
diff --git a/libvips/create/logmat.c b/libvips/create/logmat.c
index 5f15b3ea..43d753c7 100644
--- a/libvips/create/logmat.c
+++ b/libvips/create/logmat.c
@@ -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 );
diff --git a/libvips/create/perlin.c b/libvips/create/perlin.c
index 5181f365..a6651f90 100644
--- a/libvips/create/perlin.c
+++ b/libvips/create/perlin.c
@@ -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 );
diff --git a/libvips/create/point.c b/libvips/create/point.c
index 06e14229..f5f00841 100644
--- a/libvips/create/point.c
+++ b/libvips/create/point.c
@@ -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" ),
diff --git a/libvips/create/text.c b/libvips/create/text.c
index 8d26ed10..c0b14694 100644
--- a/libvips/create/text.c
+++ b/libvips/create/text.c
@@ -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().
diff --git a/libvips/create/worley.c b/libvips/create/worley.c
index 520fdd47..e2cdf424 100644
--- a/libvips/create/worley.c
+++ b/libvips/create/worley.c
@@ -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 );
diff --git a/libvips/deprecated/im_tiff2vips.c b/libvips/deprecated/im_tiff2vips.c
index d94019e9..a5ded395 100644
--- a/libvips/deprecated/im_tiff2vips.c
+++ b/libvips/deprecated/im_tiff2vips.c
@@ -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 ) &&
diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am
index e49c6e4d..2f17c1aa 100644
--- a/libvips/foreign/Makefile.am
+++ b/libvips/foreign/Makefile.am
@@ -6,6 +6,7 @@ libforeign_la_SOURCES = \
gifload.c \
cairo.c \
pdfload.c \
+ pdfload_pdfium.c \
svgload.c \
radiance.c \
radload.c \
diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c
index 61c87e03..2cc90e50 100644
--- a/libvips/foreign/foreign.c
+++ b/libvips/foreign/foreign.c
@@ -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();
diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c
index ceed5918..d67239e9 100644
--- a/libvips/foreign/jpeg2vips.c
+++ b/libvips/foreign/jpeg2vips.c
@@ -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 ) {
diff --git a/libvips/foreign/jpegload.c b/libvips/foreign/jpegload.c
index 06ec6c81..607e54f8 100644
--- a/libvips/foreign/jpegload.c
+++ b/libvips/foreign/jpegload.c
@@ -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().
*
diff --git a/libvips/foreign/magick.c b/libvips/foreign/magick.c
index fde689f0..500132b1 100644
--- a/libvips/foreign/magick.c
+++ b/libvips/foreign/magick.c
@@ -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( "" );
diff --git a/libvips/foreign/magick.h b/libvips/foreign/magick.h
index 97690f81..507e7e11 100644
--- a/libvips/foreign/magick.h
+++ b/libvips/foreign/magick.h
@@ -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 );
diff --git a/libvips/foreign/magick2vips.c b/libvips/foreign/magick2vips.c
index 738a9279..d4f358f2 100644
--- a/libvips/foreign/magick2vips.c
+++ b/libvips/foreign/magick2vips.c
@@ -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
diff --git a/libvips/foreign/magick7load.c b/libvips/foreign/magick7load.c
index 01be0930..35d18864 100644
--- a/libvips/foreign/magick7load.c
+++ b/libvips/foreign/magick7load.c
@@ -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
+#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 :-(
*/
diff --git a/libvips/foreign/magickload.c b/libvips/foreign/magickload.c
index 8c84fde4..79add3e1 100644
--- a/libvips/foreign/magickload.c
+++ b/libvips/foreign/magickload.c
@@ -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.
*
diff --git a/libvips/foreign/pdfload.c b/libvips/foreign/pdfload.c
index 89f1c960..980dd162 100644
--- a/libvips/foreign/pdfload.c
+++ b/libvips/foreign/pdfload.c
@@ -55,6 +55,8 @@
#include
#include
+#include "pforeign.h"
+
#ifdef HAVE_POPPLER
#include
@@ -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
diff --git a/libvips/foreign/pdfload_pdfium.c b/libvips/foreign/pdfload_pdfium.c
new file mode 100644
index 00000000..79bb2598
--- /dev/null
+++ b/libvips/foreign/pdfload_pdfium.c
@@ -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
+#endif /*HAVE_CONFIG_H*/
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "pforeign.h"
+
+#ifdef HAVE_PDFIUM
+
+#include
+#include
+
+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*/
diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h
index 08973569..1f326705 100644
--- a/libvips/foreign/pforeign.h
+++ b/libvips/foreign/pforeign.h
@@ -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*/
diff --git a/libvips/foreign/radiance.c b/libvips/foreign/radiance.c
index 77906151..6903221b 100644
--- a/libvips/foreign/radiance.c
+++ b/libvips/foreign/radiance.c
@@ -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 ) ||
diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c
index 49220307..70b7a810 100644
--- a/libvips/foreign/tiff2vips.c
+++ b/libvips/foreign/tiff2vips.c
@@ -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.
*/
diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c
index b645da3a..1a98000b 100644
--- a/libvips/foreign/vipspng.c
+++ b/libvips/foreign/vipspng.c
@@ -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 )
diff --git a/libvips/include/vips/colour.h b/libvips/include/vips/colour.h
index 77404b9c..7978d325 100644
--- a/libvips/include/vips/colour.h
+++ b/libvips/include/vips/colour.h
@@ -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));
diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h
index 87cea5fd..60803450 100644
--- a/libvips/include/vips/enumtypes.h
+++ b/libvips/include/vips/enumtypes.h
@@ -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())
diff --git a/libvips/include/vips/header.h b/libvips/include/vips/header.h
index 922f92ba..7fdf84f7 100644
--- a/libvips/include/vips/header.h
+++ b/libvips/include/vips/header.h
@@ -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 );
diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c
index 411e0b15..48a0dee2 100644
--- a/libvips/iofuncs/image.c
+++ b/libvips/iofuncs/image.c
@@ -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 );
+ }
}
/**
diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c
index 871a0af9..c7026333 100644
--- a/libvips/iofuncs/util.c
+++ b/libvips/iofuncs/util.c
@@ -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 );
}
diff --git a/libvips/iofuncs/vips.c b/libvips/iofuncs/vips.c
index 49e6614f..267db0aa 100644
--- a/libvips/iofuncs/vips.c
+++ b/libvips/iofuncs/vips.c
@@ -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
#endif /*HAVE_CONFIG_H*/
@@ -69,12 +75,12 @@
#include
#include
#include
-#include
#include
+#include
+#include
#ifdef HAVE_SYS_FILE_H
#include
#endif /*HAVE_SYS_FILE_H*/
-#include
#ifdef HAVE_UNISTD_H
#include
#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 );
}
diff --git a/m4/pdfium.m4 b/m4/pdfium.m4
new file mode 100644
index 00000000..393745dd
--- /dev/null
+++ b/m4/pdfium.m4
@@ -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 ],[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 ],
+ [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