diff --git a/ChangeLog b/ChangeLog index 9beb148d..5e1a2bc6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,7 +6,7 @@ - tiffsave has a "depth" param to set max pyr depth - libtiff LOGLUV images load and save as libvips XYZ - add gifload_source, csvload_source, csvsave_target, matrixload_source, - matrixsave_source + matrixsave_source, pdfload_source - revise vipsthumbnail flags - add VIPS_LEAK env var - add vips_pipe_read_limit_set(), --vips-pipe-read-limit, diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 41574ee1..f2369a7b 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -2097,9 +2097,9 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_webp_buffer_get_type( void ); extern GType vips_foreign_save_webp_target_get_type( void ); - extern GType vips_foreign_load_pdf_get_type( void ); extern GType vips_foreign_load_pdf_file_get_type( void ); extern GType vips_foreign_load_pdf_buffer_get_type( void ); + extern GType vips_foreign_load_pdf_source_get_type( void ); extern GType vips_foreign_load_svg_file_get_type( void ); extern GType vips_foreign_load_svg_buffer_get_type( void ); @@ -2151,14 +2151,13 @@ vips_foreign_operation_init( void ) vips_foreign_save_rad_target_get_type(); #endif /*HAVE_RADIANCE*/ -#ifdef HAVE_POPPLER - vips_foreign_load_pdf_get_type(); +#if defined(HAVE_POPPLER) && defined(HAVE_GIO) vips_foreign_load_pdf_file_get_type(); vips_foreign_load_pdf_buffer_get_type(); + vips_foreign_load_pdf_source_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*/ diff --git a/libvips/foreign/pdfload.c b/libvips/foreign/pdfload.c index 087b0428..481981e9 100644 --- a/libvips/foreign/pdfload.c +++ b/libvips/foreign/pdfload.c @@ -14,6 +14,8 @@ * - shut down the input file as soon as we can * 19/9/19 * - reopen the input if we minimised too early + * 11/3/20 + * - move on top of VipsSource */ /* @@ -63,7 +65,10 @@ #include "pforeign.h" -#ifdef HAVE_POPPLER +/* TODO ... put minimise support back in. + */ + +#if defined(HAVE_POPPLER) && defined(HAVE_GIO) #include #include @@ -86,6 +91,12 @@ typedef struct _VipsForeignLoadPdf { VipsForeignLoad parent_object; + /* The VipsSource we load from, and the GInputStream we wrap around + * it. Set from subclasses. + */ + VipsSource *source; + GInputStream *stream; + /* Load this page. */ int page_no; @@ -133,9 +144,6 @@ typedef struct _VipsForeignLoadPdf { typedef struct _VipsForeignLoadPdfClass { VipsForeignLoadClass parent_class; - int (*open)( VipsForeignLoadPdf *pdf ); - - void (*close)( VipsForeignLoadPdf *pdf ); } VipsForeignLoadPdfClass; G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadPdf, vips_foreign_load_pdf, @@ -145,9 +153,11 @@ static void vips_foreign_load_pdf_dispose( GObject *gobject ) { VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( gobject ); - VipsForeignLoadPdfClass *class = VIPS_FOREIGN_LOAD_PDF_GET_CLASS( pdf ); - class->close( pdf ); + VIPS_UNREF( pdf->page ); + VIPS_UNREF( pdf->doc ); + VIPS_UNREF( pdf->source ); + VIPS_UNREF( pdf->stream ); G_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )-> dispose( gobject ); @@ -158,9 +168,18 @@ vips_foreign_load_pdf_build( VipsObject *object ) { VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object ); + GError *error = NULL; + if( !vips_object_argument_isset( object, "scale" ) ) pdf->scale = pdf->dpi / 72.0; + pdf->stream = vips_g_input_stream_new_from_source( pdf->source ); + if( !(pdf->doc = poppler_document_new_from_stream( pdf->stream, + vips_source_length( pdf->source ), NULL, NULL, &error )) ) { + vips_g_error( &error ); + return( -1 ); + } + if( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )-> build( object ) ) return( -1 ); @@ -274,8 +293,6 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( load ); - VipsForeignLoadPdfClass *pdf_class = - VIPS_FOREIGN_LOAD_PDF_GET_CLASS( pdf ); int top; int i; @@ -284,9 +301,6 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load ) printf( "vips_foreign_load_pdf_header: %p\n", pdf ); #endif /*DEBUG*/ - if( pdf_class->open( pdf ) ) - return( -1 ); - pdf->n_pages = poppler_document_get_n_pages( pdf->doc ); /* @n == -1 means until the end of the doc. @@ -357,7 +371,7 @@ vips_foreign_load_pdf_header( VipsForeignLoad *load ) VIPS_AREA( pdf->background )->n )) ) return( -1 ); - pdf_class->close( pdf ); + vips_source_minimise( pdf->source ); return( 0 ); } @@ -367,7 +381,6 @@ vips_foreign_load_pdf_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( a ); - VipsForeignLoadPdfClass *class = VIPS_FOREIGN_LOAD_PDF_GET_CLASS( pdf ); VipsRect *r = &or->valid; int top; @@ -380,11 +393,6 @@ vips_foreign_load_pdf_generate( VipsRegion *or, r->left, r->top, r->width, r->height ); */ - /* We may have been minimised. Make sure the doc is open. - */ - if( class->open( pdf ) ) - return( -1 ); - /* Poppler won't always paint the background. */ vips_region_paint_pel( or, r, pdf->ink ); @@ -445,7 +453,6 @@ static int vips_foreign_load_pdf_load( VipsForeignLoad *load ) { VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( load ); - VipsForeignLoadPdfClass *class = VIPS_FOREIGN_LOAD_PDF_GET_CLASS( pdf ); VipsImage **t = (VipsImage **) vips_object_local_array( (VipsObject *) load, 2 ); @@ -453,9 +460,6 @@ vips_foreign_load_pdf_load( VipsForeignLoad *load ) printf( "vips_foreign_load_pdf_load: %p\n", pdf ); #endif /*DEBUG*/ - if( class->open( pdf ) ) - return( -1 ); - /* Read to this image, then cache to out, see below. */ t[0] = vips_image_new(); @@ -484,23 +488,6 @@ vips_foreign_load_pdf_load( VipsForeignLoad *load ) return( 0 ); } -static int -vips_foreign_load_pdf_open( VipsForeignLoadPdf *pdf ) -{ - return( 0 ); -} - -static void -vips_foreign_load_pdf_close( VipsForeignLoadPdf *pdf ) -{ -#ifdef DEBUG - printf( "vips_foreign_load_pdf_file_close:\n" ); -#endif /*DEBUG*/ - - VIPS_UNREF( pdf->page ); - VIPS_UNREF( pdf->doc ); -} - static void vips_foreign_load_pdf_class_init( VipsForeignLoadPdfClass *class ) { @@ -522,9 +509,6 @@ vips_foreign_load_pdf_class_init( VipsForeignLoadPdfClass *class ) load_class->header = vips_foreign_load_pdf_header; load_class->load = vips_foreign_load_pdf_load; - class->open = vips_foreign_load_pdf_open; - class->close = vips_foreign_load_pdf_close; - VIPS_ARG_INT( class, "page", 20, _( "Page" ), _( "Load this page from the file" ), @@ -617,19 +601,18 @@ static const char *vips_foreign_pdf_suffs[] = { }; static int -vips_foreign_load_pdf_file_open( VipsForeignLoadPdf *pdf ) +vips_foreign_load_pdf_file_build( VipsObject *object ) { + VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object ); VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) pdf; - GError *error = NULL; - #ifdef DEBUG - printf( "vips_foreign_load_pdf_file_open: %s\n", file->filename ); + printf( "vips_foreign_load_pdf_file_build: %s\n", file->filename ); #endif /*DEBUG*/ - if( !file->uri && - file->filename ) { + if( file->filename ) { char *path; + GError *error = NULL; /* We need an absolute path for a URI. */ @@ -640,18 +623,15 @@ vips_foreign_load_pdf_file_open( VipsForeignLoadPdf *pdf ) return( -1 ); } g_free( path ); + + if( !(pdf->source = + vips_source_new_from_file( file->filename )) ) + return( -1 ); + } - if( !pdf->doc && - file->uri && - !(pdf->doc = poppler_document_new_from_file( - file->uri, NULL, &error )) ) { - vips_g_error( &error ); - return( -1 ); - } - - return( VIPS_FOREIGN_LOAD_PDF_CLASS( - vips_foreign_load_pdf_file_parent_class )->open( pdf ) ); + return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_file_parent_class )-> + build( object ) ); } static void @@ -668,14 +648,14 @@ 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; load_class->is_a = vips_foreign_load_pdf_is_a; load_class->header = vips_foreign_load_pdf_file_header; - class->open = vips_foreign_load_pdf_file_open; - VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "Filename to load from" ), @@ -705,21 +685,19 @@ G_DEFINE_TYPE( VipsForeignLoadPdfBuffer, vips_foreign_load_pdf_buffer, vips_foreign_load_pdf_get_type() ); static int -vips_foreign_load_pdf_buffer_open( VipsForeignLoadPdf *pdf ) +vips_foreign_load_pdf_buffer_build( VipsObject *object ) { + VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object ); VipsForeignLoadPdfBuffer *buffer = (VipsForeignLoadPdfBuffer *) pdf; - GError *error = NULL; + 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 && - !(pdf->doc = poppler_document_new_from_data( - buffer->buf->data, buffer->buf->length, NULL, &error )) ) { - vips_g_error( &error ); - return( -1 ); - } - - return( VIPS_FOREIGN_LOAD_PDF_CLASS( - vips_foreign_load_pdf_buffer_parent_class )->open( pdf ) ); + return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_buffer_parent_class )-> + build( object ) ); } static void @@ -734,11 +712,11 @@ 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; - class->open = vips_foreign_load_pdf_buffer_open; - VIPS_ARG_BOXED( class, "buffer", 1, _( "Buffer" ), _( "Buffer to load from" ), @@ -753,7 +731,77 @@ vips_foreign_load_pdf_buffer_init( VipsForeignLoadPdfBuffer *buffer ) { } -#endif /*HAVE_POPPLER*/ +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 = VIPS_FOREIGN_LOAD_PDF( 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 /*defined(HAVE_POPPLER) && defined(HAVE_GIO)*/ /* Also used by the pdfium loader. */ @@ -889,3 +937,36 @@ vips_pdfload_buffer( void *buf, size_t len, VipsImage **out, ... ) return( result ); } +/** + * vips_pdfload_source: + * @source: source to load from + * @out: (out): image to write + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * * @page: %gint, load this page, numbered from zero + * * @n: %gint, load this many pages + * * @dpi: %gdouble, render at this DPI + * * @scale: %gdouble, scale render by this factor + * * @background: #VipsArrayDouble background colour + * + * Exactly as vips_pdfload(), but read from a source. + * + * See also: vips_pdfload() + * + * Returns: 0 on success, -1 on error. + */ +int +vips_pdfload_source( VipsSource *source, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "pdfload_source", ap, source, out ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index d618cf7b..cf6fb329 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -610,6 +610,8 @@ int vips_pdfload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); int vips_pdfload_buffer( void *buf, size_t len, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_pdfload_source( VipsSource *source, VipsImage **out, ... ) + __attribute__((sentinel)); int vips_svgload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel));