plug-ins: propose exporting as BigTIFF once if TIFF export fails…

… because we reached max TIFF size.

We detect the specific TIFF error (by string comparison so it's a bit
weak IMO yet it doesn't seem like libtiff provides anything better;
let's trust they don't change their error strings), then we reopen the
export dialog, pre-checking the BigTIFF checkbox newly created (and
making it insensitive). We still fail with error if an error happens the
second time (even for the same error).
This commit is contained in:
Jehan 2022-03-11 15:05:06 +01:00
parent 871796a126
commit 6e71478cd4
5 changed files with 157 additions and 53 deletions

View File

@ -31,6 +31,7 @@
#include "file-tiff-io.h" #include "file-tiff-io.h"
static gboolean tiff_file_size_error = FALSE;
typedef struct typedef struct
{ {
@ -150,6 +151,18 @@ tiff_open (GFile *file,
NULL, NULL); NULL, NULL);
} }
gboolean
tiff_got_file_size_error (void)
{
return tiff_file_size_error;
}
void
tiff_reset_file_size_error (void)
{
tiff_file_size_error = FALSE;
}
static void static void
tiff_io_warning (const gchar *module, tiff_io_warning (const gchar *module,
const gchar *fmt, const gchar *fmt,
@ -247,6 +260,22 @@ tiff_io_error (const gchar *module,
if (! strcmp (fmt, "Compression algorithm does not support random access")) if (! strcmp (fmt, "Compression algorithm does not support random access"))
return; return;
if (g_strcmp0 (fmt, "Maximum TIFF file size exceeded") == 0)
{
/* @module in my tests were "TIFFAppendToStrip" but I wonder if
* this same error could not happen with other "modules".
*/
tiff_file_size_error = TRUE;
}
else
{
gchar *msg = g_strdup_vprintf (fmt, ap);
/* Easier for debugging to at least print messages on stderr. */
g_printerr ("LibTiff error: [%s] %s\n", module, msg);
g_free (msg);
}
g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap); g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap);
} }

View File

@ -41,9 +41,11 @@ static const TIFFFieldInfo geotifftags_fieldinfo[] = {
{ GEOTIFF_ASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, "GeoAsciiParams" } { GEOTIFF_ASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, "GeoAsciiParams" }
}; };
TIFF * tiff_open (GFile *file, TIFF * tiff_open (GFile *file,
const gchar *mode, const gchar *mode,
GError **error); GError **error);
gboolean tiff_got_file_size_error (void);
void tiff_reset_file_size_error (void);
#endif /* __FILE_TIFF_IO_H__ */ #endif /* __FILE_TIFF_IO_H__ */

View File

@ -805,7 +805,8 @@ save_layer (TIFF *tif,
if (!success) if (!success)
{ {
g_message (_("Failed a scanline write on row %d"), row); g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Failed a scanline write on row %d"), row);
goto out; goto out;
} }
} }
@ -1144,6 +1145,7 @@ out:
gimp_progress_update (1.0); gimp_progress_update (1.0);
g_list_free (layers); g_list_free (layers);
return status; return status;
} }
@ -1196,7 +1198,8 @@ save_dialog (GimpImage *image,
gboolean has_alpha, gboolean has_alpha,
gboolean is_monochrome, gboolean is_monochrome,
gboolean is_indexed, gboolean is_indexed,
gboolean is_multi_layer) gboolean is_multi_layer,
gboolean classic_tiff_failed)
{ {
GtkWidget *dialog; GtkWidget *dialog;
GtkListStore *store; GtkListStore *store;
@ -1222,6 +1225,21 @@ save_dialog (GimpImage *image,
GIMP_PROCEDURE_CONFIG (config), GIMP_PROCEDURE_CONFIG (config),
image); image);
if (classic_tiff_failed)
{
GtkWidget *bigtiff_checkbox;
gimp_procedure_dialog_get_label (GIMP_PROCEDURE_DIALOG (dialog),
"big-tif-warning",
"\xe2\x9a\xa0 Warning: maximum TIFF file size exceeded. "
"Retry as BigTIFF or cancel.");
g_object_set (config, "bigtiff", TRUE, NULL);
bigtiff_checkbox = gimp_procedure_dialog_get_widget (GIMP_PROCEDURE_DIALOG (dialog),
"bigtiff",
G_TYPE_NONE);
gtk_widget_set_sensitive (bigtiff_checkbox, FALSE);
}
store = store =
gimp_int_store_new (_("None"), GIMP_COMPRESSION_NONE, gimp_int_store_new (_("None"), GIMP_COMPRESSION_NONE,
_("LZW"), GIMP_COMPRESSION_LZW, _("LZW"), GIMP_COMPRESSION_LZW,
@ -1267,12 +1285,21 @@ save_dialog (GimpImage *image,
"save-geotiff", "save-geotiff",
has_geotiff, NULL, NULL, FALSE); has_geotiff, NULL, NULL, FALSE);
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), if (classic_tiff_failed)
"compression", gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog),
"bigtiff", "compression",
"layers-frame", "big-tif-warning",
"save-transparent-pixels", "bigtiff",
NULL); "layers-frame",
"save-transparent-pixels",
NULL);
else
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog),
"compression",
"bigtiff",
"layers-frame",
"save-transparent-pixels",
NULL);
g_object_get (config, g_object_get (config,
"compression", &compression, "compression", &compression,

View File

@ -36,7 +36,8 @@ gboolean save_dialog (GimpImage *image,
gboolean has_alpha, gboolean has_alpha,
gboolean is_monochrome, gboolean is_monochrome,
gboolean is_indexed, gboolean is_indexed,
gboolean is_multi_layer); gboolean is_multi_layer,
gboolean classic_tiff_failed);
#endif /* __FILE_TIFF_SAVE_H__ */ #endif /* __FILE_TIFF_SAVE_H__ */

View File

@ -50,6 +50,7 @@
#include <libgimp/gimpui.h> #include <libgimp/gimpui.h>
#include "file-tiff.h" #include "file-tiff.h"
#include "file-tiff-io.h"
#include "file-tiff-load.h" #include "file-tiff-load.h"
#include "file-tiff-save.h" #include "file-tiff-save.h"
@ -77,28 +78,38 @@ struct _TiffClass
#define TIFF_TYPE (tiff_get_type ()) #define TIFF_TYPE (tiff_get_type ())
#define TIFF (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIFF_TYPE, Tiff)) #define TIFF (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIFF_TYPE, Tiff))
GType tiff_get_type (void) G_GNUC_CONST; GType tiff_get_type (void) G_GNUC_CONST;
static GList * tiff_query_procedures (GimpPlugIn *plug_in); static GList * tiff_query_procedures (GimpPlugIn *plug_in);
static GimpProcedure * tiff_create_procedure (GimpPlugIn *plug_in, static GimpProcedure * tiff_create_procedure (GimpPlugIn *plug_in,
const gchar *name); const gchar *name);
static GimpValueArray * tiff_load (GimpProcedure *procedure, static GimpValueArray * tiff_load (GimpProcedure *procedure,
GimpRunMode run_mode, GimpRunMode run_mode,
GFile *file, GFile *file,
const GimpValueArray *args, const GimpValueArray *args,
gpointer run_data); gpointer run_data);
static GimpValueArray * tiff_save (GimpProcedure *procedure, static GimpValueArray * tiff_save (GimpProcedure *procedure,
GimpRunMode run_mode, GimpRunMode run_mode,
GimpImage *image, GimpImage *image,
gint n_drawables, gint n_drawables,
GimpDrawable **drawables, GimpDrawable **drawables,
GFile *file, GFile *file,
const GimpValueArray *args, const GimpValueArray *args,
gpointer run_data); gpointer run_data);
static GimpPDBStatusType tiff_save_rec (GimpProcedure *procedure,
GimpRunMode run_mode,
GimpImage *orig_image,
gint n_orig_drawables,
GimpDrawable **orig_drawables,
GFile *file,
GimpProcedureConfig *config,
GimpMetadata *metadata,
gboolean retried,
GError **error);
static gboolean image_is_monochrome (GimpImage *image); static gboolean image_is_monochrome (GimpImage *image);
static gboolean image_is_multi_layer (GimpImage *image); static gboolean image_is_multi_layer (GimpImage *image);
G_DEFINE_TYPE (Tiff, tiff, GIMP_TYPE_PLUG_IN) G_DEFINE_TYPE (Tiff, tiff, GIMP_TYPE_PLUG_IN)
@ -316,11 +327,9 @@ tiff_save (GimpProcedure *procedure,
gpointer run_data) gpointer run_data)
{ {
GimpProcedureConfig *config; GimpProcedureConfig *config;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
GimpExportReturn export = GIMP_EXPORT_CANCEL;
GimpMetadata *metadata; GimpMetadata *metadata;
GimpImage *orig_image;
GError *error = NULL; GError *error = NULL;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
INIT_I18N (); INIT_I18N ();
gegl_init (NULL, NULL); gegl_init (NULL, NULL);
@ -329,8 +338,6 @@ tiff_save (GimpProcedure *procedure,
metadata = gimp_procedure_config_begin_export (config, image, run_mode, metadata = gimp_procedure_config_begin_export (config, image, run_mode,
args, "image/tiff"); args, "image/tiff");
orig_image = image;
switch (run_mode) switch (run_mode)
{ {
case GIMP_RUN_INTERACTIVE: case GIMP_RUN_INTERACTIVE:
@ -341,16 +348,45 @@ tiff_save (GimpProcedure *procedure,
break; break;
} }
status = tiff_save_rec (procedure, run_mode, image,
n_drawables, drawables,
file, config, metadata, FALSE, &error);
gimp_procedure_config_end_export (config, image, file, status);
g_object_unref (config);
return gimp_procedure_new_return_values (procedure, status, error);
}
static GimpPDBStatusType
tiff_save_rec (GimpProcedure *procedure,
GimpRunMode run_mode,
GimpImage *orig_image,
gint n_orig_drawables,
GimpDrawable **orig_drawables,
GFile *file,
GimpProcedureConfig *config,
GimpMetadata *metadata,
gboolean retried,
GError **error)
{
GimpImage *image = orig_image;
GimpDrawable **drawables = orig_drawables;
gint n_drawables = n_orig_drawables;
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
GimpExportReturn export = GIMP_EXPORT_CANCEL;
gboolean bigtiff = FALSE;
if (run_mode == GIMP_RUN_INTERACTIVE) if (run_mode == GIMP_RUN_INTERACTIVE)
{ {
if (! save_dialog (orig_image, procedure, G_OBJECT (config), if (! save_dialog (orig_image, procedure, G_OBJECT (config),
n_drawables == 1 ? gimp_drawable_has_alpha (drawables[0]) : TRUE, n_drawables == 1 ? gimp_drawable_has_alpha (drawables[0]) : TRUE,
image_is_monochrome (image), image_is_monochrome (orig_image),
gimp_image_get_base_type (image) == GIMP_INDEXED, gimp_image_get_base_type (orig_image) == GIMP_INDEXED,
image_is_multi_layer (image))) image_is_multi_layer (orig_image),
retried))
{ {
return gimp_procedure_new_return_values (procedure, GIMP_PDB_CANCEL, return GIMP_PDB_CANCEL;
NULL);
} }
} }
@ -365,6 +401,7 @@ tiff_save (GimpProcedure *procedure,
gboolean crop_layers; gboolean crop_layers;
g_object_get (config, g_object_get (config,
"bigtiff", &bigtiff,
"compression", &compression, "compression", &compression,
"save-layers", &save_layers, "save-layers", &save_layers,
"crop-layers", &crop_layers, "crop-layers", &crop_layers,
@ -386,7 +423,7 @@ tiff_save (GimpProcedure *procedure,
GIMP_EXPORT_CAN_HANDLE_ALPHA); GIMP_EXPORT_CAN_HANDLE_ALPHA);
} }
if (save_layers && image_is_multi_layer (image)) if (save_layers && image_is_multi_layer (orig_image))
{ {
capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS; capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
@ -398,8 +435,7 @@ tiff_save (GimpProcedure *procedure,
capabilities); capabilities);
if (export == GIMP_EXPORT_CANCEL) if (export == GIMP_EXPORT_CANCEL)
return gimp_procedure_new_return_values (procedure, GIMP_PDB_CANCEL, return GIMP_PDB_CANCEL;
NULL);
} }
break; break;
@ -420,23 +456,32 @@ tiff_save (GimpProcedure *procedure,
if (status == GIMP_PDB_SUCCESS) if (status == GIMP_PDB_SUCCESS)
{ {
if (! save_image (file, image, orig_image, G_OBJECT (config), metadata, if (! save_image (file, image, orig_image, G_OBJECT (config),
&error)) metadata, error))
{ status = GIMP_PDB_EXECUTION_ERROR;
status = GIMP_PDB_EXECUTION_ERROR;
}
} }
gimp_procedure_config_end_export (config, image, file, status);
g_object_unref (config);
if (export == GIMP_EXPORT_EXPORT) if (export == GIMP_EXPORT_EXPORT)
{ {
gimp_image_delete (image); gimp_image_delete (image);
g_free (drawables); g_free (drawables);
} }
return gimp_procedure_new_return_values (procedure, status, error); if (status == GIMP_PDB_EXECUTION_ERROR &&
run_mode == GIMP_RUN_INTERACTIVE &&
! retried && ! bigtiff && tiff_got_file_size_error ())
{
/* Retrying but just once, when the save failed because we exceeded
* TIFF max size, to propose BigTIFF instead. */
tiff_reset_file_size_error ();
g_clear_error (error);
return tiff_save_rec (procedure, run_mode,
orig_image, n_orig_drawables, orig_drawables,
file, config, metadata, TRUE, error);
}
return status;
} }
static gboolean static gboolean