/* tiff loading for GIMP * -Peter Mattis * * The TIFF loading code has been completely revamped by Nick Lamb * njl195@zepler.org.uk -- 18 May 1998 * And it now gains support for tiles (and doubtless a zillion bugs) * njl195@zepler.org.uk -- 12 June 1999 * LZW patent fuss continues :( * njl195@zepler.org.uk -- 20 April 2000 * The code for this filter is based on "tifftopnm" and "pnmtotiff", * 2 programs that are a part of the netpbm package. * khk@khk.net -- 13 May 2000 * Added support for ICCPROFILE tiff tag. If this tag is present in a * TIFF file, then a parasite is created and vice versa. * peter@kirchgessner.net -- 29 Oct 2002 * Progress bar only when run interactive * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004 * Honor EXTRASAMPLES tag while loading images with alphachannel * pablo.dangelo@web.de -- 16 Jan 2004 */ #include "config.h" #include #include #include #include #include #include "file-tiff-io.h" static gboolean tiff_file_size_error = FALSE; typedef struct { GFile *file; GObject *stream; GInputStream *input; GOutputStream *output; gboolean can_seek; gchar *buffer; gsize allocated; gsize used; gsize position; } TiffIO; static TIFFExtendProc parent_extender; static void tiff_io_warning (const gchar *module, const gchar *fmt, va_list ap) G_GNUC_PRINTF (2, 0); static void tiff_io_error (const gchar *module, const gchar *fmt, va_list ap) G_GNUC_PRINTF (2, 0); static tsize_t tiff_io_read (thandle_t handle, tdata_t buffer, tsize_t size); static tsize_t tiff_io_write (thandle_t handle, tdata_t buffer, tsize_t size); static toff_t tiff_io_seek (thandle_t handle, toff_t offset, gint whence); static gint tiff_io_close (thandle_t handle); static toff_t tiff_io_get_file_size (thandle_t handle); static void register_geotags (TIFF *tif); static void register_geotags (TIFF *tif) { static gboolean geotifftags_registered = FALSE; if (geotifftags_registered) return; geotifftags_registered = TRUE; TIFFMergeFieldInfo (tif, geotifftags_fieldinfo, (sizeof (geotifftags_fieldinfo) / sizeof (geotifftags_fieldinfo[0]))); if (parent_extender) (*parent_extender) (tif); } static TiffIO tiff_io = { 0, }; TIFF * tiff_open (GFile *file, const gchar *mode, GError **error) { TIFFSetWarningHandler ((TIFFErrorHandler) tiff_io_warning); TIFFSetErrorHandler ((TIFFErrorHandler) tiff_io_error); parent_extender = TIFFSetTagExtender (register_geotags); tiff_io.file = file; if (! strcmp (mode, "r")) { tiff_io.input = G_INPUT_STREAM (g_file_read (file, NULL, error)); if (! tiff_io.input) return NULL; tiff_io.stream = G_OBJECT (tiff_io.input); } #ifdef TIFF_VERSION_BIG else if(! strcmp (mode, "w") || ! strcmp (mode, "w8")) #else else if(! strcmp (mode, "w")) #endif { tiff_io.output = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)); if (! tiff_io.output) return NULL; tiff_io.stream = G_OBJECT (tiff_io.output); } else if(! strcmp (mode, "a")) { GIOStream *iostream = G_IO_STREAM (g_file_open_readwrite (file, NULL, error)); if (! iostream) return NULL; tiff_io.input = g_io_stream_get_input_stream (iostream); tiff_io.output = g_io_stream_get_output_stream (iostream); tiff_io.stream = G_OBJECT (iostream); } else { g_assert_not_reached (); } #if 0 #warning FIXME !can_seek code is broken tiff_io.can_seek = g_seekable_can_seek (G_SEEKABLE (tiff_io.stream)); #endif tiff_io.can_seek = TRUE; return TIFFClientOpen ("file-tiff", mode, (thandle_t) &tiff_io, tiff_io_read, tiff_io_write, tiff_io_seek, tiff_io_close, tiff_io_get_file_size, 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, va_list ap) { gint tag = 0; /* Between libtiff 3.7.0beta2 and 4.0.0alpha. */ if (! strcmp (fmt, "%s: unknown field with tag %d (0x%x) encountered") || /* Before libtiff 3.7.0beta2. */ ! strcmp (fmt, "%.1000s: unknown field with tag %d (0x%x) encountered")) { va_list ap_test; G_VA_COPY (ap_test, ap); va_arg (ap_test, const char *); /* ignore first arg */ tag = va_arg (ap_test, int); va_end (ap_test); } /* for older versions of libtiff? */ else if (! strcmp (fmt, "unknown field with tag %d (0x%x) ignored") || /* Since libtiff 4.0.0alpha. */ ! strcmp (fmt, "Unknown field with tag %d (0x%x) encountered") || /* Since libtiff 4.3.0rc1. */ ! strcmp (fmt, "Unknown field with tag %u (0x%x) encountered")) { va_list ap_test; G_VA_COPY (ap_test, ap); tag = va_arg (ap_test, int); va_end (ap_test); } else if (! strcmp (fmt, "Incorrect value for \"%s\"; tag ignored")) { va_list ap_test; const char *stag; G_VA_COPY (ap_test, ap); stag = va_arg (ap_test, const char *); if (! strcmp (stag, "RichTIFFIPTC")) { gchar *msg = g_strdup_vprintf (fmt, ap); /* This is an error in Adobe products. Just report to stderr. */ g_printerr ("[%s] %s\n", module, msg); g_free (msg); return; } va_end (ap_test); } /* Workaround for: http://bugzilla.gnome.org/show_bug.cgi?id=131975 * Ignore the warnings about unregistered private tags (>= 32768). */ if (tag >= 32768) return; /* Other unknown fields are only reported to stderr. */ if (tag > 0) { gchar *msg = g_strdup_vprintf (fmt, ap); g_printerr ("%s\n", msg); g_free (msg); return; } else if (! strcmp (module, "TIFFReadDirectory") && ! strcmp (fmt, "Sum of Photometric type-related color channels and ExtraSamples doesn't match SamplesPerPixel." " Defining non-color channels as ExtraSamples.")) { /* We will process this issue in our code. Just report to stderr. */ g_printerr ("%s: [%s] %s\n", G_STRFUNC, module, fmt); return; } else if (! strcmp (module, "Fax4Decode") || g_str_has_prefix (module, "Fax3Decode")) { /* Certain corrupt TIFF Fax images can produce a large amount of * warnings which can cause GIMP to run out of GDI resources on * Windows and eventually crash. * The real problem seems to be that the amount of error console * messages does not have a limit. * See e.g. the first page of m1-8110934bb3b18d0e87ccc1ddfc5f0107.tif * from imagetestsuite. LibTiff does not return -1 from * ReadScanline, presumably because for fax images it's not * unreasonable to expect certain lines to fail. * Let's just only report to stderr in this case. */ gchar *msg = g_strdup_vprintf (fmt, ap); g_printerr ("LibTiff warning: [%s] %s\n", module, msg); g_free (msg); return; } g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap); } static void tiff_io_error (const gchar *module, const gchar *fmt, va_list ap) { gchar *msg; /* Workaround for: http://bugzilla.gnome.org/show_bug.cgi?id=132297 * Ignore the errors related to random access and JPEG compression */ if (! strcmp (fmt, "Compression algorithm does not support random access")) return; msg = g_strdup_vprintf (fmt, ap); #ifdef TIFF_VERSION_BIG 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 /* Easier for debugging to at least print messages on stderr. */ g_printerr ("LibTiff error: [%s] %s\n", module, msg); #endif g_log (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, "%s", msg); g_free (msg); } static tsize_t tiff_io_read (thandle_t handle, tdata_t buffer, tsize_t size) { TiffIO *io = (TiffIO *) handle; GError *error = NULL; gssize read = -1; if (io->can_seek) { gsize bytes_read = 0; if (! g_input_stream_read_all (io->input, (void *) buffer, (gsize) size, &bytes_read, NULL, &error)) { g_printerr ("%s", error->message); g_clear_error (&error); } read = bytes_read; } else { if (io->position + size > io->used) { gsize missing; gsize bytes_read; missing = io->position + size - io->used; if (io->used + missing > io->allocated) { gchar *new_buffer; gsize new_size = 1; gsize needed; needed = io->used + missing - io->allocated; while (new_size < io->allocated + needed) new_size *= 2; new_buffer = g_try_realloc (io->buffer, new_size); if (! new_buffer) return -1; io->buffer = new_buffer; io->allocated = new_size; } if (! g_input_stream_read_all (io->input, (void *) (io->buffer + io->used), missing, &bytes_read, NULL, &error)) { g_printerr ("%s", error->message); g_clear_error (&error); } io->used += bytes_read; } g_assert (io->position + size <= io->used); memcpy (buffer, io->buffer + io->position, size); io->position += size; read = size; } return (tsize_t) read; } static tsize_t tiff_io_write (thandle_t handle, tdata_t buffer, tsize_t size) { TiffIO *io = (TiffIO *) handle; GError *error = NULL; gssize written = -1; if (io->can_seek) { gsize bytes_written = 0; if (! g_output_stream_write_all (io->output, (void *) buffer, (gsize) size, &bytes_written, NULL, &error)) { g_printerr ("%s", error->message); g_clear_error (&error); } written = bytes_written; } else { if (io->position + size > io->allocated) { gchar *new_buffer; gsize new_size; new_size = io->position + size; new_buffer = g_try_realloc (io->buffer, new_size); if (! new_buffer) return -1; io->buffer = new_buffer; io->allocated = new_size; } g_assert (io->position + size <= io->allocated); memcpy (io->buffer + io->position, buffer, size); io->position += size; io->used = MAX (io->used, io->position); written = size; } return (tsize_t) written; } static GSeekType lseek_to_seek_type (gint whence) { switch (whence) { default: case SEEK_SET: return G_SEEK_SET; case SEEK_CUR: return G_SEEK_CUR; case SEEK_END: return G_SEEK_END; } } static toff_t tiff_io_seek (thandle_t handle, toff_t offset, gint whence) { TiffIO *io = (TiffIO *) handle; GError *error = NULL; gboolean sought = FALSE; goffset position = -1; if (io->can_seek) { sought = g_seekable_seek (G_SEEKABLE (io->stream), (goffset) offset, lseek_to_seek_type (whence), NULL, &error); if (sought) { position = g_seekable_tell (G_SEEKABLE (io->stream)); } else { g_printerr ("%s", error->message); g_clear_error (&error); } } else { switch (whence) { default: case SEEK_SET: if (offset <= io->used) position = io->position = offset; break; case SEEK_CUR: if (io->position + offset <= io->used) position = io->position += offset; break; case G_SEEK_END: if (io->used + offset <= io->used) position = io->position = io->used + offset; break; } } return (toff_t) position; } static gint tiff_io_close (thandle_t handle) { TiffIO *io = (TiffIO *) handle; GError *error = NULL; gboolean closed = FALSE; if (io->input && ! io->output) { closed = g_input_stream_close (io->input, NULL, &error); } else { if (! io->can_seek && io->buffer && io->allocated) { if (! g_output_stream_write_all (io->output, (void *) io->buffer, io->allocated, NULL, NULL, &error)) { g_printerr ("%s", error->message); g_clear_error (&error); } } if (io->input) { closed = g_io_stream_close (G_IO_STREAM (io->stream), NULL, &error); } else { closed = g_output_stream_close (io->output, NULL, &error); } } if (! closed) { g_printerr ("%s", error->message); g_clear_error (&error); } g_object_unref (io->stream); io->stream = NULL; io->input = NULL; io->output = NULL; g_free (io->buffer); io->buffer = NULL; io->allocated = 0; io->used = 0; io->position = 0; return closed ? 0 : -1; } static toff_t tiff_io_get_file_size (thandle_t handle) { TiffIO *io = (TiffIO *) handle; GError *error = NULL; GFileInfo *info; goffset size = 0; info = g_file_query_info (io->file, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, &error); if (! info) { g_printerr ("%s", error->message); g_clear_error (&error); } else { size = g_file_info_get_size (info); g_object_unref (info); } return (toff_t) size; }