diff --git a/ChangeLog b/ChangeLog index 6face640..f8f67b65 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,15 @@ - add VipsInterpolate and guint64 support to C++ API - add VImage::new_from_memory_steal [Zeranoe] - vipsthumbnail supports stdin / stdout thumbnailing +- have a lock just for pdfium [DarthSim] +- better GraphicsMagick image write [bfriesen] +- get pdfium load building again [Projkt-James] +- add _source load support for pdfium + +18/10/20 started 8.10.3 +- relax heic is_a rules [hisham] +- fix vips7 webp load [barryspearce] +- fix out of bounds exif read in heifload 6/9/20 started 8.10.2 - update magicksave/load profile handling [kelilevi] @@ -22,6 +31,7 @@ - improve seek behaviour on pipes - add "speed" param to heifsave [lovell] - fix regression in C path for dilate / erode [kleisauke] +- fix build with libheif save but no load [estepnv] 9/8/20 started 8.10.1 - fix markdown -> xml conversion in doc generation diff --git a/configure.ac b/configure.ac index badb77df..e1236520 100644 --- a/configure.ac +++ b/configure.ac @@ -913,18 +913,20 @@ fi AC_ARG_WITH([pdfium], AS_HELP_STRING([--without-pdfium], [build without pdfium (default: test)])) +# pick 4200 as the starting version number ... no reason, really, it'd +# probably work with much older versions 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 - EXTRA_LIBS_USED="$EXTRA_LIBS_USED $PDFIUM_LIBS" - with_pdfium=yes - ],[ - with_pdfium=no - ] - ) + PKG_CHECK_MODULES(PDFIUM, pdfium >= 4200, [ + AC_DEFINE(HAVE_PDFIUM,1,[define if you have pdfium > 4200.]) + if test x"$with_poppler" != x"no"; then + AC_MSG_WARN([PDFium found, disabling poppler]) + with_poppler=no + fi + with_pdfium=yes + PACKAGES_USED="$PACKAGES_USED pdfium" + ], [ + with_pdfium=no + ]) fi # poppler @@ -932,15 +934,14 @@ AC_ARG_WITH([poppler], AS_HELP_STRING([--without-poppler], [build without poppler (default: test)])) if test x"$with_poppler" != x"no"; then - PKG_CHECK_MODULES(POPPLER, [poppler-glib >= 0.16.0 cairo >= 1.2], - [AC_DEFINE(HAVE_POPPLER,1,[define if you have poppler-glib >= 0.16.0 and cairo >= 1.2 installed.]) - with_poppler=yes - PACKAGES_USED="$PACKAGES_USED poppler-glib cairo" - ], - [AC_MSG_WARN([poppler-glib >= 0.16.0 or cairo >= 1.2 not found; disabling PDF load via poppler]) - with_poppler=no - ] - ) + PKG_CHECK_MODULES(POPPLER, [poppler-glib >= 0.16.0 cairo >= 1.2], [ + AC_DEFINE(HAVE_POPPLER,1,[define if you have poppler-glib >= 0.16.0 and cairo >= 1.2 installed.]) + with_poppler=yes + PACKAGES_USED="$PACKAGES_USED poppler-glib cairo" + ], [ + AC_MSG_WARN([poppler-glib >= 0.16.0 or cairo >= 1.2 not found; disabling PDF load via poppler]) + with_poppler=no + ]) fi # librsvg @@ -949,15 +950,14 @@ AC_ARG_WITH([rsvg], # 2.40.3 so we get the UNLIMITED open flag if test x"$with_rsvg" != x"no"; then - PKG_CHECK_MODULES(RSVG, [librsvg-2.0 >= 2.40.3 cairo >= 1.2], - [AC_DEFINE(HAVE_RSVG,1,[define if you have librsvg-2.0 >= 2.40.3 and cairo >= 1.2 installed.]) - with_rsvg=yes - PACKAGES_USED="$PACKAGES_USED librsvg-2.0 cairo" - ], - [AC_MSG_WARN([librsvg-2.0 >= 2.40.3 or cairo >= 1.2 not found; disabling SVG load via rsvg]) - with_rsvg=no - ] - ) + PKG_CHECK_MODULES(RSVG, [librsvg-2.0 >= 2.40.3 cairo >= 1.2], [ + AC_DEFINE(HAVE_RSVG,1,[define if you have librsvg-2.0 >= 2.40.3 and cairo >= 1.2 installed.]) + with_rsvg=yes + PACKAGES_USED="$PACKAGES_USED librsvg-2.0 cairo" + ], [ + AC_MSG_WARN([librsvg-2.0 >= 2.40.3 or cairo >= 1.2 not found; disabling SVG load via rsvg]) + with_rsvg=no + ]) fi # zlib @@ -1286,8 +1286,7 @@ if test x"$LIB_FUZZING_ENGINE" = x; then fi # Gather all up for VIPS_CFLAGS, VIPS_INCLUDES, VIPS_LIBS -VIPS_CFLAGS="$VIPS_CFLAGS $GTHREAD_CFLAGS $GIO_CFLAGS $REQUIRED_CFLAGS $EXPAT_CFLAGS $ZLIB_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $JPEG_CFLAGS $SPNG_CFLAGS $PNG_CFLAGS $IMAGEQUANT_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 $HEIF_CFLAGS" -VIPS_CFLAGS="$VIPS_DEBUG_FLAGS $VIPS_CFLAGS" +VIPS_CFLAGS="$VIPS_CFLAGS $GTHREAD_CFLAGS $GIO_CFLAGS $REQUIRED_CFLAGS $EXPAT_CFLAGS $ZLIB_CFLAGS $PANGOFT2_CFLAGS $GSF_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $JPEG_CFLAGS $SPNG_CFLAGS $PNG_CFLAGS $IMAGEQUANT_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $LIBWEBPMUX_CFLAGS $GIFLIB_INCLUDES $RSVG_CFLAGS $PDFIUM_CFLAGS $POPPLER_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS $HEIF_CFLAGS" VIPS_CFLAGS="$VIPS_DEBUG_FLAGS $VIPS_CFLAGS" VIPS_INCLUDES="$ZLIB_INCLUDES $PNG_INCLUDES $TIFF_INCLUDES $JPEG_INCLUDES $NIFTI_INCLUDES" VIPS_LIBS="$ZLIB_LIBS $HEIF_LIBS $MAGICK_LIBS $SPNG_LIBS $PNG_LIBS $IMAGEQUANT_LIBS $TIFF_LIBS $JPEG_LIBS $GTHREAD_LIBS $GIO_LIBS $REQUIRED_LIBS $EXPAT_LIBS $PANGOFT2_LIBS $GSF_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $GIFLIB_LIBS $RSVG_LIBS $NIFTI_LIBS $PDFIUM_LIBS $POPPLER_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $LIBWEBPMUX_LIBS $MATIO_LIBS $EXIF_LIBS -lm" diff --git a/libvips/conversion/autorot.c b/libvips/conversion/autorot.c index b297dc95..9cd92df0 100644 --- a/libvips/conversion/autorot.c +++ b/libvips/conversion/autorot.c @@ -9,6 +9,9 @@ * 10/5/20 * - handle mirrored images * - deprecate vips_autorot_get_angle() + * 24/10/20 + * - only remove main image orientation, since we don't rotate the + * embedded thumbnail */ /* @@ -69,8 +72,7 @@ static void * vips_autorot_remove_angle_sub( VipsImage *image, const char *field, GValue *value, void *my_data ) { - if( vips_isprefix( "exif-", field ) && - vips_ispostfix( field, "-Orientation" ) ) { + if( strcmp( field, "exif-ifd0-Orientation" ) == 0 ) { #ifdef DEBUG printf( "vips_autorot_remove_angle: %s\n", field ); #endif /*DEBUG*/ diff --git a/libvips/foreign/cairo.c b/libvips/foreign/cairo.c index b242ab49..753d0386 100644 --- a/libvips/foreign/cairo.c +++ b/libvips/foreign/cairo.c @@ -35,32 +35,59 @@ #include #include -/* Convert from ARGB to RGBA and undo premultiplication. +/* Convert from Cairo-style premultiplied BGRA to RGBA. * * See also openslide's argb2rgba(). */ void -vips__cairo2rgba( guint32 * restrict buf, int n ) +vips__premultiplied_bgra2rgba( guint32 * restrict p, int n ) { - int i; + int x; - for( i = 0; i < n; i++ ) { - guint32 * restrict p = buf + i; - guint32 x = *p; - guint8 a = x >> 24; - VipsPel * restrict out = (VipsPel *) p; + for( x = 0; x < n; x++ ) { + guint32 bgra = GUINT32_FROM_BE( p[x] ); + guint8 a = bgra & 0xff; - if( a == 255 ) - *p = GUINT32_TO_BE( (x << 8) | 255 ); - else if( a == 0 ) - *p = GUINT32_TO_BE( x << 8 ); - else { - /* Undo premultiplication. - */ - out[0] = 255 * ((x >> 16) & 255) / a; - out[1] = 255 * ((x >> 8) & 255) / a; - out[2] = 255 * (x & 255) / a; - out[3] = a; - } + guint32 rgba; + + if( a == 0 || + a == 255 ) + rgba = + (bgra & 0x00ff00ff) | + (bgra & 0x0000ff00) << 16 | + (bgra & 0xff000000) >> 16; + else + /* Undo premultiplication. + */ + rgba = + ((255 * ((bgra >> 8) & 0xff) / a) << 24) | + ((255 * ((bgra >> 16) & 0xff) / a) << 16) | + ((255 * ((bgra >> 24) & 0xff) / a) << 8) | + a; + + p[x] = GUINT32_TO_BE( rgba ); } } + +/* Convert from PDFium-style BGRA to RGBA. + */ +void +vips__bgra2rgba( guint32 * restrict p, int n ) +{ + int x; + + for( x = 0; x < n; x++ ) { + guint32 bgra = GUINT32_FROM_BE( p[x] ); + + guint rgba; + + /* Leave G and A, swap R and B. + */ + rgba = + (bgra & 0x00ff00ff) | + (bgra & 0x0000ff00) << 16 | + (bgra & 0xff000000) >> 16; + + p[x] = GUINT32_TO_BE( rgba ); + } +} diff --git a/libvips/foreign/exif.c b/libvips/foreign/exif.c index 3727af04..521e142f 100644 --- a/libvips/foreign/exif.c +++ b/libvips/foreign/exif.c @@ -158,10 +158,18 @@ show_values( ExifData *data ) * their default value and we won't know about it. */ static ExifData * -vips_exif_load_data_without_fix( const void *data, int length ) +vips_exif_load_data_without_fix( const void *data, size_t length ) { ExifData *ed; + /* exif_data_load_data() only allows uint for length. Limit it to less + * than that: 2**20 should be enough for anyone. + */ + if( length > 1 << 20 ) { + vips_error( "exif", "%s", _( "exif too large" ) ); + return( NULL ); + } + if( !(ed = exif_data_new()) ) { vips_error( "exif", "%s", _( "unable to init exif" ) ); return( NULL ); diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 37e92768..2e6b29d2 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -2174,7 +2174,7 @@ vips_foreign_operation_init( void ) vips_foreign_save_rad_target_get_type(); #endif /*HAVE_RADIANCE*/ -#if defined(HAVE_POPPLER) +#ifdef HAVE_POPPLER vips_foreign_load_pdf_file_get_type(); vips_foreign_load_pdf_buffer_get_type(); vips_foreign_load_pdf_source_get_type(); @@ -2183,6 +2183,7 @@ vips_foreign_operation_init( void ) #ifdef HAVE_PDFIUM vips_foreign_load_pdf_file_get_type(); vips_foreign_load_pdf_buffer_get_type(); + vips_foreign_load_pdf_source_get_type(); #endif /*HAVE_PDFIUM*/ #ifdef HAVE_RSVG diff --git a/libvips/foreign/heifload.c b/libvips/foreign/heifload.c index 2c40b01d..5b0b7160 100644 --- a/libvips/foreign/heifload.c +++ b/libvips/foreign/heifload.c @@ -68,12 +68,33 @@ #include #include -#ifdef HAVE_HEIF_DECODER +/* These are shared with the encoder. + */ +#if defined(HAVE_HEIF_DECODER) || defined(HAVE_HEIF_ENCODER) #include #include "pforeign.h" +void +vips__heif_error( struct heif_error *error ) +{ + if( error->code ) + vips_error( "heif", "%s (%d.%d)", error->message, error->code, + error->subcode ); +} + +const char *vips__heif_suffs[] = { + ".heic", + ".heif", + ".avif", + NULL +}; + +#endif /*defined(DECODE) || defined(ENCODE)*/ + +#ifdef HAVE_HEIF_DECODER + #define VIPS_TYPE_FOREIGN_LOAD_HEIF (vips_foreign_load_heif_get_type()) #define VIPS_FOREIGN_LOAD_HEIF( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ @@ -200,14 +221,6 @@ vips_foreign_load_heif_dispose( GObject *gobject ) dispose( gobject ); } -void -vips__heif_error( struct heif_error *error ) -{ - if( error->code ) - vips_error( "heif", "%s (%d.%d)", error->message, error->code, - error->subcode ); -} - static int vips_foreign_load_heif_build( VipsObject *object ) { @@ -258,7 +271,7 @@ static const char *heif_magic[] = { * * enum heif_filetype_result result = heif_check_filetype( buf, 12 ); * - * but it's very conservative and seems to be missing some of the Nokia hief + * but it's very conservative and seems to be missing some of the Nokia heif * types. */ static int @@ -269,7 +282,10 @@ vips_foreign_load_heif_is_a( const char *buf, int len ) int i; - if( chunk_len > 32 || + /* We've seen real files with 36 here, so 64 should be + * plenty. + */ + if( chunk_len > 64 || chunk_len % 4 != 0 ) return( 0 ); @@ -449,7 +465,7 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out ) char name[256]; #ifdef DEBUG - printf( "metadata type = %s, length = %zd\n", type, length ); + printf( "metadata type = %s, length = %zu\n", type, length ); #endif /*DEBUG*/ if( !(data = VIPS_ARRAY( out, length, unsigned char )) ) @@ -464,7 +480,8 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out ) /* We need to skip the first four bytes of EXIF, they just * contain the offset. */ - if( g_ascii_strcasecmp( type, "exif" ) == 0 ) { + if( length > 4 && + g_ascii_strcasecmp( type, "exif" ) == 0 ) { data += 4; length -= 4; } @@ -477,6 +494,7 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out ) if( g_ascii_strcasecmp( type, "exif" ) == 0 ) vips_snprintf( name, 256, VIPS_META_EXIF_NAME ); else if( g_ascii_strcasecmp( type, "mime" ) == 0 && + length > 10 && vips_isprefix( "signature == MagickSignature ); @@ -328,7 +331,43 @@ magick_import_pixels( Image *image, const ssize_t x, const ssize_t y, if( !constitute_image ) return( MagickFalse ); + /* image needs to inherit these fields from constitute_image. + */ + switch( type ) { + case CharPixel: + storage_type_depth = sizeof( unsigned char ) * 8; + break; + + case ShortPixel: + storage_type_depth = sizeof( unsigned short ) * 8; + break; + + case IntegerPixel: + storage_type_depth = sizeof( unsigned short ) * 8; + break; + + case LongPixel: + storage_type_depth = sizeof( unsigned long ) * 8; + break; + + case FloatPixel: + storage_type_depth = sizeof( float ) * 8; + break; + + case DoublePixel: + storage_type_depth = sizeof( double ) * 8; + break; + + default: + storage_type_depth = QuantumDepth; + break; + + } + image->depth = VIPS_MIN( storage_type_depth, QuantumDepth ); + image->matte = constitute_image->matte; + (void) CompositeImage( image, CopyCompositeOp, constitute_image, x, y ); + DestroyImage( constitute_image ); return( image->exception.severity == UndefinedException ); diff --git a/libvips/foreign/pdfiumload.c b/libvips/foreign/pdfiumload.c index c18e9521..7d1c33c8 100644 --- a/libvips/foreign/pdfiumload.c +++ b/libvips/foreign/pdfiumload.c @@ -8,6 +8,10 @@ * - shut down the input file as soon as we can [kleisauke] * 8/8/19 * - add locks, since pdfium is not threadsafe in any way + * 13/10/20 + * - have a lock just for pdfium [DarthSim] + * - update for current pdfium + * - add _source input */ /* @@ -39,12 +43,41 @@ /* TODO * - * - needs the reopen-after-minimise system that pdfload has, but we'll need - * to be able to actually build and test this before we can do that - * - what about filename encodings + * - what about filename encodings? * - need to test on Windows */ +/* How to build against PDFium: + * + * Download the prebuilt binary from: + * + * https://github.com/bblanchon/pdfium-binaries + * + * Untar to the libvips install prefix, for example: + * + * cd ~/vips + * tar xf ~/pdfium-linux.tgz + * + * Create a pdfium.pc like this (update the version number): + * + +VIPSHOME=/home/john/vips +cat > $VIPSHOME/lib/pkgconfig/pdfium.pc << EOF + prefix=$VIPSHOME + exec_prefix=\${prefix} + libdir=\${exec_prefix}/lib + includedir=\${prefix}/include + Name: pdfium + Description: pdfium + Version: 4290 + Requires: + Libs: -L\${libdir} -lpdfium + Cflags: -I\${includedir} +EOF + + * + */ + /* #define DEBUG */ @@ -73,6 +106,10 @@ typedef struct _VipsForeignLoadPdf { VipsForeignLoad parent_object; + /* Set by subclasses. + */ + VipsSource *source; + /* Load this page. */ int page_no; @@ -93,8 +130,9 @@ typedef struct _VipsForeignLoadPdf { */ VipsArrayDouble *background; - FPDF_DOCUMENT *doc; - FPDF_PAGE *page; + FPDF_FILEACCESS file_access; + FPDF_DOCUMENT doc; + FPDF_PAGE page; int current_page; /* Doc has this many pages. @@ -128,6 +166,8 @@ static char *vips_pdfium_errors[] = { "page not found or content error" }; +static GMutex *vips_pdfium_mutex = NULL; + static void vips_pdfium_error( void ) { @@ -143,12 +183,12 @@ vips_pdfium_error( void ) static void vips_foreign_load_pdf_close( VipsForeignLoadPdf *pdf ) { - g_mutex_lock( vips__global_lock ); + g_mutex_lock( vips_pdfium_mutex ); VIPS_FREEF( FPDF_ClosePage, pdf->page ); VIPS_FREEF( FPDF_CloseDocument, pdf->doc ); - g_mutex_unlock( vips__global_lock ); + g_mutex_unlock( vips_pdfium_mutex ); } static void @@ -177,18 +217,82 @@ vips_pdfium_init_cb( void *dummy ) return( NULL ); } +/* This is the m_GetBlock function for FPDF_FILEACCESS. + */ +static gboolean +vips_pdfium_GetBlock( void *param, + unsigned long position, unsigned char *pBuf, unsigned long size ) +{ + VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) param; + + /* PDFium guarantees these. + */ + g_assert( size > 0 ); + g_assert( position >= 0 ); + g_assert( position + size <= pdf->file_access.m_FileLen ); + + if( vips_source_seek( pdf->source, position, SEEK_SET ) < 0 ) + return( FALSE ); + + while( size > 0 ) { + size_t n_read; + + if( (n_read = vips_source_read( pdf->source, pBuf, size )) < 0 ) + return( FALSE ); + pBuf += n_read; + size -= n_read; + } + + return( TRUE ); +} + static int vips_foreign_load_pdf_build( VipsObject *object ) { static GOnce once = G_ONCE_INIT; VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) object; + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( pdf ); + + gint64 length; VIPS_ONCE( &once, vips_pdfium_init_cb, NULL ); if( !vips_object_argument_isset( object, "scale" ) ) pdf->scale = pdf->dpi / 72.0; + /* pdfium must know the file length, unfortunately. + */ + if( pdf->source ) { + if( (length = vips_source_length( pdf->source )) <= 0 ) + return( -1 ); + if( length > 1 << 30 ) { + vips_error( class->nickname, + _( "%s: too large for pdfium" ), + vips_connection_nick( + VIPS_CONNECTION( pdf->source ) ) ); + return( -1 ); + } + pdf->file_access.m_FileLen = length; + pdf->file_access.m_GetBlock = vips_pdfium_GetBlock; + pdf->file_access.m_Param = pdf; + + g_mutex_lock( vips_pdfium_mutex ); + + if( !(pdf->doc = FPDF_LoadCustomDocument( &pdf->file_access, + NULL )) ) { + g_mutex_unlock( vips_pdfium_mutex ); + vips_pdfium_error(); + vips_error( "pdfload", + _( "%s: unable to load" ), + vips_connection_nick( + VIPS_CONNECTION( pdf->source ) ) ); + return( -1 ); + } + + g_mutex_unlock( vips_pdfium_mutex ); + } + if( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )-> build( object ) ) return( -1 ); @@ -217,7 +321,7 @@ vips_foreign_load_pdf_get_page( VipsForeignLoadPdf *pdf, int page_no ) if( pdf->current_page != page_no ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( pdf ); - g_mutex_lock( vips__global_lock ); + g_mutex_lock( vips_pdfium_mutex ); VIPS_FREEF( FPDF_ClosePage, pdf->page ); pdf->current_page = -1; @@ -227,7 +331,7 @@ vips_foreign_load_pdf_get_page( VipsForeignLoadPdf *pdf, int page_no ) #endif /*DEBUG*/ if( !(pdf->page = FPDF_LoadPage( pdf->doc, page_no )) ) { - g_mutex_unlock( vips__global_lock ); + g_mutex_unlock( vips_pdfium_mutex ); vips_pdfium_error(); vips_error( class->nickname, _( "unable to load page %d" ), page_no ); @@ -235,7 +339,7 @@ vips_foreign_load_pdf_get_page( VipsForeignLoadPdf *pdf, int page_no ) } pdf->current_page = page_no; - g_mutex_unlock( vips__global_lock ); + g_mutex_unlock( vips_pdfium_mutex ); } return( 0 ); @@ -278,6 +382,8 @@ vips_foreign_load_pdf_set_image( VipsForeignLoadPdf *pdf, VipsImage *out ) vips_image_set_int( out, "pdf-n_pages", pdf->n_pages ); vips_image_set_int( out, VIPS_META_N_PAGES, pdf->n_pages ); + g_mutex_lock( vips_pdfium_mutex ); + for( i = 0; i < n_metadata; i++ ) { VipsForeignLoadPdfMetadata *metadata = &vips_foreign_load_pdf_metadata[i]; @@ -285,9 +391,7 @@ vips_foreign_load_pdf_set_image( VipsForeignLoadPdf *pdf, VipsImage *out ) char text[1024]; int len; - g_mutex_lock( vips__global_lock ); len = FPDF_GetMetaText( pdf->doc, metadata->tag, text, 1024 ); - g_mutex_unlock( vips__global_lock ); if( len > 0 ) { char *str; @@ -302,6 +406,8 @@ vips_foreign_load_pdf_set_image( VipsForeignLoadPdf *pdf, VipsImage *out ) } } + g_mutex_unlock( vips_pdfium_mutex ); + /* We need pixels/mm for vips. */ res = pdf->dpi / 25.4; @@ -327,9 +433,9 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load ) printf( "vips_foreign_load_pdf_header: %p\n", pdf ); #endif /*DEBUG*/ - g_mutex_lock( vips__global_lock ); + g_mutex_lock( vips_pdfium_mutex ); pdf->n_pages = FPDF_GetPageCount( pdf->doc ); - g_mutex_unlock( vips__global_lock ); + g_mutex_unlock( vips_pdfium_mutex ); /* @n == -1 means until the end of the doc. */ @@ -400,10 +506,7 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load ) static void vips_foreign_load_pdf_minimise( VipsObject *object, VipsForeignLoadPdf *pdf ) { - /* In seq mode, we can shut down the input at the end of computation. - */ - if( VIPS_FOREIGN_LOAD( pdf )->access == VIPS_ACCESS_SEQUENTIAL ) - vips_foreign_load_pdf_close( pdf ); + vips_source_minimise( pdf->source ); } static int @@ -411,7 +514,6 @@ vips_foreign_load_pdf_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) a; - VipsForeignLoad *load = (VipsForeignLoad *) pdf; VipsRect *r = &or->valid; int top; @@ -448,7 +550,7 @@ vips_foreign_load_pdf_generate( VipsRegion *or, /* 4 means RGBA. */ - g_mutex_lock( vips__global_lock ); + g_mutex_lock( vips_pdfium_mutex ); bitmap = FPDFBitmap_CreateEx( rect.width, rect.height, 4, VIPS_REGION_ADDR( or, rect.left, rect.top ), @@ -460,26 +562,18 @@ vips_foreign_load_pdf_generate( VipsRegion *or, FPDFBitmap_Destroy( bitmap ); - g_mutex_unlock( vips__global_lock ); + g_mutex_unlock( vips_pdfium_mutex ); top += rect.height; i += 1; } - /* PDFium writes BRGA, we must swap. - * - * FIXME ... this is a bit slow. + /* PDFium writes BGRA, we must swap. */ - 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; - } - } + for( y = 0; y < r->height; y++ ) + vips__bgra2rgba( + (guint32 *) VIPS_REGION_ADDR( or, r->left, r->top + y ), + r->width ); return( 0 ); } @@ -522,24 +616,40 @@ vips_foreign_load_pdf_load( VipsForeignLoad *load ) return( 0 ); } +static void * +vips_foreign_load_pdf_once_init( void *client ) +{ + /* We must make the mutex on class init (not _build) since we + * can lock ebven if build is not called. + */ + vips_pdfium_mutex = vips_g_mutex_new(); + + return( NULL ); +} + static void vips_foreign_load_pdf_class_init( VipsForeignLoadPdfClass *class ) { + static GOnce once = G_ONCE_INIT; + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + VIPS_ONCE( &once, vips_foreign_load_pdf_once_init, NULL ); + 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->description = _( "load PDF with PDFium" ); 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->header = vips_foreign_load_pdf_header; load_class->load = vips_foreign_load_pdf_load; VIPS_ARG_INT( class, "page", 10, @@ -606,24 +716,12 @@ G_DEFINE_TYPE( VipsForeignLoadPdfFile, vips_foreign_load_pdf_file, static int vips_foreign_load_pdf_file_header( VipsForeignLoad *load ) { - VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load; VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) load; - g_mutex_lock( vips__global_lock ); - - if( !(pdf->doc = FPDF_LoadDocument( file->filename, NULL )) ) { - g_mutex_unlock( vips__global_lock ); - vips_pdfium_error(); - vips_error( "pdfload", - _( "unable to load \"%s\"" ), file->filename ); - return( -1 ); - } - - g_mutex_unlock( vips__global_lock ); - VIPS_SETSTR( load->out->filename, file->filename ); - return( vips_foreign_load_pdf_header( load ) ); + return( VIPS_FOREIGN_LOAD_CLASS( + vips_foreign_load_pdf_file_parent_class )->header( load ) ); } static const char *vips_foreign_pdf_suffs[] = { @@ -631,6 +729,24 @@ static const char *vips_foreign_pdf_suffs[] = { NULL }; +static int +vips_foreign_load_pdf_file_build( VipsObject *object ) +{ + VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) object; + VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) pdf; + +#ifdef DEBUG + printf( "vips_foreign_load_pdf_file_build: %s\n", file->filename ); +#endif /*DEBUG*/ + + if( file->filename && + !(pdf->source = vips_source_new_from_file( file->filename )) ) + return( -1 ); + + return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_file_parent_class )-> + build( object ) ); +} + static void vips_foreign_load_pdf_file_class_init( VipsForeignLoadPdfFileClass *class ) @@ -644,6 +760,8 @@ vips_foreign_load_pdf_file_class_init( gobject_class->get_property = vips_object_get_property; object_class->nickname = "pdfload"; + object_class->description = _( "load PDF from file" ); + object_class->build = vips_foreign_load_pdf_file_build; foreign_class->suffs = vips_foreign_pdf_suffs; @@ -679,26 +797,19 @@ 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 ) +vips_foreign_load_pdf_buffer_build( VipsObject *object ) { - VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load; - VipsForeignLoadPdfBuffer *buffer = - (VipsForeignLoadPdfBuffer *) load; + VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) object; + VipsForeignLoadPdfBuffer *buffer = (VipsForeignLoadPdfBuffer *) pdf; - g_mutex_lock( vips__global_lock ); + if( buffer->buf && + !(pdf->source = vips_source_new_from_memory( + VIPS_AREA( buffer->buf )->data, + VIPS_AREA( buffer->buf )->length )) ) + return( -1 ); - if( !(pdf->doc = FPDF_LoadMemDocument( buffer->buf->data, - buffer->buf->length, NULL )) ) { - g_mutex_unlock( vips__global_lock ); - vips_pdfium_error(); - vips_error( "pdfload", - "%s", _( "unable to load from buffer" ) ); - return( -1 ); - } - - g_mutex_unlock( vips__global_lock ); - - return( vips_foreign_load_pdf_header( load ) ); + return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_buffer_parent_class )-> + build( object ) ); } static void @@ -713,9 +824,10 @@ vips_foreign_load_pdf_buffer_class_init( gobject_class->get_property = vips_object_get_property; object_class->nickname = "pdfload_buffer"; + object_class->description = _( "load PDF from buffer" ); + object_class->build = vips_foreign_load_pdf_buffer_build; 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" ), @@ -731,4 +843,74 @@ vips_foreign_load_pdf_buffer_init( VipsForeignLoadPdfBuffer *buffer ) { } +typedef struct _VipsForeignLoadPdfSource { + VipsForeignLoadPdf parent_object; + + VipsSource *source; + +} VipsForeignLoadPdfSource; + +typedef VipsForeignLoadPdfClass VipsForeignLoadPdfSourceClass; + +G_DEFINE_TYPE( VipsForeignLoadPdfSource, vips_foreign_load_pdf_source, + vips_foreign_load_pdf_get_type() ); + +static int +vips_foreign_load_pdf_source_build( VipsObject *object ) +{ + VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) object; + VipsForeignLoadPdfSource *source = (VipsForeignLoadPdfSource *) pdf; + + if( source->source ) { + pdf->source = source->source; + g_object_ref( pdf->source ); + } + + return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_source_parent_class )-> + build( object ) ); +} + +static gboolean +vips_foreign_load_pdf_source_is_a_source( VipsSource *source ) +{ + const unsigned char *p; + + return( (p = vips_source_sniff( source, 4 )) && + p[0] == '%' && + p[1] == 'P' && + p[2] == 'D' && + p[3] == 'F' ); +} + +static void +vips_foreign_load_pdf_source_class_init( + VipsForeignLoadPdfSourceClass *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_source"; + object_class->description = _( "load PDF from source" ); + object_class->build = vips_foreign_load_pdf_source_build; + + load_class->is_a_source = vips_foreign_load_pdf_source_is_a_source; + + VIPS_ARG_OBJECT( class, "source", 1, + _( "Source" ), + _( "Source to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadPdfSource, source ), + VIPS_TYPE_SOURCE ); + +} + +static void +vips_foreign_load_pdf_source_init( VipsForeignLoadPdfSource *source ) +{ +} + #endif /*HAVE_PDFIUM*/ diff --git a/libvips/foreign/pdfload.c b/libvips/foreign/pdfload.c index 7729a8b0..0a497510 100644 --- a/libvips/foreign/pdfload.c +++ b/libvips/foreign/pdfload.c @@ -220,8 +220,7 @@ vips_foreign_load_pdf_get_page( VipsForeignLoadPdf *pdf, int page_no ) printf( "vips_foreign_load_pdf_get_page: %d\n", page_no ); #endif /*DEBUG*/ - if( !(pdf->page = poppler_document_get_page( pdf->doc, - page_no )) ) { + if( !(pdf->page = poppler_document_get_page( pdf->doc, page_no )) ) { vips_error( class->nickname, _( "unable to load page %d" ), page_no ); return( -1 ); @@ -368,7 +367,7 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load ) /* Convert the background to the image format. * * FIXME ... we probably should convert this to pre-multiplied BGRA - * to match the Cairo convention. See vips__cairo2rgba(). + * to match the Cairo convention. See vips__premultiplied_bgra2rgba(). */ if( !(pdf->ink = vips__vector_to_ink( class->nickname, load->out, @@ -453,7 +452,7 @@ vips_foreign_load_pdf_generate( VipsRegion *or, /* Cairo makes pre-multipled BRGA, we must byteswap and unpremultiply. */ for( y = 0; y < r->height; y++ ) - vips__cairo2rgba( + vips__premultiplied_bgra2rgba( (guint32 *) VIPS_REGION_ADDR( or, r->left, r->top + y ), r->width ); diff --git a/libvips/foreign/svgload.c b/libvips/foreign/svgload.c index d8d06a38..4a3b58dc 100644 --- a/libvips/foreign/svgload.c +++ b/libvips/foreign/svgload.c @@ -368,7 +368,7 @@ vips_foreign_load_svg_generate( VipsRegion *or, /* Cairo makes pre-multipled BRGA -- we must byteswap and unpremultiply. */ for( y = 0; y < r->height; y++ ) - vips__cairo2rgba( + vips__premultiplied_bgra2rgba( (guint32 *) VIPS_REGION_ADDR( or, r->left, r->top + y ), r->width ); diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index bb2b6c48..309130a8 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -81,6 +81,7 @@ /* What we track during a read. */ typedef struct { + VipsImage *out; VipsSource *source; /* The data we load, as a webp object. @@ -340,14 +341,21 @@ read_free( Read *read ) return( 0 ); } +static void +read_close_cb( VipsImage *image, Read *read ) +{ + read_free( read ); +} + static Read * -read_new( VipsSource *source, int page, int n, double scale ) +read_new( VipsImage *out, VipsSource *source, int page, int n, double scale ) { Read *read; if( !(read = VIPS_NEW( NULL, Read )) ) return( NULL ); + read->out = out; read->source = source; g_object_ref( source ); read->page = page; @@ -359,15 +367,19 @@ read_new( VipsSource *source, int page, int n, double scale ) read->dispose_method = WEBP_MUX_DISPOSE_NONE; read->frame_no = 0; + /* Everything has to stay open until read has finished, unfortunately, + * since webp relies on us mapping the whole file. + */ + g_signal_connect( out, "close", + G_CALLBACK( read_close_cb ), read ); + WebPInitDecoderConfig( &read->config ); read->config.options.use_threads = 1; read->config.output.is_external_memory = 1; if( !(read->data.bytes = - vips_source_map( source, &read->data.size )) ) { - read_free( read ); + vips_source_map( source, &read->data.size )) ) return( NULL ); - } return( read ); } @@ -774,16 +786,10 @@ vips__webp_read_header_source( VipsSource *source, VipsImage *out, { Read *read; - if( !(read = read_new( source, page, n, scale )) ) + if( !(read = read_new( out, source, page, n, scale )) || + read_header( read, out ) ) return( -1 ); - if( read_header( read, out ) ) { - read_free( read ); - return( -1 ); - } - - read_free( read ); - return( 0 ); } @@ -793,16 +799,10 @@ vips__webp_read_source( VipsSource *source, VipsImage *out, { Read *read; - if( !(read = read_new( source, page, n, scale )) ) + if( !(read = read_new( out, source, page, n, scale )) || + read_image( read, out ) ) return( -1 ); - if( read_image( read, out ) ) { - read_free( read ); - return( -1 ); - } - - read_free( read ); - return( 0 ); } diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index d549c321..d41cdb42 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -236,7 +236,8 @@ int vips__byteswap_bool( VipsImage *in, VipsImage **out, gboolean swap ); char *vips__xml_properties( VipsImage *image ); -void vips__cairo2rgba( guint32 *buf, int n ); +void vips__premultiplied_bgra2rgba( guint32 * restrict p, int n ); +void vips__bgra2rgba( guint32 * restrict p, int n ); void vips__Lab2LabQ_vec( VipsPel *out, float *in, int width ); void vips__LabQ2Lab_vec( float *out, VipsPel *in, int width ); diff --git a/libvips/iofuncs/source.c b/libvips/iofuncs/source.c index cd0eb73c..4989e458 100644 --- a/libvips/iofuncs/source.c +++ b/libvips/iofuncs/source.c @@ -260,6 +260,8 @@ vips_source_finalize( GObject *gobject ) { VipsSource *source = VIPS_SOURCE( gobject ); + VIPS_DEBUG_MSG( "vips_source_finalize: %p\n", source ); + VIPS_FREEF( g_byte_array_unref, source->header_bytes ); VIPS_FREEF( g_byte_array_unref, source->sniff ); if( source->mmap_baseaddr ) { @@ -555,8 +557,6 @@ vips_source_minimise( VipsSource *source ) { VipsConnection *connection = VIPS_CONNECTION( source ); - VIPS_DEBUG_MSG( "vips_source_minimise:\n" ); - SANITY( source ); (void) vips_source_test_features( source ); @@ -565,7 +565,7 @@ vips_source_minimise( VipsSource *source ) connection->descriptor != -1 && connection->tracked_descriptor == connection->descriptor && !source->is_pipe ) { - VIPS_DEBUG_MSG( " tracked_close()\n" ); + VIPS_DEBUG_MSG( "vips_source_minimise:\n" ); vips_tracked_close( connection->tracked_descriptor ); connection->tracked_descriptor = -1; connection->descriptor = -1; @@ -590,13 +590,13 @@ vips_source_unminimise( VipsSource *source ) { VipsConnection *connection = VIPS_CONNECTION( source ); - VIPS_DEBUG_MSG( "vips_source_unminimise:\n" ); - if( connection->descriptor == -1 && connection->tracked_descriptor == -1 && connection->filename ) { int fd; + VIPS_DEBUG_MSG( "vips_source_unminimise: %p\n", source ); + if( (fd = vips_tracked_open( connection->filename, MODE_READ, 0 )) == -1 ) { vips_error_system( errno, diff --git a/test/test_connections.c b/test/test_connections.c index 61366b06..d59571c5 100644 --- a/test/test_connections.c +++ b/test/test_connections.c @@ -10,7 +10,7 @@ typedef struct _MyInput { const char *filename; - const unsigned char *contents; + unsigned char *contents; size_t length; size_t read_position; } MyInput; @@ -106,7 +106,7 @@ main( int argc, char **argv ) VipsTargetCustom *target_custom; VipsImage *image; - if( VIPS_INIT( NULL ) ) + if( VIPS_INIT( argv[0] ) ) return( -1 ); if( argc != 3 ) @@ -153,6 +153,7 @@ main( int argc, char **argv ) VIPS_UNREF( image ); VIPS_UNREF( source_custom ); VIPS_UNREF( target_custom ); + g_free( my_input.contents ); return( 0 ); }