Home | History | Annotate | Download | only in libexif
      1 /* exif-content.c
      2  *
      3  * Copyright (c) 2001 Lutz Mueller <lutz (at) users.sourceforge.net>
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Lesser General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Lesser General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Lesser General Public
     16  * License along with this library; if not, write to the
     17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA  02110-1301  USA.
     19  */
     20 
     21 #include <config.h>
     22 
     23 #include <libexif/exif-content.h>
     24 #include <libexif/exif-system.h>
     25 
     26 #include <stdlib.h>
     27 #include <stdio.h>
     28 #include <string.h>
     29 
     30 /* unused constant
     31  * static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
     32  */
     33 
     34 struct _ExifContentPrivate
     35 {
     36 	unsigned int ref_count;
     37 
     38 	ExifMem *mem;
     39 	ExifLog *log;
     40 };
     41 
     42 ExifContent *
     43 exif_content_new (void)
     44 {
     45 	ExifMem *mem = exif_mem_new_default ();
     46 	ExifContent *content = exif_content_new_mem (mem);
     47 
     48 	exif_mem_unref (mem);
     49 
     50 	return content;
     51 }
     52 
     53 ExifContent *
     54 exif_content_new_mem (ExifMem *mem)
     55 {
     56 	ExifContent *content;
     57 
     58 	if (!mem) return NULL;
     59 
     60 	content = exif_mem_alloc (mem, (ExifLong) sizeof (ExifContent));
     61 	if (!content)
     62 		return NULL;
     63 	content->priv = exif_mem_alloc (mem,
     64 				(ExifLong) sizeof (ExifContentPrivate));
     65 	if (!content->priv) {
     66 		exif_mem_free (mem, content);
     67 		return NULL;
     68 	}
     69 
     70 	content->priv->ref_count = 1;
     71 
     72 	content->priv->mem = mem;
     73 	exif_mem_ref (mem);
     74 
     75 	return content;
     76 }
     77 
     78 void
     79 exif_content_ref (ExifContent *content)
     80 {
     81 	content->priv->ref_count++;
     82 }
     83 
     84 void
     85 exif_content_unref (ExifContent *content)
     86 {
     87 	content->priv->ref_count--;
     88 	if (!content->priv->ref_count)
     89 		exif_content_free (content);
     90 }
     91 
     92 void
     93 exif_content_free (ExifContent *content)
     94 {
     95 	ExifMem *mem = (content && content->priv) ? content->priv->mem : NULL;
     96 	unsigned int i;
     97 
     98 	if (!content) return;
     99 
    100 	for (i = 0; i < content->count; i++)
    101 		exif_entry_unref (content->entries[i]);
    102 	exif_mem_free (mem, content->entries);
    103 
    104 	if (content->priv) {
    105 		exif_log_unref (content->priv->log);
    106 	}
    107 
    108 	exif_mem_free (mem, content->priv);
    109 	exif_mem_free (mem, content);
    110 	exif_mem_unref (mem);
    111 }
    112 
    113 void
    114 exif_content_dump (ExifContent *content, unsigned int indent)
    115 {
    116 	char buf[1024];
    117 	unsigned int i;
    118 
    119 	for (i = 0; i < 2 * indent; i++)
    120 		buf[i] = ' ';
    121 	buf[i] = '\0';
    122 
    123 	if (!content)
    124 		return;
    125 
    126 	printf ("%sDumping exif content (%u entries)...\n", buf,
    127 		content->count);
    128 	for (i = 0; i < content->count; i++)
    129 		exif_entry_dump (content->entries[i], indent + 1);
    130 }
    131 
    132 void
    133 exif_content_add_entry (ExifContent *c, ExifEntry *entry)
    134 {
    135 	ExifEntry **entries;
    136 	if (!c || !c->priv || !entry || entry->parent) return;
    137 
    138 	/* One tag can only be added once to an IFD. */
    139 	if (exif_content_get_entry (c, entry->tag)) {
    140 		exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "ExifContent",
    141 			"An attempt has been made to add "
    142 			"the tag '%s' twice to an IFD. This is against "
    143 			"specification.", exif_tag_get_name (entry->tag));
    144 		return;
    145 	}
    146 
    147 	entries = exif_mem_realloc (c->priv->mem,
    148 		c->entries, sizeof (ExifEntry*) * (c->count + 1));
    149 	if (!entries) return;
    150 	entry->parent = c;
    151 	entries[c->count++] = entry;
    152 	c->entries = entries;
    153 	exif_entry_ref (entry);
    154 }
    155 
    156 void
    157 exif_content_remove_entry (ExifContent *c, ExifEntry *e)
    158 {
    159 	unsigned int i;
    160 	ExifEntry **t, *temp;
    161 
    162 	if (!c || !c->priv || !e || (e->parent != c)) return;
    163 
    164 	/* Search the entry */
    165 	for (i = 0; i < c->count; i++)
    166 			if (c->entries[i] == e)
    167 					break;
    168 
    169 	if (i == c->count)
    170 			return;
    171 
    172 	/* Remove the entry */
    173 	temp = c->entries[c->count-1];
    174 	if (c->count > 1) {
    175 		t = exif_mem_realloc (c->priv->mem, c->entries,
    176 					sizeof(ExifEntry*) * (c->count - 1));
    177 		if (!t) {
    178 			return;
    179 		}
    180 		c->entries = t;
    181 		c->count--;
    182 		if (i != c->count) { /* we deallocated the last slot already */
    183 			memmove (&t[i], &t[i + 1], sizeof (ExifEntry*) * (c->count - i - 1));
    184 			t[c->count-1] = temp;
    185 		}
    186 	} else {
    187 		exif_mem_free (c->priv->mem, c->entries);
    188 		c->entries = NULL;
    189 		c->count = 0;
    190 	}
    191 	e->parent = NULL;
    192 	exif_entry_unref (e);
    193 }
    194 
    195 ExifEntry *
    196 exif_content_get_entry (ExifContent *content, ExifTag tag)
    197 {
    198 	unsigned int i;
    199 
    200 	if (!content)
    201 		return (NULL);
    202 
    203 	for (i = 0; i < content->count; i++)
    204 		if (content->entries[i]->tag == tag)
    205 			return (content->entries[i]);
    206 	return (NULL);
    207 }
    208 
    209 void
    210 exif_content_foreach_entry (ExifContent *content,
    211 			    ExifContentForeachEntryFunc func, void *data)
    212 {
    213 	unsigned int i;
    214 
    215 	if (!content || !func)
    216 		return;
    217 
    218 	for (i = 0; i < content->count; i++)
    219 		func (content->entries[i], data);
    220 }
    221 
    222 void
    223 exif_content_log (ExifContent *content, ExifLog *log)
    224 {
    225 	if (!content || !content->priv || !log || content->priv->log == log)
    226 		return;
    227 
    228 	if (content->priv->log) exif_log_unref (content->priv->log);
    229 	content->priv->log = log;
    230 	exif_log_ref (log);
    231 }
    232 
    233 ExifIfd
    234 exif_content_get_ifd (ExifContent *c)
    235 {
    236 	if (!c || !c->parent) return EXIF_IFD_COUNT;
    237 
    238 	return
    239 		((c)->parent->ifd[EXIF_IFD_EXIF] == (c)) ? EXIF_IFD_EXIF :
    240 		((c)->parent->ifd[EXIF_IFD_0] == (c)) ? EXIF_IFD_0 :
    241 		((c)->parent->ifd[EXIF_IFD_1] == (c)) ? EXIF_IFD_1 :
    242 		((c)->parent->ifd[EXIF_IFD_GPS] == (c)) ? EXIF_IFD_GPS :
    243 		((c)->parent->ifd[EXIF_IFD_INTEROPERABILITY] == (c)) ? EXIF_IFD_INTEROPERABILITY :
    244 		EXIF_IFD_COUNT;
    245 }
    246 
    247 static void
    248 fix_func (ExifEntry *e, void *UNUSED(data))
    249 {
    250 	exif_entry_fix (e);
    251 }
    252 
    253 /*!
    254  * Check if this entry is unknown and if so, delete it.
    255  * \note Be careful calling this function in a loop. Deleting an entry from
    256  * an ExifContent changes the index of subsequent entries, as well as the
    257  * total size of the entries array.
    258  */
    259 static void
    260 remove_not_recorded (ExifEntry *e, void *UNUSED(data))
    261 {
    262 	ExifIfd ifd = exif_entry_get_ifd(e) ;
    263 	ExifContent *c = e->parent;
    264 	ExifDataType dt = exif_data_get_data_type (c->parent);
    265 	ExifTag t = e->tag;
    266 
    267 	if (exif_tag_get_support_level_in_ifd (t, ifd, dt) ==
    268 			 EXIF_SUPPORT_LEVEL_NOT_RECORDED) {
    269 		exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "exif-content",
    270 				"Tag 0x%04x is not recorded in IFD '%s' and has therefore been "
    271 				"removed.", t, exif_ifd_get_name (ifd));
    272 		exif_content_remove_entry (c, e);
    273 	}
    274 
    275 }
    276 
    277 void
    278 exif_content_fix (ExifContent *c)
    279 {
    280 	ExifIfd ifd = exif_content_get_ifd (c);
    281 	ExifDataType dt;
    282 	ExifEntry *e;
    283 	unsigned int i, num;
    284 
    285 	if (!c)
    286 		return;
    287 
    288 	dt = exif_data_get_data_type (c->parent);
    289 
    290 	/*
    291 	 * First of all, fix all existing entries.
    292 	 */
    293 	exif_content_foreach_entry (c, fix_func, NULL);
    294 
    295 	/*
    296 	 * Go through each tag and if it's not recorded, remove it. If one
    297 	 * is removed, exif_content_foreach_entry() will skip the next entry,
    298 	 * so if this happens do the loop again from the beginning to ensure
    299 	 * they're all checked. This could be avoided if we stop relying on
    300 	 * exif_content_foreach_entry but loop intelligently here.
    301 	 */
    302 	do {
    303 		num = c->count;
    304 		exif_content_foreach_entry (c, remove_not_recorded, NULL);
    305 	} while (num != c->count);
    306 
    307 	/*
    308 	 * Then check for non-existing mandatory tags and create them if needed
    309 	 */
    310 	num = exif_tag_table_count();
    311 	for (i = 0; i < num; ++i) {
    312 		const ExifTag t = exif_tag_table_get_tag (i);
    313 		if (exif_tag_get_support_level_in_ifd (t, ifd, dt) ==
    314 			EXIF_SUPPORT_LEVEL_MANDATORY) {
    315 			if (exif_content_get_entry (c, t))
    316 				/* This tag already exists */
    317 				continue;
    318 			exif_log (c->priv->log, EXIF_LOG_CODE_DEBUG, "exif-content",
    319 					"Tag '%s' is mandatory in IFD '%s' and has therefore been added.",
    320 					exif_tag_get_name_in_ifd (t, ifd), exif_ifd_get_name (ifd));
    321 			e = exif_entry_new ();
    322 			exif_content_add_entry (c, e);
    323 			exif_entry_initialize (e, t);
    324 			exif_entry_unref (e);
    325 		}
    326 	}
    327 }
    328