pdfload reopens after minimise if necessary

We were using "minimise" to close pdf input early, but this will break
programs which make several output images from one sequential input
image. For example, loading all pages of a PDF as a toilet-roll image,
then saving pages as a set of PNGs.

This patch adds vfuncs for open and close, and makes _generate reopen
the input if necessary.

We will need similar patches for pdfiumload, gifload, gifnsload,
tiffload etc.

see https://github.com/libvips/libvips/issues/1370#issuecomment-533169856
This commit is contained in:
John Cupitt 2019-09-19 17:04:42 +01:00
parent 2472f52123
commit 0323b77666
1 changed files with 108 additions and 45 deletions

View File

@ -10,8 +10,10 @@
* - use a much larger strip size, thanks bubba
* 8/6/18
* - add background param
* 16/8/18
* - shut down the input file as soon as we can [kleisauke]
* 16/8/18 [kleisauke]
* - shut down the input file as soon as we can
* 19/9/19
* - reopen the input if we minimised too early
*/
/*
@ -66,6 +68,21 @@
#include <cairo.h>
#include <poppler.h>
#define VIPS_TYPE_FOREIGN_LOAD_PDF (vips_foreign_load_pdf_get_type())
#define VIPS_FOREIGN_LOAD_PDF( obj ) \
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
VIPS_TYPE_FOREIGN_LOAD_PDF, VipsForeignLoadPdf ))
#define VIPS_FOREIGN_LOAD_PDF_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_CAST( (klass), \
VIPS_TYPE_FOREIGN_LOAD_PDF, VipsForeignLoadPdfClass))
#define VIPS_IS_FOREIGN_LOAD_PDF( obj ) \
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_FOREIGN_LOAD_PDF ))
#define VIPS_IS_FOREIGN_LOAD_PDF_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_FOREIGN_LOAD_PDF ))
#define VIPS_FOREIGN_LOAD_PDF_GET_CLASS( obj ) \
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
VIPS_TYPE_FOREIGN_LOAD_PDF, VipsForeignLoadPdfClass ))
typedef struct _VipsForeignLoadPdf {
VipsForeignLoad parent_object;
@ -113,24 +130,24 @@ typedef struct _VipsForeignLoadPdf {
} VipsForeignLoadPdf;
typedef VipsForeignLoadClass VipsForeignLoadPdfClass;
typedef struct _VipsForeignLoadPdfClass {
VipsForeignLoadClass parent_class;
int (*open)( VipsForeignLoadPdf *pdf );
void (*close)( VipsForeignLoadPdf *pdf );
} VipsForeignLoadPdfClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadPdf, vips_foreign_load_pdf,
VIPS_TYPE_FOREIGN_LOAD );
static void
vips_foreign_load_pdf_close( VipsForeignLoadPdf *pdf )
{
VIPS_UNREF( pdf->page );
VIPS_UNREF( pdf->doc );
}
static void
vips_foreign_load_pdf_dispose( GObject *gobject )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) gobject;
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( gobject );
VipsForeignLoadPdfClass *class = VIPS_FOREIGN_LOAD_PDF_GET_CLASS( pdf );
vips_foreign_load_pdf_close( pdf );
class->close( pdf );
G_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )->
dispose( gobject );
@ -139,7 +156,7 @@ vips_foreign_load_pdf_dispose( GObject *gobject )
static int
vips_foreign_load_pdf_build( VipsObject *object )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) object;
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object );
if( !vips_object_argument_isset( object, "scale" ) )
pdf->scale = pdf->dpi / 72.0;
@ -255,7 +272,9 @@ static int
vips_foreign_load_pdf_header( VipsForeignLoad *load )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( load );
VipsForeignLoadPdfClass *pdf_class =
VIPS_FOREIGN_LOAD_PDF_GET_CLASS( pdf );
int top;
int i;
@ -264,6 +283,9 @@ 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.
@ -342,15 +364,20 @@ 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 );
if( VIPS_FOREIGN_LOAD( pdf )->access == VIPS_ACCESS_SEQUENTIAL ) {
VipsForeignLoadPdfClass *class =
VIPS_FOREIGN_LOAD_PDF_GET_CLASS( pdf );
class->close( pdf );
}
}
static int
vips_foreign_load_pdf_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) a;
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( a );
VipsForeignLoadPdfClass *class = VIPS_FOREIGN_LOAD_PDF_GET_CLASS( pdf );
VipsRect *r = &or->valid;
int top;
@ -363,6 +390,11 @@ 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 );
@ -422,7 +454,7 @@ vips_foreign_load_pdf_generate( VipsRegion *or,
static int
vips_foreign_load_pdf_load( VipsForeignLoad *load )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( load );
VipsImage **t = (VipsImage **)
vips_object_local_array( (VipsObject *) load, 2 );
@ -459,6 +491,19 @@ 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 )
{
VIPS_UNREF( pdf->page );
VIPS_UNREF( pdf->doc );
}
static void
vips_foreign_load_pdf_class_init( VipsForeignLoadPdfClass *class )
{
@ -479,6 +524,9 @@ vips_foreign_load_pdf_class_init( VipsForeignLoadPdfClass *class )
load_class->get_flags = vips_foreign_load_pdf_get_flags;
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" ),
@ -557,28 +605,8 @@ vips_foreign_load_pdf_file_dispose( GObject *gobject )
static int
vips_foreign_load_pdf_file_header( VipsForeignLoad *load )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) load;
char *path;
GError *error = NULL;
/* We need an absolute path for a URI.
*/
path = vips_realpath( file->filename );
if( !(file->uri = g_filename_to_uri( path, NULL, &error )) ) {
g_free( path );
vips_g_error( &error );
return( -1 );
}
g_free( path );
if( !(pdf->doc = poppler_document_new_from_file(
file->uri, NULL, &error )) ) {
vips_g_error( &error );
return( -1 );
}
VIPS_SETSTR( load->out->filename, file->filename );
return( vips_foreign_load_pdf_header( load ) );
@ -589,6 +617,38 @@ static const char *vips_foreign_pdf_suffs[] = {
NULL
};
static int
vips_foreign_load_pdf_file_open( VipsForeignLoadPdf *pdf )
{
VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) pdf;
GError *error = NULL;
if( !file->uri ) {
char *path;
/* We need an absolute path for a URI.
*/
path = vips_realpath( file->filename );
if( !(file->uri = g_filename_to_uri( path, NULL, &error )) ) {
g_free( path );
vips_g_error( &error );
return( -1 );
}
g_free( path );
}
if( !pdf->doc &&
!(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 ) );
}
static void
vips_foreign_load_pdf_file_class_init(
VipsForeignLoadPdfFileClass *class )
@ -609,6 +669,8 @@ vips_foreign_load_pdf_file_class_init(
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" ),
@ -638,21 +700,21 @@ 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_open( VipsForeignLoadPdf *pdf )
{
VipsForeignLoadPdf *pdf = (VipsForeignLoadPdf *) load;
VipsForeignLoadPdfBuffer *buffer =
(VipsForeignLoadPdfBuffer *) load;
VipsForeignLoadPdfBuffer *buffer = (VipsForeignLoadPdfBuffer *) pdf;
GError *error = NULL;
if( !(pdf->doc = poppler_document_new_from_data(
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_header( load ) );
return( VIPS_FOREIGN_LOAD_PDF_CLASS(
vips_foreign_load_pdf_buffer_parent_class )->open( pdf ) );
}
static void
@ -669,7 +731,8 @@ vips_foreign_load_pdf_buffer_class_init(
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;
class->open = vips_foreign_load_pdf_buffer_open;
VIPS_ARG_BOXED( class, "buffer", 1,
_( "Buffer" ),