static void
append_exif_info (NemoImagePropertiesPage *page)
{
#ifdef HAVE_EXIF
	ExifData *exifdata;

	exifdata = exif_loader_get_data (page->details->exifldr);
	if (exifdata == NULL)
		return;

	if (exifdata->ifd[0] && exifdata->ifd[0]->count) {
                append_tag_value_pair (page, exifdata, EXIF_TAG_MAKE, _("Camera Brand"));
                append_tag_value_pair (page, exifdata, EXIF_TAG_MODEL, _("Camera Model"));

                /* Choose which date to show in order of relevance */
                if (!append_tag_value_pair (page, exifdata, EXIF_TAG_DATE_TIME_ORIGINAL, _("Date Taken"))) {
			if (!append_tag_value_pair (page, exifdata, EXIF_TAG_DATE_TIME_DIGITIZED, _("Date Digitized"))) {
				append_tag_value_pair (page, exifdata, EXIF_TAG_DATE_TIME, _("Date Modified"));
			}
		}

                append_tag_value_pair (page, exifdata, EXIF_TAG_EXPOSURE_TIME, _("Exposure Time"));
                append_tag_value_pair (page, exifdata, EXIF_TAG_APERTURE_VALUE, _("Aperture Value"));
                append_tag_value_pair (page, exifdata, EXIF_TAG_ISO_SPEED_RATINGS, _("ISO Speed Rating"));
                append_tag_value_pair (page, exifdata, EXIF_TAG_FLASH,_("Flash Fired"));
                append_tag_value_pair (page, exifdata, EXIF_TAG_METERING_MODE, _("Metering Mode"));
                append_tag_value_pair (page, exifdata, EXIF_TAG_EXPOSURE_PROGRAM, _("Exposure Program"));
                append_tag_value_pair (page, exifdata, EXIF_TAG_FOCAL_LENGTH,_("Focal Length"));
                append_tag_value_pair (page, exifdata, EXIF_TAG_SOFTWARE, _("Software"));
	}

	exif_data_unref (exifdata);
#endif
}
/**
 * Updates the sensor by reading the current value into it
 */
static void update(CAMERA_STATE *camera, struct sensor *sensor) {
    if (!sensor->enabled)
        return;

    struct state *state = (struct state *) sensor;

    if (!state->loader)
        state->loader = exif_loader_new();

    exif_loader_reset(state->loader);
    exif_loader_write(state->loader, camera->imagedata.buffer, camera->imagedata.pos);
    ExifData *data = exif_loader_get_data(state->loader);
    ExifByteOrder bo = exif_data_get_byte_order(data);

    int valid = get_shutter_speed(data, bo, &state->shutter_speed);

    if (valid)
        valid = get_aperture(data, bo, &state->aperture);

    if (valid)
        valid = getDouble(data, bo, EXIF_TAG_ISO_SPEED_RATINGS, &state->iso);

    exif_data_free(data);

    if (valid) {
        // Calculate the light level
        state->light_level = (2.0 * log(state->aperture) - log(state->shutter_speed) - log(state->iso / 100.0)) / log(2.0);

        // Update the sensor
        sensor_log(camera, &state->sensor, (int) (state->light_level * 1000), "Lvl %.1f", state->light_level);
    }
}
Example #3
0
	Exif::Exif(QString path) {
		ExifLoader *loader;

		loader = exif_loader_new();
		exif_loader_write_file(loader, path.toStdString().c_str());
		m_data = exif_loader_get_data(loader);
		exif_loader_unref(loader);
	}
Example #4
0
int main(int argc, char **argv)
{
    int rc = 1;
    ExifLoader *l;

    if (argc < 2) {
        printf("Usage: %s image.jpg\n", argv[0]);
        printf("Extracts a thumbnail from the given EXIF image.\n");
        return rc;
    }

    /* Create an ExifLoader object to manage the EXIF loading process */
    l = exif_loader_new();
    if (l) {
        ExifData *ed;

        /* Load the EXIF data from the image file */
        exif_loader_write_file(l, argv[1]);

        /* Get a pointer to the EXIF data */
        ed = exif_loader_get_data(l);

	/* The loader is no longer needed--free it */
        exif_loader_unref(l);
	l = NULL;
        if (ed) {
	    /* Make sure the image had a thumbnail before trying to write it */
            if (ed->data && ed->size) {
                FILE *thumb;
                char thumb_name[1024];

		/* Try to create a unique name for the thumbnail file */
                snprintf(thumb_name, sizeof(thumb_name),
                         "%s_thumb.jpg", argv[1]);

                thumb = fopen(thumb_name, "wb");
                if (thumb) {
		    /* Write the thumbnail image to the file */
                    fwrite(ed->data, 1, ed->size, thumb);
                    fclose(thumb);
                    printf("Wrote thumbnail to %s\n", thumb_name);
                    rc = 0;
                } else {
                    printf("Could not create file %s\n", thumb_name);
                    rc = 2;
                }
            } else {
                printf("No EXIF thumbnail in file %s\n", argv[1]);
                rc = 1;
            }
	    /* Free the EXIF data */
            exif_data_unref(ed);
        }
    }
    return rc;
}
ExifData *
exif_data_new_from_file (const char *path)
{
	ExifData *edata;
	ExifLoader *loader;

	loader = exif_loader_new ();
	exif_loader_write_file (loader, path);
	edata = exif_loader_get_data (loader);
	exif_loader_unref (loader);

	return (edata);
}
static void
load_finished (CajaImagePropertiesPage *page)
{
    GdkPixbufFormat *format;
    char *name, *desc;

    gtk_widget_destroy (page->details->loading_label);

    if (page->details->loader != NULL) {
        gdk_pixbuf_loader_close (page->details->loader, NULL);
    }

    if (page->details->got_size)
    {
#ifdef HAVE_EXIF
        ExifData *exif_data;
#endif /*HAVE_EXIF*/

        format = gdk_pixbuf_loader_get_format (page->details->loader);

        name = gdk_pixbuf_format_get_name (format);
        desc = gdk_pixbuf_format_get_description (format);
        append_label_take_str
        (page->details->vbox,
         g_strdup_printf ("<b>%s</b> %s (%s)",
                          _("Image Type:"), name, desc));
        append_label_take_str
        (page->details->vbox,
         g_strdup_printf (ngettext ("<b>Width:</b> %d pixel",
                                    "<b>Width:</b> %d pixels",
                                    page->details->width),
                          page->details->width));
        append_label_take_str
        (page->details->vbox,
         g_strdup_printf (ngettext ("<b>Height:</b> %d pixel",
                                    "<b>Height:</b> %d pixels",
                                    page->details->height),
                          page->details->height));
        g_free (name);
        g_free (desc);

#ifdef HAVE_EXIF
        exif_data = exif_loader_get_data (page->details->exifldr);
        append_exifdata_string (exif_data, page);
        exif_data_unref (exif_data);
#endif /*HAVE_EXIF*/
#ifdef HAVE_EXEMPI
        append_xmpdata_string (page->details->xmp, page);
#endif /*HAVE_EXEMPI*/
    }
    else
    {
        append_label (page->details->vbox,
                      _("Failed to load image information"));
    }

    if (page->details->loader != NULL)
    {
        g_object_unref (page->details->loader);
        page->details->loader = NULL;
    }
#ifdef HAVE_EXIF
    if (page->details->exifldr != NULL)
    {
        exif_loader_unref (page->details->exifldr);
        page->details->exifldr = NULL;
    }
#endif /*HAVE_EXIF*/
#ifdef HAVE_EXEMPI
    if (page->details->xmp != NULL)
    {
        xmp_free(page->details->xmp);
        page->details->xmp = NULL;
    }
#endif /*HAVE_EXEMPI*/
}
Example #7
0
int64_t
GetImageMetadata(const char *path, char *name)
{
	ExifData *ed;
	ExifEntry *e = NULL;
	ExifLoader *l;
	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	FILE *infile;
	int width=0, height=0, thumb=0;
	char make[32], model[64] = {'\0'};
	char b[1024];
	struct stat file;
	int64_t ret;
	image_s *imsrc;
	metadata_t m;
	uint32_t free_flags = 0xFFFFFFFF;
	memset(&m, '\0', sizeof(metadata_t));

	//DEBUG DPRINTF(E_DEBUG, L_METADATA, "Parsing %s...\n", path);
	if ( stat(path, &file) != 0 )
		return 0;
	strip_ext(name);
	//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * size: %jd\n", file.st_size);

	/* MIME hard-coded to JPEG for now, until we add PNG support */
	m.mime = strdup("image/jpeg");

	l = exif_loader_new();
	exif_loader_write_file(l, path);
	ed = exif_loader_get_data(l);
	exif_loader_unref(l);
	if( !ed )
		goto no_exifdata;

	e = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_ORIGINAL);
	if( e || (e = exif_content_get_entry(ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_DIGITIZED)) )
	{
		m.date = strdup(exif_entry_get_value(e, b, sizeof(b)));
		if( strlen(m.date) > 10 )
		{
			m.date[4] = '-';
			m.date[7] = '-';
			m.date[10] = 'T';
		}
		else {
			free(m.date);
			m.date = NULL;
		}
	}
	else {
		/* One last effort to get the date from XMP */
		image_get_jpeg_date_xmp(path, &m.date);
	}
	//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * date: %s\n", m.date);

	e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_MAKE);
	if( e )
	{
		strncpyt(make, exif_entry_get_value(e, b, sizeof(b)), sizeof(make));
		e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_MODEL);
		if( e )
		{
			strncpyt(model, exif_entry_get_value(e, b, sizeof(b)), sizeof(model));
			if( !strcasestr(model, make) )
				snprintf(model, sizeof(model), "%s %s", make, exif_entry_get_value(e, b, sizeof(b)));
			m.creator = escape_tag(trim(model), 1);
		}
	}
	//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * model: %s\n", model);

	e = exif_content_get_entry(ed->ifd[EXIF_IFD_0], EXIF_TAG_ORIENTATION);
	if( e )
	{
		int rotate;
		switch( exif_get_short(e->data, exif_data_get_byte_order(ed)) )
		{
		case 3:
			rotate = 180;
			break;
		case 6:
			rotate = 90;
			break;
		case 8:
			rotate = 270;
			break;
		default:
			rotate = 0;
			break;
		}
		if( rotate )
			xasprintf(&m.rotation, "%d", rotate);
	}

	if( ed->size )
	{
		/* We might need to verify that the thumbnail is 160x160 or smaller */
		if( ed->size > 12000 )
		{
			imsrc = image_new_from_jpeg(NULL, 0, (char *)ed->data, ed->size, 1, ROTATE_NONE);
			if( imsrc )
			{
 				if( (imsrc->width <= 160) && (imsrc->height <= 160) )
					thumb = 1;
				image_free(imsrc);
			}
		}
		else
		{
			thumb = 1;
			//- 20130708 Sungmin add
			if(ed->data && ed->size)
			{
				char* art_file;
				if( !thumb_cache_exists(path, &art_file) )
				{
					char cache_dir[MAXPATHLEN];
					strncpyt(cache_dir, art_file, sizeof(cache_dir));
					make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);

					FILE *thumb = fopen(art_file, "wb");
					//DPRINTF(E_WARN, L_METADATA, " * cache_dir: %s\n", cache_dir);
					//DPRINTF(E_WARN, L_METADATA, " * thumbnail: %s\n", art_file);
					if(thumb)
					{
						fwrite(ed->data, 1, ed->size, thumb);
						fclose(thumb);
					}
				}
				free(art_file);
			}
		}
	}
	//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * thumbnail: %d\n", thumb);

	exif_data_unref(ed);

no_exifdata:
	/* If SOF parsing fails, then fall through to reading the JPEG data with libjpeg to get the resolution */
	if( image_get_jpeg_resolution(path, &width, &height) != 0 || !width || !height )
	{
		infile = fopen(path, "r");
		if( infile )
		{
			cinfo.err = jpeg_std_error(&jerr);
			jerr.error_exit = libjpeg_error_handler;
			jpeg_create_decompress(&cinfo);
			if( setjmp(setjmp_buffer) )
				goto error;
			jpeg_stdio_src(&cinfo, infile);
			jpeg_read_header(&cinfo, TRUE);
			jpeg_start_decompress(&cinfo);
			width = cinfo.output_width;
			height = cinfo.output_height;
			error:
			jpeg_destroy_decompress(&cinfo);
			fclose(infile);
		}
	}
	//DEBUG DPRINTF(E_DEBUG, L_METADATA, " * resolution: %dx%d\n", width, height);

	if( !width || !height )
	{
		free_metadata(&m, free_flags);
		return 0;
	}
	if( width <= 640 && height <= 480 )
		m.dlna_pn = strdup("JPEG_SM");
	else if( width <= 1024 && height <= 768 )
		m.dlna_pn = strdup("JPEG_MED");
	else if( (width <= 4096 && height <= 4096) || !GETFLAG(DLNA_STRICT_MASK) )
		m.dlna_pn = strdup("JPEG_LRG");
	xasprintf(&m.resolution, "%dx%d", width, height);

	ret = sql_exec(db, "INSERT into DETAILS"
	                   " (PATH, TITLE, SIZE, TIMESTAMP, DATE, RESOLUTION,"
	                    " ROTATION, THUMBNAIL, CREATOR, DLNA_PN, MIME) "
	                   "VALUES"
	                   " (%Q, '%q', %lld, %ld, %Q, %Q, %Q, %d, %Q, %Q, %Q);",
	                   path, name, (long long)file.st_size, file.st_mtime, m.date, m.resolution,
	                   m.rotation, thumb, m.creator, m.dlna_pn, m.mime);
	if( ret != SQLITE_OK )
	{
		fprintf(stderr, "Error inserting details for '%s'!\n", path);
		ret = 0;
	}
	else
	{
		ret = sqlite3_last_insert_rowid(db);
	}
	free_metadata(&m, free_flags);

	return ret;
}
Example #8
0
/*------------------------------------------------------------------*
 * MODIFICATIONS:                                                   *
 *  Date        Description                         Author          *
 *============  ==================================  =============== *
 *                                                                  *
 *------------------------------------------------------------------*/
void ZImageThread::run()
{
	ZRequestList::iterator	vIt;
	ZImageThreadEvent		*vEvt;
#ifdef ZULU_EXIF
	ExifData				*vEd;
#endif
	bool					vExifHasThumb;
	qDebug("%s::Thread started", __FILE__);
	for(;;)
	{
		qDebug("%s::Loop entered", __FILE__);
		/*
		 * Wait until we are notified to begin processing
		 */
		mThreadWait.wait();
		if (mShutdown)
			break;

		/*
		 * We've got data
		 */
		qDebug("%s::Data to process", __FILE__);
		vIt = mRequests.begin();
		QPixmap *vPix;
		while (vIt != mRequests.end())
		{
			vExifHasThumb = false;
			vPix = NULL;

#ifdef ZULU_EXIF
			/*
			 * try to obtain thumbnail from exif
			 */
			ExifLoader *vLoader = exif_loader_new();
			exif_loader_write_file(vLoader, (*vIt).GetURI().latin1());
			vEd = exif_loader_get_data(vLoader);
			exif_loader_unref(vLoader);
			if (vEd == NULL)
			{
				qDebug("%s::%s does not contain EXIF data!", __FILE__, (*vIt).GetURI().latin1());
			}
			else
			{
				qDebug("%s::%s has EXIF data!", __FILE__, (*vIt).GetURI().latin1());
				if (vEd->data != NULL)
				{
					QImage vTemp;
					if (vTemp.loadFromData(vEd->data, vEd->size) == false)
						qDebug("%s::Unable to load pixmap", __FILE__);
					else
					{
						vPix = new QPixmap(vTemp.smoothScale((*vIt).GetWidth(), (*vIt).GetHeight(), QImage::ScaleMin));
						if (vPix == NULL)
							qDebug("%s::Unable to create pixmap", __FILE__);
						else
							vExifHasThumb = true;
					}
				}
			}
#endif
			/*
			 * Create the thumbnail
			 */
			if (vExifHasThumb == false)
			{
				QString vURI = (*vIt).GetURI();
				qDebug("%s::Image format => [%s]", __FILE__, QImage::imageFormat(vURI));
				qDebug("%s::Loading image %s", __FILE__, (*vIt).GetURI().latin1());
				QImageIO vImgIO;
				vImgIO.setFileName(vURI);
				if (vImgIO.read() == false)
				{
					qDebug("%s::Unable to read image", __FILE__);
					continue;
				}

				vPix = new QPixmap(vImgIO.image().scale((*vIt).GetWidth() , (*vIt).GetHeight(), QImage::ScaleMin));
				if (vPix == NULL)
					qDebug("%s::Unable to create pixmap from image", __FILE__);
			}

			/*
			 * Create and send the notification event
			 */
			vEvt = new ZImageThreadEvent((*vIt).GetKey(), vPix);
			delete vPix;
			vPix = NULL;
			QApplication::postEvent(mParent, vEvt);

			/*
			 * Now remove this request from the queue
			 */
			mListLock.lock();
			vIt = mRequests.erase(vIt);
			mListLock.unlock();
		}
	}
	qDebug("%s::Thread exiting", __FILE__);
}
    /**
     * Module execution function. Receives a pointer to a file the module is to
     * process. The file is represented by a TskFile interface which is used
     * to read the contents of the file and post extracted EXIF data to the  
     * database.
     *
     * @param pFile A pointer to a file.
     * @returns TskModule::OK on success, TskModule::FAIL on error.
     */
    TskModule::Status TSK_MODULE_EXPORT run(TskFile * pFile) 
    {
        if (pFile == NULL) 
        {
            LOGERROR(L"ExifExtractModule: passed NULL file pointer.");
            return TskModule::FAIL;
        }

        try 
        {
            char buffer[FILE_BUFFER_SIZE];
            int bytesRead = 0;

            memset(buffer, 0, FILE_BUFFER_SIZE);
            bytesRead = pFile->read(buffer, FILE_BUFFER_SIZE);

            if (bytesRead < 4)
                return TskModule::OK;

            // Check the first 4 bytes to see if this is a JPEG file.
            // We check for both the JFIF and EXIF signatures.
            if (memcmp(buffer, jfifSig, sizeof(jfifSig)) != 0 &&
                memcmp(buffer, exifSig, sizeof(exifSig)) != 0)
            {
                // It's not a JPEG file so we skip it.
                return TskModule::OK;
            }

            ExifLoader * exifLoader = exif_loader_new();

            if (exifLoader == NULL)
            {
                LOGERROR(L"ExifExtractModule - Received NULL ExifLoader pointer");
                return TskModule::FAIL;
            }

            // Feed the file content into libexif
            while (bytesRead > 0)
            {
                exif_loader_write(exifLoader, reinterpret_cast<unsigned char *>(buffer), bytesRead);
                memset(buffer, 0, FILE_BUFFER_SIZE);
                bytesRead = pFile->read(buffer, FILE_BUFFER_SIZE);
            }

            ExifData * exifData = exif_loader_get_data(exifLoader);

            // exifData will be NULL if there is no EXIF data in the image
            if (exifData != NULL)
            {
                // For debugging, exif_data_dump writes all exif data to stdout
                //exif_data_dump(exifData);

                extractExifData(exifData, pFile);

                exif_data_unref(exifData);
            }

            // Free the loader
            exif_loader_unref(exifLoader);
        }
        catch (TskException& tskEx)
        {
            std::wstringstream msg;
            msg << L"ExifExtractModule - Error processing file id " << pFile->getId() << L": " << tskEx.what();
            LOGERROR(msg.str());
            return TskModule::FAIL;
        }
        catch (std::exception& ex)
        {
            std::wstringstream msg;
            msg << L"ExifExtractModule - Error processing file id " << pFile->getId() << L": " << ex.what();
            LOGERROR(msg.str());
            return TskModule::FAIL;
        }

        return TskModule::OK;
    }
static void
append_xmp_value_pair (GString    *string,
		       XmpPtr      xmp,
		       const char *ns,
		       const char *propname,
		       char       *descr)
{
	uint32_t options;
	XmpStringPtr value;

	value = xmp_string_new();
#ifdef HAVE_EXEMPI_NEW_API
	if (xmp_get_property (xmp, ns, propname, value, &options)) {
#else
	if (xmp_get_property_and_bits (xmp, ns, propname, value, &options)) {
#endif
		if (XMP_IS_PROP_SIMPLE (options)) {
			g_string_append_printf (string,
						"<b>%s:</b> %s\n",
						descr,
						xmp_string_cstr (value));
		}
		else if (XMP_IS_PROP_ARRAY (options)) {
			XmpIteratorPtr iter;

			iter = xmp_iterator_new (xmp, ns, propname, XMP_ITER_JUSTLEAFNODES);
			if (iter) {
				gboolean first = TRUE;
				g_string_append_printf (string, "<b>%s:</b> ", descr);
				while (xmp_iterator_next (iter, NULL, NULL, value, &options) 
				       && !XMP_IS_PROP_QUALIFIER(options)) {
					if (!first) {
						g_string_append_printf (string, ", ");
					}
					else {
						first = FALSE;
					}
					g_string_append_printf (string,
								"%s",
								xmp_string_cstr(value));
				}
				xmp_iterator_free(iter);
				g_string_append_printf(string, "\n");
			}
		}
	}
	xmp_string_free(value);
}

static void
append_xmpdata_string(XmpPtr xmp, GString *string)
{
	if(xmp != NULL) {
		append_xmp_value_pair(string, xmp, NS_IPTC4XMP, "Location", _("Location"));
		append_xmp_value_pair(string, xmp, NS_DC, "description", _("Description"));
		append_xmp_value_pair(string, xmp, NS_DC, "subject", _("Keywords"));
		append_xmp_value_pair(string, xmp, NS_DC, "creator", _("Creator"));
		append_xmp_value_pair(string, xmp, NS_DC, "rights", _("Copyright"));
		append_xmp_value_pair(string, xmp, NS_XAP,"Rating", _("Rating"));
		/* TODO add CC licenses */
	}
}
#endif

static void
load_finished (NautilusImagePropertiesPage *page)
{
	GdkPixbufFormat *format;
	char *name, *desc;
	GString *str;

	if (page->details->got_size) {
#ifdef HAVE_EXIF
                ExifData *exif_data;
#endif

		str = g_string_new (NULL);
		format = gdk_pixbuf_loader_get_format (page->details->loader);
	
		name = gdk_pixbuf_format_get_name (format);
		desc = gdk_pixbuf_format_get_description (format);
		g_string_append_printf (str, "<b>%s</b> %s (%s)\n",
					_("Image Type:"), name, desc);
		g_string_append_printf (str, ngettext ("<b>Width:</b> %d pixel\n",
						       "<b>Width:</b> %d pixels\n",
						       page->details->width),
					page->details->width);
		g_string_append_printf (str, ngettext ("<b>Height:</b> %d pixel\n",
						       "<b>Height:</b> %d pixels\n",
						       page->details->height),
					page->details->height);
		g_free (name);
		g_free (desc);
		
#ifdef HAVE_EXIF
		exif_data = exif_loader_get_data (page->details->exifldr);
                append_exifdata_string (exif_data, str);
                exif_data_unref (exif_data);
#endif /*HAVE_EXIF*/
#ifdef HAVE_EXEMPI
		append_xmpdata_string(page->details->xmp, str);
#endif /*HAVE EXEMPI*/
		
		gtk_label_set_markup (GTK_LABEL (page->details->resolution), str->str);
		gtk_label_set_selectable (GTK_LABEL (page->details->resolution), TRUE);
		g_string_free (str, TRUE);
	} else {
		gtk_label_set_text (GTK_LABEL (page->details->resolution), _("Failed to load image information"));
	}

	if (page->details->loader != NULL) {
		gdk_pixbuf_loader_close (page->details->loader, NULL);
		g_object_unref (page->details->loader);
		page->details->loader = NULL;
	}
#ifdef HAVE_EXIF
	if (page->details->exifldr != NULL) {
		exif_loader_unref (page->details->exifldr);
		page->details->exifldr = NULL;
	}
#endif /*HAVE_EXIF*/
#ifdef HAVE_EXEMPI
	if (page->details->xmp != NULL) {
		xmp_free(page->details->xmp);
		page->details->xmp = NULL;
	}
#endif
}