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



commit 6e71478cd484c7f86d24b02e3025394cd63c500d
Author: Jehan <jehan girinstud io>
Date:   Fri Mar 11 15:05:06 2022 +0100

    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).

 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      | 133 ++++++++++++++++++++++++------------
 5 files changed, 160 insertions(+), 56 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 <libgimp/gimpui.h>
 
 #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;
-
-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 gboolean         image_is_monochrome  (GimpImage            *image);
-static gboolean         image_is_multi_layer (GimpImage            *image);
+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 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);
 
 
 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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]