From ecf63bf1bee7f1af1d578164f16abc0aca932323 Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 26 Feb 2018 21:16:39 +0100 Subject: [PATCH] Add ability to display various chunks from the PNG image file (bug #10321) --- acinclude.m4 | 3 + plugins/thunar-apr/Makefile.am | 4 + plugins/thunar-apr/thunar-apr-image-page.c | 123 ++++- plugins/thunar-apr/thunar-apr-png-read-data.c | 616 ++++++++++++++++++++++++++ plugins/thunar-apr/thunar-apr-png-read-data.h | 119 +++++ 5 files changed, 844 insertions(+), 21 deletions(-) create mode 100644 plugins/thunar-apr/thunar-apr-png-read-data.c create mode 100644 plugins/thunar-apr/thunar-apr-png-read-data.h diff --git a/acinclude.m4 b/acinclude.m4 index b7c9a158..59eaf536 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -21,6 +21,9 @@ AC_MSG_RESULT([$ac_bm_thunar_plugin_apr]) dnl Check for libexif (for the "Image" properties page) XDT_CHECK_OPTIONAL_PACKAGE([EXIF], [libexif], [0.6.0], [exif], [Exif support]) + +dnl Check for libpng (for the "Image" properties page) +XDT_CHECK_OPTIONAL_PACKAGE([PNG], [libpng], [1.5], [png], [PNG Support]) ]) diff --git a/plugins/thunar-apr/Makefile.am b/plugins/thunar-apr/Makefile.am index b0ed555f..f80404d7 100644 --- a/plugins/thunar-apr/Makefile.am +++ b/plugins/thunar-apr/Makefile.am @@ -18,6 +18,8 @@ thunar_apr_la_SOURCES = \ thunar-apr-desktop-page.h \ thunar-apr-image-page.c \ thunar-apr-image-page.h \ + thunar-apr-png-read-data.c \ + thunar-apr-png-read-data.h \ thunar-apr-plugin.c \ thunar-apr-private.c \ thunar-apr-private.h \ @@ -26,6 +28,7 @@ thunar_apr_la_SOURCES = \ thunar_apr_la_CFLAGS = \ $(EXIF_CFLAGS) \ + $(PNG_CFLAGS) \ $(EXO_CFLAGS) \ $(GLIB_CFLAGS) \ $(GTK_CFLAGS) \ @@ -42,6 +45,7 @@ thunar_apr_la_LDFLAGS = \ thunar_apr_la_LIBADD = \ $(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la \ $(EXIF_LIBS) \ + $(PNG_LIBS) \ $(EXO_LIBS) \ $(GLIB_LIBS) \ $(GTK_LIBS) diff --git a/plugins/thunar-apr/thunar-apr-image-page.c b/plugins/thunar-apr/thunar-apr-image-page.c index 5a846192..e6e30bcf 100644 --- a/plugins/thunar-apr/thunar-apr-image-page.c +++ b/plugins/thunar-apr/thunar-apr-image-page.c @@ -25,6 +25,9 @@ #include #include +#ifdef HAVE_PNG +#include "thunar-apr-png-read-data.h" +#endif #ifdef HAVE_EXIF #include @@ -77,6 +80,11 @@ struct _ThunarAprImagePage #ifdef HAVE_EXIF GtkWidget *exif_labels[G_N_ELEMENTS (TAIP_EXIF)]; #endif +#ifdef HAVE_PNG + GtkWidget *png_container; + GtkWidget *png_name_labels[number_of_png_chunks]; + GtkWidget *png_data_labels[number_of_png_chunks]; +#endif }; @@ -98,20 +106,45 @@ thunar_apr_image_page_class_init (ThunarAprImagePageClass *klass) +#if defined(HAVE_EXIF) || defined(HAVE_PNG) +void +thunar_apr_image_page_Atk_bindlabels(GtkWidget *left, GtkWidget *right); + +void +thunar_apr_image_page_Atk_bindlabels(GtkWidget *left, GtkWidget *right) +{ + AtkRelationSet *relations; + AtkRelation *relation; + AtkObject *object; + + exo_binding_new (G_OBJECT (right), "visible", G_OBJECT (left), "visible"); + + /* set Atk label relation for the label */ + object = gtk_widget_get_accessible (right); + relations = atk_object_ref_relation_set (gtk_widget_get_accessible (left)); + relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR); + atk_relation_set_add (relations, relation); + g_object_unref (G_OBJECT (relation)); +} + +#endif + + + static void thunar_apr_image_page_init (ThunarAprImagePage *image_page) { - AtkRelationSet *relations; PangoAttribute *attribute; PangoAttrList *attr_list; - AtkRelation *relation; - AtkObject *object; GtkWidget *label; GtkWidget *grid; GtkWidget *spacer; #ifdef HAVE_EXIF guint n; #endif +#ifdef HAVE_PNG + guint i; +#endif gtk_container_set_border_width (GTK_CONTAINER (image_page), 12); thunarx_property_page_set_label (THUNARX_PROPERTY_PAGE (image_page), _("Image")); @@ -143,12 +176,7 @@ thunar_apr_image_page_init (ThunarAprImagePage *image_page) gtk_grid_attach (GTK_GRID (grid), image_page->type_label, 1, 0, 1, 1); gtk_widget_show (image_page->type_label); - /* set Atk label relation for the label */ - object = gtk_widget_get_accessible (image_page->type_label); - relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label)); - relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR); - atk_relation_set_add (relations, relation); - g_object_unref (G_OBJECT (relation)); + thunar_apr_image_page_Atk_bindlabels (label, image_page->type_label); label = gtk_label_new (_("Image Size:")); gtk_label_set_xalign (GTK_LABEL (label), 1.0f); @@ -164,12 +192,7 @@ thunar_apr_image_page_init (ThunarAprImagePage *image_page) gtk_grid_attach (GTK_GRID (grid), image_page->dimensions_label, 1, 1, 1, 1); gtk_widget_show (image_page->dimensions_label); - /* set Atk label relation for the label */ - object = gtk_widget_get_accessible (image_page->dimensions_label); - relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label)); - relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR); - atk_relation_set_add (relations, relation); - g_object_unref (G_OBJECT (relation)); + thunar_apr_image_page_Atk_bindlabels (label, image_page->dimensions_label); #ifdef HAVE_EXIF /* some spacing between the General info and the Exif info */ @@ -196,12 +219,36 @@ thunar_apr_image_page_init (ThunarAprImagePage *image_page) exo_binding_new (G_OBJECT (image_page->exif_labels[n]), "visible", G_OBJECT (label), "visible"); - /* set Atk label relation for the label */ - object = gtk_widget_get_accessible (image_page->exif_labels[n]); - relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label)); - relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR); - atk_relation_set_add (relations, relation); - g_object_unref (G_OBJECT (relation)); + thunar_apr_image_page_Atk_bindlabels (label, image_page->exif_labels[n]); + } +#endif + +#ifdef HAVE_PNG + /* some spacing between the General info and the PNG info */ + spacer = g_object_new (GTK_TYPE_BOX, "orientation", GTK_ORIENTATION_VERTICAL, "height-request", 6, NULL); + gtk_grid_attach (GTK_GRID (grid), spacer, 0, 2, 2, 1); + gtk_widget_show (spacer); + + /* add labels for the PNG info */ + for (i = 0; i < number_of_png_chunks; ++i) + { + image_page->png_name_labels[i] = gtk_label_new (""); + gtk_misc_set_alignment (GTK_MISC (image_page->png_name_labels[i]), 1.0f, 0.5f); + gtk_label_set_attributes (GTK_LABEL (image_page->png_name_labels[i]), attr_list); + gtk_grid_attach (GTK_GRID (grid), image_page->png_name_labels[i], 0, i + 3, 1, 1); + gtk_widget_show (image_page->png_name_labels[i]); + + image_page->png_data_labels[i] = gtk_label_new (""); + gtk_label_set_selectable (GTK_LABEL (image_page->png_data_labels[i]), TRUE); + gtk_misc_set_alignment (GTK_MISC (image_page->png_data_labels[i]), 0.0f, 0.5f); + gtk_label_set_single_line_mode (GTK_LABEL (image_page->png_data_labels[i]), FALSE); + gtk_label_set_line_wrap (GTK_LABEL (image_page->png_data_labels[i]), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (image_page->png_data_labels[i]), TRUE); + gtk_label_set_line_wrap_mode (GTK_LABEL (image_page->png_data_labels[i]), PANGO_WRAP_WORD); + gtk_grid_attach (GTK_GRID (grid), image_page->png_data_labels[i], 1, i + 3, 1, 1); + gtk_widget_show (image_page->png_data_labels[i]); + + thunar_apr_image_page_Atk_bindlabels (image_page->png_name_labels[i], image_page->png_data_labels[i]); } #endif @@ -228,6 +275,10 @@ thunar_apr_image_page_file_changed (ThunarAprAbstractPage *abstract_page, gchar exif_buffer[1024]; guint n; #endif +#ifdef HAVE_PNG + png_read_data_ptr_t png_read_data=NULL; + guint i; +#endif /* determine the URI for the file */ uri = thunarx_file_info_get_uri (file); @@ -286,6 +337,32 @@ thunar_apr_image_page_file_changed (ThunarAprAbstractPage *abstract_page, exif_data_free (exif_data); } #endif +#ifdef HAVE_PNG + /* hide all PNG labels (will be shown again if data is available) */ + gtk_widget_hide (image_page->png_container); + + png_read_data=png_read_data_from_file(filename); + if (G_LIKELY (png_read_data != NULL)) + { + png_chunk_details_ptr_t png_chunks=png_read_data_get_chunk_ptrs(png_read_data); + if (G_LIKELY (png_chunks != NULL)) + { + i=0; + do + { + if (G_LIKELY (png_chunks->data != NULL)) + { + gtk_label_set_text (GTK_LABEL (image_page->png_name_labels[i]), png_chunks->trans.name); + gtk_label_set_text (GTK_LABEL (image_page->png_data_labels[i]), png_chunks->data); + ++i; + } + } + while (G_LIKELY ((png_chunks=png_read_data_next_item(png_chunks)) != NULL)); + gtk_widget_show (image_page->png_container); + } + png_read_data_free_ptr(png_read_data); + } +#endif } else { @@ -298,6 +375,10 @@ thunar_apr_image_page_file_changed (ThunarAprAbstractPage *abstract_page, for (n = 0; n < G_N_ELEMENTS (TAIP_EXIF); ++n) gtk_widget_hide (image_page->exif_labels[n]); #endif +#ifdef HAVE_PNG + /* hide all PNG labels */ + gtk_widget_hide (image_page->png_container); +#endif } } diff --git a/plugins/thunar-apr/thunar-apr-png-read-data.c b/plugins/thunar-apr/thunar-apr-png-read-data.c new file mode 100644 index 00000000..ed2b8cf0 --- /dev/null +++ b/plugins/thunar-apr/thunar-apr-png-read-data.c @@ -0,0 +1,616 @@ +/* $Id$ */ +/*- + * Copyright (c) 2013 Jason Hearne-McGuiness + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_PNG + +#ifdef DEBUG +# ifndef PNG_DEBUG +# define PNG_DEBUG 1 +# endif +#else +# undef PNG_DEBUG +#endif + +#include "thunar-apr-png-read-data.h" + +/** + * Don't use any path as on my system a version is included in the path, which might be different on other machines. + */ +#include + +#include +#include +#include +#include + +/** + * The list of valid chunk names. + * From http://www.libpng.org/pub/png/spec/register/pngreg-1.3.0-pdg.html + */ +char const * const png_chunk_names[number_of_png_chunks]={ + "IHDR", + "tIME", + "PLTE", + "bKGD", + "cHRM", + "fRAc", + "gAMA", + "gIFg", + "gIFt", + "gIFx", + "hIST", + "iCCP", + "iTXt", + "oFFs", + "pCAL", + "pHYs", + "sBIT", + "sCAL", + "sPLT", + "sRGB", + "sTER", + "tEXt", + "tRNS", + "zTXt" +}; + +/** + * Not all of these are representable data. + * Convenience:: make it easier whilst constructing the private_png_read_data_ptr_t to index into it and the collection of png_chunk_details. + * These must be in the same order as png_chunk_names. + */ +typedef enum { + png_chunk_names_IHDR=0, + png_chunk_names_tIME, + png_chunk_names_PLTE, + png_chunk_names_bKGD, + png_chunk_names_cHRM, + png_chunk_names_fRAc, + png_chunk_names_gAMA, + png_chunk_names_gIFg, + png_chunk_names_gIFt, + png_chunk_names_gIFx, + png_chunk_names_hIST, + png_chunk_names_iCCP, + png_chunk_names_iTXt, + png_chunk_names_oFFs, + png_chunk_names_pCAL, + png_chunk_names_pHYs, + png_chunk_names_sBIT, + png_chunk_names_sCAL, + png_chunk_names_sPLT, + png_chunk_names_sRGB, + png_chunk_names_sTER, + png_chunk_names_tEXt, + png_chunk_names_tRNS, + png_chunk_names_zTXt +} png_chunk_names_enum; + +typedef struct +{ + png_chunk_name_translate_t const trans; ///< The translation of the chunk. + char const *fmt_str; ///< The format string for converting the raw chunk-data into human-readable form. +} png_chunk_fmt_t; + +/** + * These must be in the same order as png_chunk_names. + */ +static const png_chunk_fmt_t png_chunk_details[number_of_png_chunks]={ + {{"IHDR", "Header:"}, "Width=%u, height=%u, bit depth=%i, colour type: %s, interlace type: %s"}, + {{"tIME", "Modification time:"}, "%hu/%hhu/%hhu %02hhu:%02hhu:%02hhu"}, ///< In ISO format, because Linux is international. + {{"PLTE", "Palette entries:"}, "%i, RGB=(%hhu, %hhu, %hhu)"}, + {{"bKGD", "Palette index:"}, "%hhu, RGB=(%hu, %hu, %hu), gray=%hu"}, + {{"cHRM", "CIE chromaticities:"}, "white x=%f, white y=%f, red x=%f, red y=%f, green x=%f, green y=%f, blue x=%f, blue y=%f"}, + {{"fRAc", ""}, ""}, ///< No specification in PNG spec. + {{"gAMA", "Gamma factor:"}, "%f"}, + {{"gIFg", ""}, ""}, ///< No specification in libpng headers. + {{"gIFt", ""}, ""}, ///< Deprecated, so not supported. + {{"gIFx", ""}, ""}, ///< No specification in libpng headers. + {{"hIST", "Histogram:"}, "%hu"}, + {{"iCCP", "iCCP Embedded ICC profile:"}, ""}, ///< TODO Need to complete representing this. + {{"iTXt", "Text:"}, ""}, ///< TODO Need to complete representing this. + {{"oFFs", "Offset:"}, "(%i, %i) %s"}, + {{"pCAL", ""}, ""}, ///< TODO Need to complete representing this. + {{"pHYs", "Resolution:"}, "(%u, %u) %s"}, + {{"sBIT", "Significant bits:"}, "RGB=(%hhu, %hhu, %hhu), gray=%hhu, alpha=%hhu"}, + {{"sCAL", "Scale:"}, "width=%f, height=%f %s"}, + {{"sPLT", "Suggested palette:"}, "names=%s, depth=%hhu, no. entries=%i"}, + {{"sRGB", "ICC Colour space:"}, "%s"}, + {{"sTER", ""}, ""}, ///< No specification in libpng headers. + {{"tEXt", "Text:"}, "%s"}, ///< Format specifier not used, as the various components are concatenated in the code. Just here to symbolically indicate that this has been implemented. + {{"tRNS", "Transparency:"}, "alpha=%hhu, number=%i, index=%hhu, RGB=(%hu, %hu, %hu), gray=%hu"}, + {{"zTXt", "Text:"}, ""} ///< TODO Need to complete representing this. +}; + +// Mr.compiler you are a liar: see this is constant. +typedef enum { + number_of_png_chunks_enum_hack=sizeof(png_chunk_names)/sizeof(png_chunk_names[0]) +} number_of_png_chunks_enum_hack_t; + +const unsigned short sizeof_of_png_chunk_details=sizeof(png_chunk_details_t)*number_of_png_chunks_enum_hack; + +/// The size of the PNG signature to check in the file. +const unsigned short PNG_BYTES_TO_CHECK=8; + +int +check_if_png(const char * const file_name, FILE ** const fp); + +private_png_read_data_ptr_t +init_png_data(void); + +private_png_read_data_ptr_t +read_png_data_from_file(FILE *fp); + +char const * +last_png_chunk_detail_item(void); + +private_png_read_data_ptr_t +init_png_data(void) +{ + private_png_read_data_ptr_t png_data=malloc(sizeof_of_png_chunk_details); + if (png_data) + { + unsigned short i; + // Make sure that unread chunks are NULL, as per the interface. + memset(png_data, 0, sizeof_of_png_chunk_details); + // Make sure the chunk names are set correctly, as per the interface. + for(i=0; iindex, image_background->red, image_background->green, image_background->blue, image_background->gray); + if (error<0) + { + png_data_read[png_chunk_names_bKGD].data=0; + } + } + } + + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_cHRM)) + { + double white_x=0.0, white_y=0.0, red_x=0.0, red_y=0.0, green_x=0.0, green_y=0.0, blue_x=0.0, blue_y=0.0; + if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + const int error=asprintf(&png_data_read[png_chunk_names_cHRM].data, png_chunk_details[png_chunk_names_cHRM].fmt_str, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); + if (error<0) + { + png_data_read[png_chunk_names_cHRM].data=0; + } + } + } + + { + int intent=0; + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_sRGB) && png_get_sRGB(read_ptr, read_info_ptr, &intent)) + { + int error; + switch (intent) + { + case PNG_sRGB_INTENT_PERCEPTUAL: + error=asprintf(&png_data_read[png_chunk_names_sRGB].data, png_chunk_details[png_chunk_names_sRGB].fmt_str, "perpetual"); + break; + case PNG_sRGB_INTENT_RELATIVE: + error=asprintf(&png_data_read[png_chunk_names_sRGB].data, png_chunk_details[png_chunk_names_sRGB].fmt_str, "relative"); + break; + case PNG_sRGB_INTENT_SATURATION: + error=asprintf(&png_data_read[png_chunk_names_sRGB].data, png_chunk_details[png_chunk_names_sRGB].fmt_str, "saturation"); + break; + case PNG_sRGB_INTENT_ABSOLUTE: + error=asprintf(&png_data_read[png_chunk_names_sRGB].data, png_chunk_details[png_chunk_names_sRGB].fmt_str, "absolute"); + break; + default: + error=asprintf(&png_data_read[png_chunk_names_sRGB].data, png_chunk_details[png_chunk_names_sRGB].fmt_str, "unknown"); + }; + if (error<0) + { + png_data_read[png_chunk_names_sRGB].data=0; + } + } + } + { + double image_gamma=0; + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_gAMA) && png_get_gAMA(read_ptr, read_info_ptr, &image_gamma)) + { + const int error=asprintf(&png_data_read[png_chunk_names_gAMA].data, png_chunk_details[png_chunk_names_gAMA].fmt_str, image_gamma); + if (error<0) + { + png_data_read[png_chunk_names_gAMA].data=0; + } + } + } + { + int num_palette=0; + png_colorp palette=NULL; + /* This reduces the image to the application supplied palette. */ + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_PLTE) && png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) + { + const int error=asprintf(&png_data_read[png_chunk_names_PLTE].data, png_chunk_details[png_chunk_names_PLTE].fmt_str, num_palette, palette->red, palette->green, palette->blue); + if (error<0) + { + png_data_read[png_chunk_names_PLTE].data=0; + } + } + } + { + /* This reduces the image to the palette supplied in the file. */ + png_uint_16p histogram=NULL; + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_hIST) && png_get_hIST(read_ptr, read_info_ptr, &histogram)) + { + const int error=asprintf(&png_data_read[png_chunk_names_hIST].data, png_chunk_details[png_chunk_names_hIST].fmt_str, *histogram); + if (error<0) + { + png_data_read[png_chunk_names_hIST].data=0; + } + } + } + { + png_timep mod_time=NULL; + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_tIME) && png_get_tIME(read_ptr, read_info_ptr, &mod_time)) + { + const int error=asprintf(&png_data_read[png_chunk_names_tIME].data, png_chunk_details[png_chunk_names_tIME].fmt_str, mod_time->year, mod_time->month, mod_time->day, mod_time->hour, mod_time->minute, mod_time->second); + if (error<0) + { + png_data_read[png_chunk_names_tIME].data=0; + } + } + } + { + png_uint_32 res_x=0, res_y=0; + int unit_type=PNG_RESOLUTION_UNKNOWN; + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_pHYs) && png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type)) + { + const int error=asprintf(&png_data_read[png_chunk_names_pHYs].data, png_chunk_details[png_chunk_names_pHYs].fmt_str, res_x, res_y, unit_type==PNG_RESOLUTION_UNKNOWN ? "unknown" : "pixels/meter"); + if (error<0) + { + png_data_read[png_chunk_names_pHYs].data=0; + } + } + } + { + png_color_8p sig_bit=NULL; + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_sBIT) && png_get_sBIT(read_ptr, read_info_ptr, &sig_bit)) + { + const int error=asprintf(&png_data_read[png_chunk_names_sBIT].data, png_chunk_details[png_chunk_names_sBIT].fmt_str, sig_bit->red, sig_bit->green, sig_bit->blue, sig_bit->gray, sig_bit->alpha); + if (error<0) + { + png_data_read[png_chunk_names_sBIT].data=0; + } + } + } + { + int unit=PNG_SCALE_UNKNOWN; + double width_sCAL=0.0, height_sCAL=0.0; + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_sCAL) && png_get_sCAL(read_ptr, read_info_ptr, &unit, &width_sCAL, &height_sCAL)) + { + int error; + switch (unit) + { + case PNG_SCALE_METER: + error=asprintf(&png_data_read[png_chunk_names_sCAL].data, png_chunk_details[png_chunk_names_sCAL].fmt_str, width_sCAL, height_sCAL, "meters"); + break; + case PNG_SCALE_RADIAN: + error=asprintf(&png_data_read[png_chunk_names_sCAL].data, png_chunk_details[png_chunk_names_sCAL].fmt_str, width_sCAL, height_sCAL, "radians"); + break; + default: + error=asprintf(&png_data_read[png_chunk_names_sCAL].data, png_chunk_details[png_chunk_names_sCAL].fmt_str, width_sCAL, height_sCAL, "unknown"); + } + if (error<0) + { + png_data_read[png_chunk_names_sCAL].data=0; + } + } + } + { + png_sPLT_tp entries=NULL; + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_sPLT) && png_get_sPLT(read_ptr, read_info_ptr, &entries)) + { + const int error=asprintf(&png_data_read[png_chunk_names_sPLT].data, png_chunk_details[png_chunk_names_sPLT].fmt_str, entries->name, entries->depth, entries->nentries); + if (error<0) + { + png_data_read[png_chunk_names_sPLT].data=0; + } + } + } + { + png_int_32 offset_x=0, offset_y=0; + int unit_type=PNG_OFFSET_LAST; + if (png_get_valid(read_ptr, read_info_ptr, PNG_INFO_oFFs) && png_get_oFFs(read_ptr, read_info_ptr, &offset_x, &offset_y, &unit_type)) + { + const int error=asprintf(&png_data_read[png_chunk_names_oFFs].data, png_chunk_details[png_chunk_names_oFFs].fmt_str, offset_x, offset_y, unit_type==PNG_OFFSET_PIXEL ? "pixel" : "micrometer"); + if (error<0) + { + png_data_read[png_chunk_names_oFFs].data=0; + } + } + } + + // Attempt to read in the text blocks in case the rest fails, so that at least we get the description bit. + { + const char key_separator[]=": "; + const char text_separator[]="\n"; + const unsigned short max_key_size=79; + png_textp text_ptr=NULL; + int num_text=0; + if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text)) + { + // TODO Need to handle compressed text parts correctly. + if (text_ptr->compression==PNG_TEXT_COMPRESSION_NONE_WR + || text_ptr->compression==PNG_TEXT_COMPRESSION_NONE + || text_ptr->compression==PNG_ITXT_COMPRESSION_NONE) + { + size_t total_size=(max_key_size+sizeof(key_separator)+sizeof(text_separator))*num_text; + int i; + for(i=0; iindex, trans_color->red, trans_color->green, trans_color->blue, trans_color->gray); + if (error<0) + { + png_data_read[png_chunk_names_tRNS].data=0; + } + } + } + // Don't read rest of file, in case there are corruptions in it, ignore the advice on the libpng website that it is required. + // Clean up after the read, and free any memory allocated - REQUIRED. + png_destroy_read_struct(&read_ptr, &read_info_ptr, (png_infopp)NULL); + return png_data_read; +} + +char const * +last_png_chunk_detail_item(void) +{ + return png_chunk_details[png_chunk_names_zTXt].trans.chunk; +} + +png_read_data_ptr_t +png_read_data_from_file(char const *filename) +{ + // Do some sanity checking on the logic of the design. If any of these fail there is a serious coding error in these data structures, that *must* be fixed before release. This is *not* a run-time check in release code. + assert((number_of_png_chunks_enum_hack_t)number_of_png_chunks==number_of_png_chunks_enum_hack); + assert(number_of_png_chunks_enum_hack==sizeof(png_chunk_details)/sizeof(png_chunk_fmt_t)); + assert((png_chunk_names_enum)(number_of_png_chunks_enum_hack-1)==png_chunk_names_zTXt); + + if (filename!=0 && filename!='\0') + { + FILE *fp=0; + if (check_if_png(filename, &fp)==0) + { + const private_png_read_data_ptr_t private_png_read_data_ptr=read_png_data_from_file(fp); + png_read_data_ptr_t png_read_data_ptr=(png_read_data_ptr_t)private_png_read_data_ptr; + fclose(fp); + return png_read_data_ptr; + } + else + { + fclose(fp); + return (png_read_data_ptr_t)NULL; + } + } + else + { + return (png_read_data_ptr_t)NULL; + } +} + +void +png_read_data_free_ptr(png_read_data_ptr_t png_data) +{ + if (png_data!=NULL) + { + const private_png_read_data_ptr_t png_chunk_data=(const private_png_read_data_ptr_t)png_data; + unsigned short i; + for(i=0; itrans.chunk, last_png_chunk_detail_item())!=0) + { + return ++png_chunk_data; + } + else + { + return (png_chunk_details_ptr_t)NULL;; + } + } + else + { + return (png_chunk_details_ptr_t)NULL; + } +} + +#endif diff --git a/plugins/thunar-apr/thunar-apr-png-read-data.h b/plugins/thunar-apr/thunar-apr-png-read-data.h new file mode 100644 index 00000000..e348ad0e --- /dev/null +++ b/plugins/thunar-apr/thunar-apr-png-read-data.h @@ -0,0 +1,119 @@ +/* $Id$ */ +/*- + * Copyright (c) 2013 Jason Hearne-McGuiness + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_PNG + +/** + * The list of valid chunk names. + * From http://www.libpng.org/pub/png/spec/register/pngreg-1.3.0-pdg.html + */ +extern char const * const png_chunk_names[]; +/* From the source file, to assist with identifying the chunks in the png_chunk_details_t collection. +={ + "IHDR", + "tIME", + "PLTE", + "bKGD", + "cHRM", + "fRAc", // No specification in PNG spec. + "gAMA", + "gIFg", // No specification in libpng headers. + "gIFt", // Deprecated, so not supported. + "gIFx", // No specification in libpng headers. + "hIST", + "iCCP", // Not yet implemented. + "iTXt", // Not yet implemented. + "oFFs", + "pCAL", // Not yet implemented. + "pHYs", + "sBIT", + "sCAL", + "sPLT", + "sRGB", + "sTER", // No specification in libpng headers. + "tEXt", + "tRNS", + "zTXt" // Not yet implemented. +};*/ + +enum { + number_of_png_chunks=24 +}; + +/// The structure that maps the name of the chunk into a human-readable name. +typedef struct +{ + char const *chunk; ///< A chunk name from png_chunk_names. + char const *name; ///< A human-readable chunk name derived from png_chunk_names. +} png_chunk_name_translate_t; +/** + * TODO This should really be private... + */ +typedef struct +{ + png_chunk_name_translate_t trans; ///< The translation of the chunk. + char *data; ///< The converted, human-readable data, if the chunk in the PNG file was present and read correctly, otherwise NULL. +} private_png_chunk_details_t; + +/** + * TODO This should really be private... + */ +typedef private_png_chunk_details_t* private_png_read_data_ptr_t; + +/// A pointer to the PNG data that has been read from the file. (TODO Would be nice it it was opaque, i.e. a void *.) +typedef private_png_chunk_details_t const * png_read_data_ptr_t; + +/// The structure that maps the name of the chunk to the data it contains. +typedef struct +{ + png_chunk_name_translate_t const trans; ///< The translation of the chunk. + char const *data; ///< The converted, human-readable data, if the chunk in the PNG file was present and read correctly, otherwise NULL. +} png_chunk_details_t; + +typedef png_chunk_details_t const * png_chunk_details_ptr_t; + +/// Returns an opaque pointer to the PNG data that has been read from the file. +/** + * \param filename The name of the file to be read, possibly in PNG format. The pointer can be NULL or an empty string. + * \return NULL if the PNG data could not be read from the input file. + */ +png_read_data_ptr_t png_read_data_from_file(char const *filename); + +/// Free any data allocated by png_read_data_from_file(). +/** + * \param png_data A pointer allocated by png_read_data_from_file(). NULL values are permitted to be passed. + */ +void png_read_data_free_ptr(png_read_data_ptr_t png_data); + +/// Get at the chunk data from the opaque pointer returned by png_read_data_from_file(). NULL values are permitted to be passed. +/** + * \param png_data The opaque pointer returned by png_read_data_from_file(). NULL values are permitted to be passed. + * \return A pointer the item at the beginning of the collection of png_chunk_details_t obtained from the PNG file, or NULL if invalid. + */ +png_chunk_details_ptr_t png_read_data_get_chunk_ptrs(png_read_data_ptr_t png_data); + +/// Get the next png_chunk_details_t from the collection of png_chunk_details_t returned from png_read_data_get_chunk_ptrs(). +/** + * \param png_chunk_data A pointer to an item in the collection of png_chunk_details_t returned by png_read_data_get_chunk_ptrs(). NULL values are permitted to be passed. + * \return A pointer to the next png_chunk_details_t in the collection, or NULL if the end of the collection has been reached or invalid. + */ +png_chunk_details_ptr_t png_read_data_next_item(png_chunk_details_ptr_t png_chunk_data); + +#endif -- 2.11.0