From 6e71478cd484c7f86d24b02e3025394cd63c500d Mon Sep 17 00:00:00 2001 From: Jehan Date: Fri, 11 Mar 2022 15:05:06 +0100 Subject: [PATCH] =?UTF-8?q?plug-ins:=20propose=20exporting=20as=20BigTIFF?= =?UTF-8?q?=20once=20if=20TIFF=20export=20fails=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … 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). --- plug-ins/file-tiff/file-tiff-io.c | 29 +++++++ plug-ins/file-tiff/file-tiff-io.h | 8 +- plug-ins/file-tiff/file-tiff-save.c | 43 ++++++++-- plug-ins/file-tiff/file-tiff-save.h | 3 +- plug-ins/file-tiff/file-tiff.c | 127 +++++++++++++++++++--------- 5 files changed, 157 insertions(+), 53 deletions(-) diff --git a/plug-ins/file-tiff/file-tiff-io.c b/plug-ins/file-tiff/file-tiff-io.c index eba1dc22ad..b97cc49959 100644 --- a/plug-ins/file-tiff/file-tiff-io.c +++ b/plug-ins/file-tiff/file-tiff-io.c @@ -31,6 +31,7 @@ #include "file-tiff-io.h" +static gboolean tiff_file_size_error = FALSE; typedef struct { @@ -150,6 +151,18 @@ tiff_open (GFile *file, 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 tiff_io_warning (const gchar *module, const gchar *fmt, @@ -247,6 +260,22 @@ tiff_io_error (const gchar *module, if (! strcmp (fmt, "Compression algorithm does not support random access")) 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); } diff --git a/plug-ins/file-tiff/file-tiff-io.h b/plug-ins/file-tiff/file-tiff-io.h index fc26357758..3c910a7575 100644 --- a/plug-ins/file-tiff/file-tiff-io.h +++ b/plug-ins/file-tiff/file-tiff-io.h @@ -41,9 +41,11 @@ static const TIFFFieldInfo geotifftags_fieldinfo[] = { { GEOTIFF_ASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, "GeoAsciiParams" } }; -TIFF * tiff_open (GFile *file, - const gchar *mode, - GError **error); +TIFF * tiff_open (GFile *file, + const gchar *mode, + GError **error); +gboolean tiff_got_file_size_error (void); +void tiff_reset_file_size_error (void); #endif /* __FILE_TIFF_IO_H__ */ diff --git a/plug-ins/file-tiff/file-tiff-save.c b/plug-ins/file-tiff/file-tiff-save.c index fb78042866..e88084e785 100644 --- a/plug-ins/file-tiff/file-tiff-save.c +++ b/plug-ins/file-tiff/file-tiff-save.c @@ -805,7 +805,8 @@ save_layer (TIFF *tif, 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; } } @@ -1144,6 +1145,7 @@ out: gimp_progress_update (1.0); g_list_free (layers); + return status; } @@ -1196,7 +1198,8 @@ save_dialog (GimpImage *image, gboolean has_alpha, gboolean is_monochrome, gboolean is_indexed, - gboolean is_multi_layer) + gboolean is_multi_layer, + gboolean classic_tiff_failed) { GtkWidget *dialog; GtkListStore *store; @@ -1222,6 +1225,21 @@ save_dialog (GimpImage *image, GIMP_PROCEDURE_CONFIG (config), 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 = gimp_int_store_new (_("None"), GIMP_COMPRESSION_NONE, _("LZW"), GIMP_COMPRESSION_LZW, @@ -1267,12 +1285,21 @@ save_dialog (GimpImage *image, "save-geotiff", has_geotiff, NULL, NULL, FALSE); - gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), - "compression", - "bigtiff", - "layers-frame", - "save-transparent-pixels", - NULL); + if (classic_tiff_failed) + gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), + "compression", + "big-tif-warning", + "bigtiff", + "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, "compression", &compression, diff --git a/plug-ins/file-tiff/file-tiff-save.h b/plug-ins/file-tiff/file-tiff-save.h index db50ce49cf..7b0dd19841 100644 --- a/plug-ins/file-tiff/file-tiff-save.h +++ b/plug-ins/file-tiff/file-tiff-save.h @@ -36,7 +36,8 @@ gboolean save_dialog (GimpImage *image, gboolean has_alpha, gboolean is_monochrome, gboolean is_indexed, - gboolean is_multi_layer); + gboolean is_multi_layer, + gboolean classic_tiff_failed); #endif /* __FILE_TIFF_SAVE_H__ */ diff --git a/plug-ins/file-tiff/file-tiff.c b/plug-ins/file-tiff/file-tiff.c index d5b2245f49..77577aee66 100644 --- a/plug-ins/file-tiff/file-tiff.c +++ b/plug-ins/file-tiff/file-tiff.c @@ -50,6 +50,7 @@ #include #include "file-tiff.h" +#include "file-tiff-io.h" #include "file-tiff-load.h" #include "file-tiff-save.h" @@ -77,28 +78,38 @@ struct _TiffClass #define TIFF_TYPE (tiff_get_type ()) #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 GimpProcedure * tiff_create_procedure (GimpPlugIn *plug_in, - const gchar *name); +static GList * tiff_query_procedures (GimpPlugIn *plug_in); +static GimpProcedure * tiff_create_procedure (GimpPlugIn *plug_in, + const gchar *name); -static GimpValueArray * tiff_load (GimpProcedure *procedure, - GimpRunMode run_mode, - GFile *file, - const GimpValueArray *args, - gpointer run_data); -static GimpValueArray * tiff_save (GimpProcedure *procedure, - GimpRunMode run_mode, - GimpImage *image, - gint n_drawables, - GimpDrawable **drawables, - GFile *file, - const GimpValueArray *args, - gpointer run_data); +static GimpValueArray * tiff_load (GimpProcedure *procedure, + GimpRunMode run_mode, + GFile *file, + const GimpValueArray *args, + gpointer run_data); +static GimpValueArray * tiff_save (GimpProcedure *procedure, + GimpRunMode run_mode, + GimpImage *image, + gint n_drawables, + GimpDrawable **drawables, + GFile *file, + const GimpValueArray *args, + 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_multi_layer (GimpImage *image); +static gboolean image_is_monochrome (GimpImage *image); +static gboolean image_is_multi_layer (GimpImage *image); G_DEFINE_TYPE (Tiff, tiff, GIMP_TYPE_PLUG_IN) @@ -316,11 +327,9 @@ tiff_save (GimpProcedure *procedure, gpointer run_data) { GimpProcedureConfig *config; - GimpPDBStatusType status = GIMP_PDB_SUCCESS; - GimpExportReturn export = GIMP_EXPORT_CANCEL; GimpMetadata *metadata; - GimpImage *orig_image; GError *error = NULL; + GimpPDBStatusType status = GIMP_PDB_SUCCESS; INIT_I18N (); gegl_init (NULL, NULL); @@ -329,8 +338,6 @@ tiff_save (GimpProcedure *procedure, metadata = gimp_procedure_config_begin_export (config, image, run_mode, args, "image/tiff"); - orig_image = image; - switch (run_mode) { case GIMP_RUN_INTERACTIVE: @@ -341,16 +348,45 @@ tiff_save (GimpProcedure *procedure, 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 (! save_dialog (orig_image, procedure, G_OBJECT (config), n_drawables == 1 ? gimp_drawable_has_alpha (drawables[0]) : TRUE, - image_is_monochrome (image), - gimp_image_get_base_type (image) == GIMP_INDEXED, - image_is_multi_layer (image))) + image_is_monochrome (orig_image), + gimp_image_get_base_type (orig_image) == GIMP_INDEXED, + image_is_multi_layer (orig_image), + retried)) { - return gimp_procedure_new_return_values (procedure, GIMP_PDB_CANCEL, - NULL); + return GIMP_PDB_CANCEL; } } @@ -365,6 +401,7 @@ tiff_save (GimpProcedure *procedure, gboolean crop_layers; g_object_get (config, + "bigtiff", &bigtiff, "compression", &compression, "save-layers", &save_layers, "crop-layers", &crop_layers, @@ -386,7 +423,7 @@ tiff_save (GimpProcedure *procedure, 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; @@ -398,8 +435,7 @@ tiff_save (GimpProcedure *procedure, capabilities); if (export == GIMP_EXPORT_CANCEL) - return gimp_procedure_new_return_values (procedure, GIMP_PDB_CANCEL, - NULL); + return GIMP_PDB_CANCEL; } break; @@ -420,23 +456,32 @@ tiff_save (GimpProcedure *procedure, if (status == GIMP_PDB_SUCCESS) { - if (! save_image (file, image, orig_image, G_OBJECT (config), metadata, - &error)) - { - status = GIMP_PDB_EXECUTION_ERROR; - } + if (! save_image (file, image, orig_image, G_OBJECT (config), + metadata, error)) + status = GIMP_PDB_EXECUTION_ERROR; } - gimp_procedure_config_end_export (config, image, file, status); - g_object_unref (config); - if (export == GIMP_EXPORT_EXPORT) { gimp_image_delete (image); 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