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