tracker-extract-png.c 7.07 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Copyright (C) 2006, Mr Jamie McCracken (jamiemcc@gnome.org)
 * Copyright (C) 2008, Nokia
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

#include "config.h"

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <fcntl.h>
#include <string.h>
30
#include <stdlib.h>
31 32 33 34 35 36 37 38 39
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <png.h>

#include <glib.h>
#include <glib/gstdio.h>

40 41
#include <libtracker-common/tracker-type-utils.h>

42
#include "tracker-main.h"
43
#include "tracker-xmp.h"
44
#include "tracker-escape.h"
45 46 47 48 49 50

#define RFC1123_DATE_FORMAT "%d %B %Y %H:%M:%S %z"

typedef gchar * (*PostProcessor) (gchar *);

typedef struct {
51 52
	const gchar   *name;
	const gchar   *type;
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
	PostProcessor  post;
} TagProcessors;

static gchar *rfc1123_to_iso8601_date (gchar	   *rfc_date);
static void   extract_png	      (const gchar *filename,
				       GHashTable  *metadata);

static TagProcessors tag_processors[] = {
	{ "Author",		"Image:Creator",      NULL},
	{ "Creator",		"Image:Creator",      NULL},
	{ "Description",	"Image:Description",  NULL},
	{ "Comment",		"Image:Comments",     NULL},
	{ "Copyright",		"File:Copyright",     NULL},
	{ "Creation Time",	"Image:Date",	      rfc1123_to_iso8601_date},
	{ "Title",		"Image:Title",	      NULL},
	{ "Software",		"Image:Software",     NULL},
	{ "Disclaimer",		"File:License",       NULL},
	{ NULL,			NULL,		      NULL},
};

73
static TrackerExtractData data[] = {
74 75 76 77 78
	{ "image/png", extract_png },
	{ NULL, NULL }
};

static gchar *
79
rfc1123_to_iso8601_date (gchar *date)
80 81 82 83
{
	/* From: ex. RFC1123 date: "22 May 1997 18:07:10 -0600"
	 * To  : ex. ISO8601 date: "2007-05-22T18:07:10-0600"
	 */
84
	return tracker_date_format_to_iso8601 (date, RFC1123_DATE_FORMAT);
85 86
}

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
static void
read_metadata (png_structp png_ptr, png_infop info_ptr, GHashTable *metadata)
{
	gint	     num_text;
	png_textp    text_ptr;

	if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_text) > 0) {
		gint i;
		gint j;
		
		for (i = 0; i < num_text; i++) {
			if (!text_ptr[i].key) {
				continue;
			}
			
#if defined(HAVE_EXEMPI) && defined(PNG_iTXt_SUPPORTED)
			if (strcmp ("XML:com.adobe.xmp", text_ptr[i].key) == 0) {
				tracker_read_xmp (text_ptr[i].text,
						  text_ptr[i].itxt_length,
						  metadata);
				continue;
			}
#endif
			
			for (j = 0; tag_processors[j].type; j++) {
				if (strcasecmp (tag_processors[j].name, text_ptr[i].key) != 0) {
					continue;
				}
				
				if (text_ptr[i].text && text_ptr[i].text[0] != '\0') {
					if (tag_processors[j].post) {
						gchar *str;
						
						str = (*tag_processors[j].post) (text_ptr[i].text);
						if (str) {
							g_hash_table_insert (metadata,
									     g_strdup (tag_processors[j].type),
124 125
									     tracker_escape_metadata (str));
							g_free (str);
126 127 128 129
						}
					} else {
						g_hash_table_insert (metadata,
								     g_strdup (tag_processors[j].type),
130
								     tracker_escape_metadata (text_ptr[i].text));
131 132 133 134 135 136 137 138 139
					}
					
					break;
				}
			}
		}
	}
}

140 141 142 143
static void
extract_png (const gchar *filename,
	     GHashTable  *metadata)
{
144 145 146
	struct stat  fstatbuf;
	size_t	     size;

147 148 149 150
	gint	     fd_png;
	FILE	    *png;
	png_structp  png_ptr;
	png_infop    info_ptr;
151 152 153 154
	png_infop    end_ptr;
	png_bytepp   row_pointers;
	guint        row;

155 156 157 158 159
	png_uint_32  width, height;
	gint	     bit_depth, color_type;
	gint	     interlace_type, compression_type, filter_type;

#if defined(__linux__)
160 161
	if (((fd_png = g_open (filename, (O_RDONLY | O_NOATIME))) == -1) &&
	    ((fd_png = g_open (filename, (O_RDONLY))) == -1 ) ) {
162 163 164 165 166 167
#else
	if ((fd_png = g_open (filename, O_RDONLY)) == -1) {
#endif
		return;
	}

168 169 170 171 172 173 174 175 176 177 178 179
	if (stat (filename, &fstatbuf) == -1) {
		close(fd_png);
		return;
	}

	/* Check for minimum header size */
	size = fstatbuf.st_size;
	if (size < 64) {
		close (fd_png);
		return;
	}

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
	if ((png = fdopen (fd_png, "r"))) {
		png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
						  NULL,
						  NULL,
						  NULL);
		if (!png_ptr) {
			fclose (png);
			return;
		}

		info_ptr = png_create_info_struct (png_ptr);
		if (!info_ptr) {
			png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
			fclose (png);
			return;
		}

197 198 199 200 201 202 203
		end_ptr = png_create_info_struct (png_ptr);
		if (!info_ptr) {
			png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
			png_destroy_read_struct (&png_ptr, &end_ptr, NULL);
			fclose (png);
			return;
		}
204

205 206 207 208 209 210 211 212
		if (setjmp(png_jmpbuf(png_ptr))) {
			png_destroy_read_struct (&png_ptr, &info_ptr,
						 (png_infopp)NULL);
			png_destroy_read_struct (&png_ptr, &end_ptr,
						 (png_infopp)NULL);
			fclose (png);
			return;
		}
213

214 215
		png_init_io (png_ptr, png);
		png_read_info (png_ptr, info_ptr);
216

217 218 219 220 221 222 223 224 225 226 227 228 229
		if (!png_get_IHDR (png_ptr,
				   info_ptr,
				   &width,
				   &height,
				   &bit_depth,
				   &color_type,
				   &interlace_type,
				   &compression_type,
				   &filter_type)) {
			png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
			png_destroy_read_struct (&png_ptr, &end_ptr, NULL);
			fclose (png);
			return;
230
		}
231 232 233
		
		/* Read the image. FIXME We should be able to skip this step and
		 * just get the info from the end. This causes some errors atm.
234
		 */
235
		row_pointers = (png_bytepp) malloc (height * sizeof (png_bytep));		
236
		for (row = 0; row < height; row++) {
237 238
			row_pointers[row] = png_malloc (png_ptr,
							png_get_rowbytes(png_ptr,info_ptr));
239
		}
240

241
		png_read_image (png_ptr, row_pointers);
242

243 244
		for (row = 0; row < height; row++) {
			png_free (png_ptr, row_pointers[row]);
245
		}
246 247 248 249 250 251 252 253 254 255 256
 		g_free (row_pointers);

		png_read_end (png_ptr, end_ptr);

 		read_metadata (png_ptr, info_ptr, metadata);
 		read_metadata (png_ptr, end_ptr, metadata);
		
		/* We want native have higher priority than XMP etc.
		 */
		g_hash_table_insert (metadata,
				     g_strdup ("Image:Width"),
257
				     tracker_escape_metadata_printf ("%ld", width));
258 259
		g_hash_table_insert (metadata,
				     g_strdup ("Image:Height"),
260
				     tracker_escape_metadata_printf ("%ld", height));
261
		
262 263 264 265 266 267
		/* Check that we have the minimum data. FIXME We should not need to do this */

		if (!g_hash_table_lookup (metadata, "Image:Date")) {
			struct stat st;

			if (g_lstat(filename, &st) >= 0) {
268
				gchar *date;
269

270 271 272 273 274 275
				date = tracker_date_to_string (st.st_mtime);

				g_hash_table_insert (metadata,
						     g_strdup ("Image:Date"),
						     tracker_escape_metadata (date));
				g_free (date);
276 277 278
			}
		}

279 280 281 282 283 284 285
		png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
		fclose (png);
	} else {
		close (fd_png);
	}
}

286 287
TrackerExtractData *
tracker_get_extract_data (void)
288 289 290
{
	return data;
}