Merge branch 'master' into region-shrink-method
This commit is contained in:
commit
b752d2e442
27
.travis.yml
27
.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
|
||||
|
15
ChangeLog
15
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
|
||||
|
@ -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
|
||||
|
||||
|
64
configure.ac
64
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
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
<programlisting language="cpp">
|
||||
/* compile with:
|
||||
* g++ -g -Wall try.cc `pkg-config vips-cpp --cflags --libs`
|
||||
* g++ -g -Wall example.cc `pkg-config vips-cpp --cflags --libs`
|
||||
*/
|
||||
|
||||
#include <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);
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Everything before <code>VImage in = VImage::..</code> is exactly
|
||||
as the C API. This boilerplate gives the example a set of standard
|
||||
command-line flags.
|
||||
as the C API. vips_error_exit() just prints the arguments plus the
|
||||
libvips error log and exits with an error code.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This line is the C++ equivalent of vips_image_new_from_file(). It works
|
||||
<code>VImage in = VImage::..</code> is the C++ equivalent of
|
||||
vips_image_new_from_file(). It works
|
||||
in the same way, the differences being:
|
||||
|
||||
<itemizedlist>
|
||||
|
@ -1226,6 +1226,36 @@ vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* TRUE if a profile is compatible with an image.
|
||||
*/
|
||||
gboolean
|
||||
vips_icc_is_compatible_profile( VipsImage *image,
|
||||
void *data, size_t data_length )
|
||||
{
|
||||
cmsHPROFILE profile;
|
||||
|
||||
if( !(profile = cmsOpenProfileFromMem( data, data_length )) ) {
|
||||
g_warning( "%s", _( "corrupt profile" ) );
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
if( vips_image_expected_bands( image ) !=
|
||||
vips_icc_profile_needs_bands( profile ) ) {
|
||||
VIPS_FREEF( cmsCloseProfile, profile );
|
||||
g_warning( "%s",
|
||||
_( "profile incompatible with image" ) );
|
||||
return( FALSE );
|
||||
}
|
||||
if( vips_image_expected_sig( image ) != cmsGetColorSpace( profile ) ) {
|
||||
VIPS_FREEF( cmsCloseProfile, profile );
|
||||
g_warning( "%s",
|
||||
_( "profile colourspace differs from image" ) );
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
#else /*!HAVE_LCMS2*/
|
||||
|
||||
#include <vips/vips.h>
|
||||
@ -1245,6 +1275,13 @@ vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
gboolean
|
||||
vips_icc_is_compatible_profile( VipsImage *image,
|
||||
void *data, size_t data_length )
|
||||
{
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
#endif /*HAVE_LCMS*/
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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" ),
|
||||
|
@ -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().
|
||||
|
@ -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 );
|
||||
|
@ -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 ) &&
|
||||
|
@ -6,6 +6,7 @@ libforeign_la_SOURCES = \
|
||||
gifload.c \
|
||||
cairo.c \
|
||||
pdfload.c \
|
||||
pdfload_pdfium.c \
|
||||
svgload.c \
|
||||
radiance.c \
|
||||
radload.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();
|
||||
|
@ -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 ) {
|
||||
|
@ -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().
|
||||
*
|
||||
|
@ -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( "" );
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -4,6 +4,8 @@
|
||||
* - from magickload
|
||||
* 25/11/16
|
||||
* - add @n, deprecate @all_frames (just sets n = -1)
|
||||
* 24/4/18
|
||||
* - add format hint
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -56,6 +58,8 @@
|
||||
|
||||
#include <MagickCore/MagickCore.h>
|
||||
|
||||
#include "magick.h"
|
||||
|
||||
typedef struct _VipsForeignLoadMagick7 {
|
||||
VipsForeignLoad parent_object;
|
||||
|
||||
@ -63,6 +67,7 @@ typedef struct _VipsForeignLoadMagick7 {
|
||||
*/
|
||||
gboolean all_frames;
|
||||
|
||||
char *format; /* Format hint */
|
||||
char *density; /* Load at this resolution */
|
||||
int page; /* Load this page (frame) */
|
||||
int n; /* Load this many pages */
|
||||
@ -71,7 +76,11 @@ typedef struct _VipsForeignLoadMagick7 {
|
||||
ImageInfo *image_info;
|
||||
ExceptionInfo *exception;
|
||||
|
||||
int n_frames; /* Number of frames in file */
|
||||
/* Number of pages in image.
|
||||
*/
|
||||
int n_pages;
|
||||
|
||||
int n_frames; /* Number of frames we will read */
|
||||
Image **frames; /* An Image* for each frame */
|
||||
CacheView **cache_view; /* A CacheView for each frame */
|
||||
int frame_height;
|
||||
@ -278,26 +287,6 @@ vips_foreign_load_magick7_dispose( GObject *gobject )
|
||||
dispose( gobject );
|
||||
}
|
||||
|
||||
static void *
|
||||
vips_foreign_load_magick7_genesis_cb( void *client )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_load_magick7_genesis:\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
MagickCoreGenesis( vips_get_argv0(), MagickFalse );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick7_genesis( void )
|
||||
{
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
VIPS_ONCE( &once, vips_foreign_load_magick7_genesis_cb, NULL );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_magick7_build( VipsObject *object )
|
||||
{
|
||||
@ -307,7 +296,7 @@ vips_foreign_load_magick7_build( VipsObject *object )
|
||||
printf( "vips_foreign_load_magick7_build: %p\n", object );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
vips_foreign_load_magick7_genesis();
|
||||
magick_genesis();
|
||||
|
||||
magick7->image_info = CloneImageInfo( NULL );
|
||||
magick7->exception = AcquireExceptionInfo();
|
||||
@ -319,6 +308,12 @@ vips_foreign_load_magick7_build( VipsObject *object )
|
||||
if( magick7->all_frames )
|
||||
magick7->n = -1;
|
||||
|
||||
/* The file format hint, eg. "ICO".
|
||||
*/
|
||||
if( magick7->format )
|
||||
vips_strncpy( magick7->image_info->magick,
|
||||
magick7->format, MaxTextExtent );
|
||||
|
||||
/* Canvas resolution for rendering vector formats like SVG.
|
||||
*/
|
||||
VIPS_SETSTR( magick7->image_info->density, magick7->density );
|
||||
@ -377,12 +372,12 @@ vips_foreign_load_magick7_class_init( VipsForeignLoadMagick7Class *class )
|
||||
vips_foreign_load_magick7_get_flags_filename;
|
||||
load_class->get_flags = vips_foreign_load_magick7_get_flags;
|
||||
|
||||
VIPS_ARG_BOOL( class, "all_frames", 3,
|
||||
_( "all_frames" ),
|
||||
_( "Read all frames from an image" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick7, all_frames ),
|
||||
FALSE );
|
||||
VIPS_ARG_STRING( class, "format", 3,
|
||||
_( "Format" ),
|
||||
_( "Image format hint" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick7, format ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_STRING( class, "density", 4,
|
||||
_( "Density" ),
|
||||
@ -405,6 +400,13 @@ vips_foreign_load_magick7_class_init( VipsForeignLoadMagick7Class *class )
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick7, n ),
|
||||
-1, 100000, 1 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "all_frames", 7,
|
||||
_( "all_frames" ),
|
||||
_( "Read all frames from an image" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick7, all_frames ),
|
||||
FALSE );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@ -423,6 +425,26 @@ vips_foreign_load_magick7_error( VipsForeignLoadMagick7 *magick7 )
|
||||
magick7->exception->description );
|
||||
}
|
||||
|
||||
static int
|
||||
magick7_get_bands( Image *image )
|
||||
{
|
||||
int bands;
|
||||
int i;
|
||||
|
||||
/* We skip all index channels. Lots of images can have these, it's not
|
||||
* just the palette ones.
|
||||
*/
|
||||
bands = 0;
|
||||
for( i = 0; i < GetPixelChannels( image ); i++ ) {
|
||||
PixelChannel channel = GetPixelChannelChannel( image, i );
|
||||
|
||||
if( channel != IndexPixelChannel )
|
||||
bands += 1;
|
||||
}
|
||||
|
||||
return( bands );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_magick7_parse( VipsForeignLoadMagick7 *magick7,
|
||||
Image *image, VipsImage *out )
|
||||
@ -430,7 +452,7 @@ vips_foreign_load_magick7_parse( VipsForeignLoadMagick7 *magick7,
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( magick7 );
|
||||
|
||||
const char *key;
|
||||
int i;
|
||||
Image *p;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "image->depth = %zd\n", image->depth );
|
||||
@ -446,17 +468,7 @@ vips_foreign_load_magick7_parse( VipsForeignLoadMagick7 *magick7,
|
||||
out->Xsize = image->columns;
|
||||
out->Ysize = image->rows;
|
||||
magick7->frame_height = image->rows;
|
||||
|
||||
/* We skip all index channels. Lots of images can have these, it's not
|
||||
* just the palette ones.
|
||||
*/
|
||||
out->Bands = 0;
|
||||
for( i = 0; i < GetPixelChannels( image ); i++ ) {
|
||||
PixelChannel channel = GetPixelChannelChannel( image, i );
|
||||
|
||||
if( channel != IndexPixelChannel )
|
||||
out->Bands += 1;
|
||||
}
|
||||
out->Bands = magick7_get_bands( image );
|
||||
|
||||
/* Depth can be 'fractional'. You'd think we should use
|
||||
* GetImageDepth() but that seems to compute something very complex.
|
||||
@ -554,16 +566,43 @@ vips_foreign_load_magick7_parse( VipsForeignLoadMagick7 *magick7,
|
||||
vips_image_set_string( out, vips_buf_all( &name ), value );
|
||||
}
|
||||
|
||||
magick7->n_pages = GetImageListLength( GetFirstImageInList( image ) );
|
||||
#ifdef DEBUG
|
||||
printf( "image has %d pages\n", magick7->n_pages );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Do we have a set of equal-sized frames? Append them.
|
||||
|
||||
FIXME ... there must be an attribute somewhere from dicom read
|
||||
which says this is a volumetric image
|
||||
|
||||
*/
|
||||
magick7->n_frames = GetImageListLength( GetFirstImageInList( image ) );
|
||||
magick7->n_frames = 0;
|
||||
for( p = image; p; (p = GetNextImageInList( p )) ) {
|
||||
if( p->columns != (unsigned int) out->Xsize ||
|
||||
p->rows != (unsigned int) out->Ysize ||
|
||||
magick7_get_bands( p ) != out->Bands ||
|
||||
p->depth != image->depth ) {
|
||||
#ifdef DEBUG
|
||||
printf( "frame %d differs\n", magick7->n_frames );
|
||||
printf( "%zdx%zd, %d bands\n",
|
||||
p->columns, p->rows, magick7_get_bands( p ) );
|
||||
printf( "first frame is %dx%d, %d bands\n",
|
||||
out->Xsize, out->Ysize, out->Bands );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
magick7->n_frames += 1;
|
||||
}
|
||||
if( p )
|
||||
/* Nope ... just do the first image in the list.
|
||||
*/
|
||||
magick7->n_frames = 1;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "image has %d frames\n", magick7->n_frames );
|
||||
printf( "will read %d frames\n", magick7->n_frames );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( magick7->n != -1 )
|
||||
@ -576,11 +615,13 @@ vips_foreign_load_magick7_parse( VipsForeignLoadMagick7 *magick7,
|
||||
out->Ysize *= magick7->n_frames;
|
||||
}
|
||||
|
||||
vips_image_set_int( out, VIPS_META_N_PAGES, magick7->n_pages );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* We don't bother with GetPixelReadMask((), assume it's everywhere. Don't
|
||||
* bother with traits, assume taht's always update.
|
||||
/* We don't bother with GetPixelReadMask(), assume it's everywhere. Don't
|
||||
* bother with traits, assume that's always updated.
|
||||
*
|
||||
* We do skip index channels. Palette images add extra index channels
|
||||
* containing the index value from the file before colourmap lookup.
|
||||
@ -681,7 +722,8 @@ vips_foreign_load_magick7_load( VipsForeignLoadMagick7 *magick7 )
|
||||
/* Record frame pointers.
|
||||
*/
|
||||
g_assert( !magick7->frames );
|
||||
if( !(magick7->frames = VIPS_ARRAY( NULL, magick7->n_frames, Image * )) )
|
||||
if( !(magick7->frames =
|
||||
VIPS_ARRAY( NULL, magick7->n_frames, Image * )) )
|
||||
return( -1 );
|
||||
p = magick7->image;
|
||||
for( i = 0; i < magick7->n_frames; i++ ) {
|
||||
@ -735,7 +777,7 @@ ismagick7( const char *filename )
|
||||
ExceptionInfo *exception;
|
||||
int result;
|
||||
|
||||
vips_foreign_load_magick7_genesis();
|
||||
magick_genesis();
|
||||
|
||||
/* Horribly slow :-(
|
||||
*/
|
||||
@ -835,7 +877,7 @@ vips_foreign_load_magick7_buffer_is_a_buffer( const void *buf, size_t len )
|
||||
ExceptionInfo *exception;
|
||||
int result;
|
||||
|
||||
vips_foreign_load_magick7_genesis();
|
||||
magick_genesis();
|
||||
|
||||
/* Horribly slow :-(
|
||||
*/
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -55,6 +55,8 @@
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "pforeign.h"
|
||||
|
||||
#ifdef HAVE_POPPLER
|
||||
|
||||
#include <cairo.h>
|
||||
@ -91,7 +93,7 @@ typedef struct _VipsForeignLoadPdf {
|
||||
*/
|
||||
int n_pages;
|
||||
|
||||
/* We need to read out the side of each page we will render, and lay
|
||||
/* We need to read out the size of each page we will render, and lay
|
||||
* them out in the final image.
|
||||
*/
|
||||
VipsRect image;
|
||||
@ -145,33 +147,6 @@ vips_foreign_load_pdf_get_flags( VipsForeignLoad *load )
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len )
|
||||
{
|
||||
const guchar *str = (const guchar *) buf;
|
||||
|
||||
if( len >= 4 &&
|
||||
str[0] == '%' &&
|
||||
str[1] == 'P' &&
|
||||
str[2] == 'D' &&
|
||||
str[3] == 'F' )
|
||||
return( 1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
vips_foreign_load_pdf_is_a( const char *filename )
|
||||
{
|
||||
unsigned char buf[4];
|
||||
|
||||
if( vips__get_bytes( filename, buf, 4 ) == 4 &&
|
||||
vips_foreign_load_pdf_is_a_buffer( buf, 4 ) )
|
||||
return( 1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_pdf_get_page( VipsForeignLoadPdf *pdf, int page_no )
|
||||
{
|
||||
@ -229,9 +204,10 @@ vips_foreign_load_pdf_set_image( VipsForeignLoadPdf *pdf, VipsImage *out )
|
||||
*/
|
||||
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
|
||||
|
||||
/* Extract and attach metadata.
|
||||
/* Extract and attach metadata. Set the old name too for compat.
|
||||
*/
|
||||
vips_image_set_int( out, "pdf-n_pages", pdf->n_pages );
|
||||
vips_image_set_int( out, VIPS_META_N_PAGES, pdf->n_pages );
|
||||
|
||||
for( i = 0; i < n_metadata; i++ ) {
|
||||
VipsForeignLoadPdfMetadata *metadata =
|
||||
@ -429,7 +405,7 @@ vips_foreign_load_pdf_load( VipsForeignLoad *load )
|
||||
* (again) keep the number of calls to page_render low.
|
||||
*/
|
||||
if( vips_linecache( t[0], &t[1],
|
||||
"tile_height", 5000,
|
||||
"tile_height", VIPS_MIN( 5000, pdf->pages[0].height ),
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
if( vips_image_write( t[1], load->real ) )
|
||||
@ -658,6 +634,37 @@ vips_foreign_load_pdf_buffer_init( VipsForeignLoadPdfBuffer *buffer )
|
||||
|
||||
#endif /*HAVE_POPPLER*/
|
||||
|
||||
/* Also used by the pdfium loader.
|
||||
*/
|
||||
gboolean
|
||||
vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len )
|
||||
{
|
||||
const guchar *str = (const guchar *) buf;
|
||||
|
||||
if( len >= 4 &&
|
||||
str[0] == '%' &&
|
||||
str[1] == 'P' &&
|
||||
str[2] == 'D' &&
|
||||
str[3] == 'F' )
|
||||
return( 1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Also used by the pdfium loader.
|
||||
*/
|
||||
gboolean
|
||||
vips_foreign_load_pdf_is_a( const char *filename )
|
||||
{
|
||||
unsigned char buf[4];
|
||||
|
||||
if( vips__get_bytes( filename, buf, 4 ) == 4 &&
|
||||
vips_foreign_load_pdf_is_a_buffer( buf, 4 ) )
|
||||
return( 1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_pdfload:
|
||||
* @filename: file to load
|
||||
@ -678,18 +685,6 @@ vips_foreign_load_pdf_buffer_init( VipsForeignLoadPdfBuffer *buffer )
|
||||
* converted. If you need CMYK bitmaps, you should use vips_magickload()
|
||||
* instead.
|
||||
*
|
||||
* Rendering is progressive, that is, the image is rendered in strips equal in
|
||||
* height to the tile height. If your PDF contains large image files and
|
||||
* they span several strips in the output image, they will be decoded multiple
|
||||
* times. To fix this, increase the the tile height, for example:
|
||||
*
|
||||
* |[
|
||||
* vips copy huge.pdf x.png --vips-tile-height=1024
|
||||
* ]|
|
||||
*
|
||||
* Will process images in 1024-pixel high strips, potentially much faster,
|
||||
* though of course also using a lot more memory.
|
||||
*
|
||||
* Use @page to select a page to render, numbering from zero.
|
||||
*
|
||||
* Use @n to select the number of pages to render. The default is 1. Pages are
|
||||
|
651
libvips/foreign/pdfload_pdfium.c
Normal file
651
libvips/foreign/pdfload_pdfium.c
Normal file
@ -0,0 +1,651 @@
|
||||
/* load PDF with PDFium
|
||||
*
|
||||
* 5/4/18
|
||||
* - from pdfload.c
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/* TODO
|
||||
*
|
||||
* - what about filename encodings
|
||||
* - need to test on Windows
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "pforeign.h"
|
||||
|
||||
#ifdef HAVE_PDFIUM
|
||||
|
||||
#include <fpdfview.h>
|
||||
#include <fpdf_doc.h>
|
||||
|
||||
typedef struct _VipsForeignLoadPdf {
|
||||
VipsForeignLoad parent_object;
|
||||
|
||||
/* Load this page.
|
||||
*/
|
||||
int page_no;
|
||||
|
||||
/* Load this many pages.
|
||||
*/
|
||||
int n;
|
||||
|
||||
/* Render at this DPI.
|
||||
*/
|
||||
double dpi;
|
||||
|
||||
/* Calculate this from DPI. At 72 DPI, we render 1:1.
|
||||
*/
|
||||
double scale;
|
||||
|
||||
FPDF_DOCUMENT *doc;
|
||||
FPDF_PAGE *page;
|
||||
int current_page;
|
||||
|
||||
/* Doc has this many pages.
|
||||
*/
|
||||
int n_pages;
|
||||
|
||||
/* We need to read out the size of each page we will render, and lay
|
||||
* them out in the final image.
|
||||
*/
|
||||
VipsRect image;
|
||||
VipsRect *pages;
|
||||
|
||||
} VipsForeignLoadPdf;
|
||||
|
||||
typedef VipsForeignLoadClass VipsForeignLoadPdfClass;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadPdf, vips_foreign_load_pdf,
|
||||
VIPS_TYPE_FOREIGN_LOAD );
|
||||
|
||||
static char *vips_pdfium_errors[] = {
|
||||
"no error",
|
||||
"unknown error",
|
||||
"file not found or could not be opened",
|
||||
"file not in PDF format or corrupted",
|
||||
"password required or incorrect password",
|
||||
"unsupported security scheme",
|
||||
"page not found or content error"
|
||||
};
|
||||
|
||||
static void
|
||||
vips_pdfium_error( void )
|
||||
{
|
||||
int err = FPDF_GetLastError();
|
||||
|
||||
if( err >= 0 &&
|
||||
err < VIPS_NUMBER( vips_pdfium_errors ) )
|
||||
vips_error( "pdfload", "%s", _( vips_pdfium_errors[err] ) );
|
||||
else
|
||||
vips_error( "pdfload", "%s", _( "unknown error" ) );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_pdf_dispose( GObject *gobject )
|
||||
{
|
||||
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) gobject;
|
||||
|
||||
VIPS_FREEF( FPDF_ClosePage, pdf->page );
|
||||
VIPS_FREEF( FPDF_CloseDocument, pdf->doc );
|
||||
|
||||
G_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )->dispose( gobject );
|
||||
}
|
||||
|
||||
static void *
|
||||
vips_pdfium_init_cb( void *dummy )
|
||||
{
|
||||
FPDF_LIBRARY_CONFIG config;
|
||||
|
||||
config.version = 2;
|
||||
config.m_pUserFontPaths = NULL;
|
||||
config.m_pIsolate = NULL;
|
||||
config.m_v8EmbedderSlot = 0;
|
||||
|
||||
FPDF_InitLibraryWithConfig( &config );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_pdf_build( VipsObject *object )
|
||||
{
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) object;
|
||||
|
||||
VIPS_ONCE( &once, vips_pdfium_init_cb, NULL );
|
||||
|
||||
if( !vips_object_argument_isset( object, "scale" ) )
|
||||
pdf->scale = pdf->dpi / 72.0;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_pdf_get_flags_filename( const char *filename )
|
||||
{
|
||||
/* We can't render any part of the page on demand, but we can render
|
||||
* separate pages. Might as well call ourselves partial.
|
||||
*/
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_pdf_get_flags( VipsForeignLoad *load )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_pdf_get_page( VipsForeignLoadPdf *pdf, int page_no )
|
||||
{
|
||||
if( pdf->current_page != page_no ) {
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( pdf );
|
||||
|
||||
VIPS_FREEF( FPDF_ClosePage, pdf->page );
|
||||
pdf->current_page = -1;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_load_pdf_get_page: %d\n", page_no );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( !(pdf->page = FPDF_LoadPage( pdf->doc, page_no )) ) {
|
||||
vips_pdfium_error();
|
||||
vips_error( class->nickname,
|
||||
_( "unable to load page %d" ), page_no );
|
||||
return( -1 );
|
||||
}
|
||||
pdf->current_page = page_no;
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* String-based metadata fields we extract.
|
||||
*/
|
||||
typedef struct _VipsForeignLoadPdfMetadata {
|
||||
char *tag; /* as understood by PDFium */
|
||||
char *field; /* as understood by libvips */
|
||||
} VipsForeignLoadPdfMetadata;
|
||||
|
||||
static VipsForeignLoadPdfMetadata vips_foreign_load_pdf_metadata[] = {
|
||||
{ "Title", "pdf-title" },
|
||||
{ "Author", "pdf-author" },
|
||||
{ "Subject", "pdf-subject" },
|
||||
{ "Keywords", "pdf-keywords" },
|
||||
{ "Creator", "pdf-creator" },
|
||||
{ "Producer", "pdf-producer" },
|
||||
/* poppler has "metadata" as well, but pdfium does not support this */
|
||||
};
|
||||
static int n_metadata = VIPS_NUMBER( vips_foreign_load_pdf_metadata );
|
||||
|
||||
static int
|
||||
vips_foreign_load_pdf_set_image( VipsForeignLoadPdf *pdf, VipsImage *out )
|
||||
{
|
||||
int i;
|
||||
double res;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_load_pdf_set_image: %p\n", pdf );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* We render to a linecache, so fat strips work well.
|
||||
*/
|
||||
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
|
||||
|
||||
/* Extract and attach metadata. Set the old name too for compat.
|
||||
*/
|
||||
vips_image_set_int( out, "pdf-n_pages", pdf->n_pages );
|
||||
vips_image_set_int( out, VIPS_META_N_PAGES, pdf->n_pages );
|
||||
|
||||
for( i = 0; i < n_metadata; i++ ) {
|
||||
VipsForeignLoadPdfMetadata *metadata =
|
||||
&vips_foreign_load_pdf_metadata[i];
|
||||
|
||||
char text[1024];
|
||||
int len;
|
||||
|
||||
len = FPDF_GetMetaText( pdf->doc, metadata->tag, text, 1024 );
|
||||
if( len > 0 ) {
|
||||
char *str;
|
||||
|
||||
/* Silently ignore coding errors.
|
||||
*/
|
||||
if( (str = g_utf16_to_utf8( (gunichar2 *) text, len,
|
||||
NULL, NULL, NULL )) ) {
|
||||
vips_image_set_string( out,
|
||||
metadata->field, str );
|
||||
g_free( str );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We need pixels/mm for vips.
|
||||
*/
|
||||
res = pdf->dpi / 25.4;
|
||||
|
||||
vips_image_init_fields( out,
|
||||
pdf->image.width, pdf->image.height,
|
||||
4, VIPS_FORMAT_UCHAR,
|
||||
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, res, res );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_pdf_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
|
||||
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
|
||||
|
||||
int top;
|
||||
int i;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_load_pdf_header: %p\n", pdf );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
pdf->n_pages = FPDF_GetPageCount( pdf->doc );
|
||||
|
||||
/* @n == -1 means until the end of the doc.
|
||||
*/
|
||||
if( pdf->n == -1 )
|
||||
pdf->n = pdf->n_pages - pdf->page_no;
|
||||
|
||||
if( pdf->page_no + pdf->n > pdf->n_pages ||
|
||||
pdf->page_no < 0 ||
|
||||
pdf->n <= 0 ) {
|
||||
vips_error( class->nickname, "%s", _( "pages out of range" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Lay out the pages in our output image.
|
||||
*/
|
||||
if( !(pdf->pages = VIPS_ARRAY( pdf, pdf->n, VipsRect )) )
|
||||
return( -1 );
|
||||
|
||||
top = 0;
|
||||
pdf->image.left = 0;
|
||||
pdf->image.top = 0;
|
||||
pdf->image.width = 0;
|
||||
pdf->image.height = 0;
|
||||
for( i = 0; i < pdf->n; i++ ) {
|
||||
if( vips_foreign_load_pdf_get_page( pdf, pdf->page_no + i ) )
|
||||
return( -1 );
|
||||
pdf->pages[i].left = 0;
|
||||
pdf->pages[i].top = top;
|
||||
pdf->pages[i].width =
|
||||
FPDF_GetPageWidth( pdf->page ) * pdf->scale;
|
||||
pdf->pages[i].height =
|
||||
FPDF_GetPageHeight( pdf->page ) * pdf->scale;
|
||||
|
||||
if( pdf->pages[i].width > pdf->image.width )
|
||||
pdf->image.width = pdf->pages[i].width;
|
||||
pdf->image.height += pdf->pages[i].height;
|
||||
|
||||
top += pdf->pages[i].height;
|
||||
}
|
||||
|
||||
/* If all pages are the same size, we can tag this as a toilet roll
|
||||
* image and tiffsave will be able to save it as a multipage tiff.
|
||||
*/
|
||||
for( i = 1; i < pdf->n; i++ )
|
||||
if( pdf->pages[i].width != pdf->pages[0].width ||
|
||||
pdf->pages[i].height != pdf->pages[0].height )
|
||||
break;
|
||||
if( i == pdf->n )
|
||||
vips_image_set_int( load->out,
|
||||
VIPS_META_PAGE_HEIGHT, pdf->pages[0].height );
|
||||
|
||||
vips_foreign_load_pdf_set_image( pdf, load->out );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_pdf_generate( VipsRegion *or,
|
||||
void *seq, void *a, void *b, gboolean *stop )
|
||||
{
|
||||
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) a;
|
||||
VipsRect *r = &or->valid;
|
||||
|
||||
int top;
|
||||
int i;
|
||||
int y;
|
||||
|
||||
/*
|
||||
printf( "vips_foreign_load_pdf_generate: "
|
||||
"left = %d, top = %d, width = %d, height = %d\n",
|
||||
r->left, r->top, r->width, r->height );
|
||||
*/
|
||||
|
||||
/* Poppler won't always paint the background. Use 255 (white) for the
|
||||
* bg, PDFs generally assume a paper backgrocund colour.
|
||||
*/
|
||||
vips_region_paint( or, r, 255 );
|
||||
|
||||
/* Search through the pages we are drawing for the first containing
|
||||
* this rect. This could be quicker, perhaps a binary search, but who
|
||||
* cares.
|
||||
*/
|
||||
for( i = 0; i < pdf->n; i++ )
|
||||
if( VIPS_RECT_BOTTOM( &pdf->pages[i] ) > r->top )
|
||||
break;
|
||||
|
||||
top = r->top;
|
||||
while( top < VIPS_RECT_BOTTOM( r ) ) {
|
||||
VipsRect rect;
|
||||
FPDF_BITMAP bitmap;
|
||||
|
||||
vips_rect_intersectrect( r, &pdf->pages[i], &rect );
|
||||
|
||||
if( vips_foreign_load_pdf_get_page( pdf, pdf->page_no + i ) )
|
||||
return( -1 );
|
||||
|
||||
/* 4 means RGBA.
|
||||
*/
|
||||
bitmap = FPDFBitmap_CreateEx( rect.width, rect.height, 4,
|
||||
VIPS_REGION_ADDR( or, rect.left, rect.top ),
|
||||
VIPS_REGION_LSKIP( or ) );
|
||||
|
||||
FPDF_RenderPageBitmap( bitmap, pdf->page,
|
||||
0, 0, rect.width, rect.height,
|
||||
0, 0 );
|
||||
|
||||
FPDFBitmap_Destroy( bitmap );
|
||||
|
||||
top += rect.height;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
/* PDFium writes BRGA, we must swap.
|
||||
*
|
||||
* FIXME ... this is a bit slow.
|
||||
*/
|
||||
for( y = 0; y < r->height; y++ ) {
|
||||
VipsPel *p;
|
||||
int x;
|
||||
|
||||
p = VIPS_REGION_ADDR( or, r->left, r->top + y );
|
||||
for( x = 0; x < r->width; x++ ) {
|
||||
VIPS_SWAP( VipsPel, p[0], p[2] );
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_pdf_load( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
|
||||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( (VipsObject *) load, 2 );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_load_pdf_load: %p\n", pdf );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
/* Read to this image, then cache to out, see below.
|
||||
*/
|
||||
t[0] = vips_image_new();
|
||||
|
||||
vips_foreign_load_pdf_set_image( pdf, t[0] );
|
||||
if( vips_image_generate( t[0],
|
||||
NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
/* PDFium does not like rendering parts of pages :-( always render
|
||||
* complete ones.
|
||||
*/
|
||||
if( vips_linecache( t[0], &t[1],
|
||||
"tile_height", pdf->pages[0].height,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
if( vips_image_write( t[1], load->real ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_pdf_class_init( VipsForeignLoadPdfClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->dispose = vips_foreign_load_pdf_dispose;
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "pdfload_base";
|
||||
object_class->description = _( "load PDF with libpoppler" );
|
||||
object_class->build = vips_foreign_load_pdf_build;
|
||||
|
||||
load_class->get_flags_filename =
|
||||
vips_foreign_load_pdf_get_flags_filename;
|
||||
load_class->get_flags = vips_foreign_load_pdf_get_flags;
|
||||
load_class->load = vips_foreign_load_pdf_load;
|
||||
|
||||
VIPS_ARG_INT( class, "page", 10,
|
||||
_( "Page" ),
|
||||
_( "Load this page from the file" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadPdf, page_no ),
|
||||
0, 100000, 0 );
|
||||
|
||||
VIPS_ARG_INT( class, "n", 11,
|
||||
_( "n" ),
|
||||
_( "Load this many pages" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadPdf, n ),
|
||||
-1, 100000, 1 );
|
||||
|
||||
VIPS_ARG_DOUBLE( class, "dpi", 12,
|
||||
_( "DPI" ),
|
||||
_( "Render at this DPI" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadPdf, dpi ),
|
||||
0.001, 100000.0, 72.0 );
|
||||
|
||||
VIPS_ARG_DOUBLE( class, "scale", 13,
|
||||
_( "Scale" ),
|
||||
_( "Scale output by this factor" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadPdf, scale ),
|
||||
0.001, 100000.0, 1.0 );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_pdf_init( VipsForeignLoadPdf *pdf )
|
||||
{
|
||||
pdf->dpi = 72.0;
|
||||
pdf->scale = 1.0;
|
||||
pdf->n = 1;
|
||||
pdf->current_page = -1;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadPdfFile {
|
||||
VipsForeignLoadPdf parent_object;
|
||||
|
||||
/* Filename for load.
|
||||
*/
|
||||
char *filename;
|
||||
|
||||
} VipsForeignLoadPdfFile;
|
||||
|
||||
typedef VipsForeignLoadPdfClass VipsForeignLoadPdfFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadPdfFile, vips_foreign_load_pdf_file,
|
||||
vips_foreign_load_pdf_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_load_pdf_file_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
|
||||
VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) load;
|
||||
|
||||
if( !(pdf->doc = FPDF_LoadDocument( file->filename, NULL )) ) {
|
||||
vips_pdfium_error();
|
||||
vips_error( "pdfload",
|
||||
_( "unable to load \"%s\"" ), file->filename );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
VIPS_SETSTR( load->out->filename, file->filename );
|
||||
|
||||
return( vips_foreign_load_pdf_header( load ) );
|
||||
}
|
||||
|
||||
static const char *vips_foreign_pdf_suffs[] = {
|
||||
".pdf",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void
|
||||
vips_foreign_load_pdf_file_class_init(
|
||||
VipsForeignLoadPdfFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "pdfload";
|
||||
|
||||
foreign_class->suffs = vips_foreign_pdf_suffs;
|
||||
|
||||
load_class->is_a = vips_foreign_load_pdf_is_a;
|
||||
load_class->header = vips_foreign_load_pdf_file_header;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadPdfFile, filename ),
|
||||
NULL );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_pdf_file_init( VipsForeignLoadPdfFile *file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadPdfBuffer {
|
||||
VipsForeignLoadPdf parent_object;
|
||||
|
||||
/* Load from a buffer.
|
||||
*/
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignLoadPdfBuffer;
|
||||
|
||||
typedef VipsForeignLoadPdfClass VipsForeignLoadPdfBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadPdfBuffer, vips_foreign_load_pdf_buffer,
|
||||
vips_foreign_load_pdf_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_load_pdf_buffer_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
|
||||
VipsForeignLoadPdfBuffer *buffer =
|
||||
(VipsForeignLoadPdfBuffer *) load;
|
||||
|
||||
if( !(pdf->doc = FPDF_LoadMemDocument( buffer->buf->data,
|
||||
buffer->buf->length, NULL )) ) {
|
||||
vips_pdfium_error();
|
||||
vips_error( "pdfload",
|
||||
"%s", _( "unable to load from buffer" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( vips_foreign_load_pdf_header( load ) );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_pdf_buffer_class_init(
|
||||
VipsForeignLoadPdfBufferClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "pdfload_buffer";
|
||||
|
||||
load_class->is_a_buffer = vips_foreign_load_pdf_is_a_buffer;
|
||||
load_class->header = vips_foreign_load_pdf_buffer_header;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadPdfBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_pdf_buffer_init( VipsForeignLoadPdfBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /*HAVE_PDFIUM*/
|
@ -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*/
|
||||
|
@ -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 ) ||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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 )
|
||||
|
@ -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));
|
||||
|
@ -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())
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -21,6 +21,8 @@
|
||||
* - use expat for xml read, printf for xml write
|
||||
* 16/8/17
|
||||
* - validate strs as being utf-8 before we write
|
||||
* 9/4/18 Alexander--
|
||||
* - use O_TMPFILE, if available
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -55,6 +57,10 @@
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
/* Enable linux extensions like O_TMPFILE, if available.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
@ -69,12 +75,12 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#ifdef HAVE_SYS_FILE_H
|
||||
#include <sys/file.h>
|
||||
#endif /*HAVE_SYS_FILE_H*/
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif /*HAVE_UNISTD_H*/
|
||||
@ -181,19 +187,37 @@ vips__open_image_write( const char *filename, gboolean temp )
|
||||
|
||||
flags = MODE_WRITE;
|
||||
|
||||
#ifdef O_TMPFILE
|
||||
/* Linux-only extension creates an unlinked file. CREAT and TRUNC must
|
||||
* be clear. The filename arg to open() must name a directory.
|
||||
*/
|
||||
if( temp ) {
|
||||
char *dirname;
|
||||
|
||||
flags |= O_TMPFILE;
|
||||
flags &= ~O_CREAT;
|
||||
flags &= ~O_TRUNC;
|
||||
|
||||
dirname = g_path_get_dirname( filename );
|
||||
fd = vips_tracked_open( dirname, flags, 0666 );
|
||||
g_free( dirname );
|
||||
}
|
||||
else
|
||||
fd = vips_tracked_open( filename, flags, 0666 );
|
||||
#else /*!O_TMPFILE*/
|
||||
#ifdef _O_TEMPORARY
|
||||
/* On Windows, setting O_TEMP gets the file automatically
|
||||
/* On Windows, setting _O_TEMPORARY gets the file automatically
|
||||
* deleted on process exit, even if the processes crashes. See
|
||||
* vips_image_rewind() for what we do to help on *nix.
|
||||
*/
|
||||
if( temp )
|
||||
flags |= _O_TEMPORARY;
|
||||
#endif /*_O_TEMPORARY*/
|
||||
#endif /*O_TMPFILE*/
|
||||
|
||||
if( (fd = vips_tracked_open( filename, flags, 0666 )) < 0 ) {
|
||||
if( fd < 0 ) {
|
||||
vips_error_system( errno, "VipsImage",
|
||||
_( "unable to write to \"%s\"" ),
|
||||
filename );
|
||||
_( "unable to write to \"%s\"" ), filename );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
|
143
m4/pdfium.m4
Normal file
143
m4/pdfium.m4
Normal file
@ -0,0 +1,143 @@
|
||||
dnl From FIND_MOTIF and ACX_PTHREAD, without much understanding
|
||||
dnl
|
||||
dnl FIND_PDFIUM[ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]
|
||||
dnl ---------------------------------------------------
|
||||
dnl
|
||||
dnl Find pdfium libraries and headers
|
||||
dnl
|
||||
dnl Put -I stuff in PDFIUM_INCLUDES
|
||||
dnl Put PDFium objects in PDFIUM_LIBS (add this to the link line untouched!)
|
||||
dnl Define HAVE_PDFIUM if found
|
||||
dnl
|
||||
AC_DEFUN([FIND_PDFIUM], [
|
||||
AC_REQUIRE([AC_PATH_XTRA])
|
||||
|
||||
ZLIB_INCLUDES=""
|
||||
ZLIB_LIBS=""
|
||||
|
||||
# PDFium has a set of object archives that must be linked firectly into your
|
||||
# app, and a couple of true libraries. The objects must be linked in this
|
||||
# order.
|
||||
pdfium_objects="\
|
||||
libpdfium.a \
|
||||
libfpdfapi.a \
|
||||
libfxge.a \
|
||||
libfpdfdoc.a \
|
||||
libfxcrt.a \
|
||||
libfx_agg.a \
|
||||
libfxcodec.a \
|
||||
libfx_lpng.a \
|
||||
libfx_libopenjpeg.a \
|
||||
libfx_lcms2.a \
|
||||
libfx_freetype.a \
|
||||
libjpeg.a \
|
||||
libfdrm.a \
|
||||
libpwl.a \
|
||||
libbigint.a \
|
||||
libformfiller.a"
|
||||
|
||||
AC_ARG_WITH(pdfium,
|
||||
AS_HELP_STRING([--without-pdfium], [build without pdfium (default: test)]))
|
||||
# Treat --without-pdfium like --without-pdfium-includes
|
||||
# --without-pdfium-libraries
|
||||
if test "$with_pdfium" = "no"; then
|
||||
PDFIUM_INCLUDES=no
|
||||
PDFIUM_LIBS=no
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(pdfium-includes,
|
||||
AS_HELP_STRING([--with-pdfium-includes=DIR], [pdfium includes are in DIR]),
|
||||
PDFIUM_INCLUDES="-I$withval")
|
||||
AC_ARG_WITH(pdfium-libraries,
|
||||
AS_HELP_STRING([--with-pdfium-libraries=DIR], [pdfium libraries are in DIR]),
|
||||
PDFIUM_LIBS="$withval")
|
||||
|
||||
AC_MSG_CHECKING(for PDFIUM)
|
||||
|
||||
# Look for fpdfview.h ... this is a documented header, so it should be a good
|
||||
# target
|
||||
#
|
||||
# it won't be in the standard search path, but try $PREFIX
|
||||
if test "$PDFIUM_INCLUDES" = ""; then
|
||||
pdfium_save_CPPFLAGS="$CPPFLAGS"
|
||||
|
||||
CPPFLAGS="-I${prefix}/include $CPPFLAGS"
|
||||
|
||||
AC_TRY_COMPILE([#include <fpdfview.h>],[int a;],[
|
||||
PDFIUM_INCLUDES="-I${prefix}/include"
|
||||
], [
|
||||
PDFIUM_INCLUDES="no"
|
||||
]
|
||||
)
|
||||
|
||||
CPPFLAGS="$pdfium_save_CPPFLAGS"
|
||||
fi
|
||||
|
||||
# Now for the libraries ... if there's nothing set, try $PREFIX/lib
|
||||
if test "$PDFIUM_LIBS" = ""; then
|
||||
pdfium_save_LIBS="$LIBS"
|
||||
pdfium_save_CPPFLAGS="$CPPFLAGS"
|
||||
|
||||
LIBS=""
|
||||
for i in $pdfium_objects; do
|
||||
LIBS="$LIBS $prefix/lib/pdfium-obj/$i"
|
||||
done
|
||||
LIBS="$LIBS -L$prefix/lib -lm -lpthread"
|
||||
CPPFLAGS="$PDFIUM_INCLUDES $CPPFLAGS"
|
||||
|
||||
AC_TRY_LINK([#include <fpdfview.h>],
|
||||
[FPDF_DOCUMENT doc; doc = FPDF_LoadDocument("", "")], [
|
||||
PDFIUM_LIBS="${prefix}/lib"
|
||||
], [
|
||||
PDFIUM_LIBS=no
|
||||
]
|
||||
)
|
||||
|
||||
LIBS="$pdfium_save_LIBS"
|
||||
CPPFLAGS="$pdfium_save_CPPFLAGS"
|
||||
fi
|
||||
|
||||
# Print a helpful message
|
||||
pdfium_libraries_result="$PDFIUM_LIBS"
|
||||
pdfium_includes_result="$PDFIUM_INCLUDES"
|
||||
|
||||
if test x"$pdfium_libraries_result" = x""; then
|
||||
pdfium_libraries_result="in default path"
|
||||
fi
|
||||
if test x"$pdfium_includes_result" = x""; then
|
||||
pdfium_includes_result="in default path"
|
||||
fi
|
||||
|
||||
if test "$pdfium_libraries_result" = "no"; then
|
||||
pdfium_libraries_result="(none)"
|
||||
fi
|
||||
if test "$pdfium_includes_result" = "no"; then
|
||||
pdfium_includes_result="(none)"
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([libraries $pdfium_libraries_result, headers $pdfium_includes_result])
|
||||
|
||||
if test x"$PDFIUM_LIBS" != x"no"; then
|
||||
dir="$PDFIUM_LIBS"
|
||||
PDFIUM_LIBS=""
|
||||
for i in $pdfium_objects; do
|
||||
PDFIUM_LIBS="$PDFIUM_LIBS $prefix/lib/pdfium-obj/$i"
|
||||
done
|
||||
PDFIUM_LIBS="$PDFIUM_LIBS -L$dir -lm -lpthread"
|
||||
fi
|
||||
|
||||
AC_SUBST(PDFIUM_LIBS)
|
||||
AC_SUBST(PDFIUM_INCLUDES)
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test "$PDFIUM_INCLUDES" != "no" && test "$PDFIUM_LIBS" != "no"; then
|
||||
AC_DEFINE(HAVE_PDFIUM,1,
|
||||
[Define if you have pdfium libraries and header files.])
|
||||
$1
|
||||
else
|
||||
PDFIUM_INCLUDES=""
|
||||
PDFIUM_LIBS=""
|
||||
$2
|
||||
fi
|
||||
|
||||
])dnl
|
Loading…
x
Reference in New Issue
Block a user