#EXIF tag: get informations directly from the images.

In place of the default tags provided by SPIP for documents, we are going to extract the data stored in the image files with the help of new tags for SPIP.


With the new compiler, there is now the possibility to create personalized tags.

The integration in SPIP of the jpg EXIF metadata could be done with this. The data stored in the jpg file can be the time the photo has been taken, a copyright or GPS data for example.

All the code presented in this article has to be copied at the end of your file mes_fonctions.php3.

PHP provides functions to access these data. We will then start to write a little wrapper to access them on SPIP documents:

function verifier_JPG_TIFF($id_document) {
  	if ($id_document > 0) {
		$query = "SELECT id_type FROM spip_documents WHERE id_document = $id_document";
		$result = spip_query($query);
		if ($row = spip_fetch_array($result)) {
			$id_type = $row['id_type'];
	return (($id_type==1) || ($id_type==6));

function tag_exif($url_document,$section='',$tag='') {
  $to_ret = '';

  static $last_url;
  static $last_exif;

  if($last_url == $url_document) {
	$exif = $last_exif;
  } else {
	$exif = $last_exif =  @exif_read_data($url_document, 0, true);
	$last_url = $url_document;

  if($exif) {
	if(($section != '') && ($tag != '')) {
	  $to_ret = $exif[$section][$tag];
	} else if($section) {
	  if($exif[$section]) {
		foreach ($exif[$section] as $name => $val) {
		  $to_ret .= "<B>$section.$name</B>: $val<br />\n";
	} else {
	  foreach ($exif as $key => $section) {
		foreach ($section as $name => $val) {
		  $to_ret .= "<B>$key.$name</B>: $val<br />\n";
  return $to_ret;

-  The first function tests if a file can contains EXIF metadata (if it’s a TIFF or JPEG file).
-  The second one returns the field we are interested in. It is accessed with an argument giving the section where to look for it and another argument being the name of the field. These arguments are not mandatory and if not provided, a list of tag is returned [1].

The tag

A new tag #EXIF is declared. Here is a first simple version:

function balise_EXIF($params) {

  $id_doc = champ_sql('id_document', $params);

  $params->code = "(verifier_JPG_TIFF($id_doc))?(tag_exif(generer_url_document($id_doc),'$section','$tag')):''";
  $params->type = 'php';  

  return $params;

When this tag will be detected, the function
balise_exif will be called without any parameters. Hence, it will return a list of all the fields available in the file.

We begin by getting the name of the file attached to the document. For that we need the ID of the document. This is done by the sugar-function champ_sql that is a shortcut to index_pile:

index_pile returns the position in the stack of the SQL field $nom_champ. Starting from the nearest loop (given by $idb). If nothing is found, the field is considered to come from the outmost context (the URL or include) that is found in $Pile[0].
If the name references a SQL field, then it is stored in the $boucle structure to construct the minimal SQL query.

champ_sql will use $param to find the nearest loop, the second parameter is the name of the field: 'id_document'

The code for this tag must be run during the construction of the cache. Hence, we return a string containing the code to run later: "(verifier_JPG_TIFF($id_doc))?(tag_exif(generer_url_document($id_doc))):''"

To distinguish between php and pure html, the type of code is also specified by: $params->type = 'php';

Adding parameters

Well, this new tag is not really useful as is. In fact, we would prefer to extract only the field that we are interested in. But we do not really want to create one tag per field [2]. To avoid that, we will user parameters passed to this tag to select the right field.

Since version 1.8 [3], SPIP provides a simple function to retrieve parameters passed to a tag. This function is used in the following code. For earlier method of retrieving parameters, as for #EXPOSER|on,off, see the logo part of the code explained below.

The same method will be used to add two parameters to the #EXIF tag. For example:#EXIF{FILE,FileName} and #EXIF{IFD0,DateTime} will respectively retrieve the file name field from the File section and the date and time field from the IFD0 section (date and time the photo has been taken).

Here is the new code:

function balise_EXIF($params) {

  list($section,$tag) = split(',',param_balise($params));

  $section = addslashes($section);
  $tag = addslashes($tag);

  $id_doc = champ_sql('id_document', $params);

  $params->code = "(verifier_JPG_TIFF($id_doc))?(tag_exif(generer_url_document($id_doc),'$section','$tag')):''";
  $params->type = 'php';
  return $params;

The difference with the code returned by the previous version is small. However, the parameters $section and $tag are retrieved from the filter list (params->fonctions) to be passed (if found) to the function tag_exif.

A tag for a thumbnail

In the EXIF metadata from a jpg image, there is — often — a thumbnail of the image [4]. This thumbnail is not of the best quality, but when you are unable to install any image processing plugin in PHP it is a good start.

Lets begin with two new tool functions:
-  generer_url_logo_EXIF look for the file name_file.exif.jpg on the server. If this one is not find, the thumbnail is retrieved from the image and written to disk. The name of the thumbnail file is returned at the end.
-  generer_html_logo_EXIF create the html code for the thumbnail, with alignment, link, etc...

function generer_url_logo_EXIF($filename) {

  $thumbname = substr($filename,0,-4).".exif.jpg";

  if(file_exists($thumbname)) {
	return $thumbname;

  $image = exif_thumbnail($filename);
  if ($image!==false) {
	$handle = fopen ($thumbname, 'a');
	fwrite($handle, $image);
	return $thumbname;
  return '';


function generer_html_logo_EXIF($url_doc,$code_lien='',$align='') {
            return '';
  if($code_lien) {
	$code = "<a href=\"$code_lien\">";

  $code .= "<IMG src=\"$url_doc\"".(($align)?"align=$align":'').">";
  if($code_lien) {
	$code .= "</a>";

  return $code;

This new tag should have the same behavior as the other LOGO tags in SPIP. Therefor the alignment and link filters should be implemented properly.

The principle is the same as before. The list of filters is parsed for one of the following:
-  an alignment,
-  fichier to return the name of the file,
-  a link to put around the thumbnail.

The first part of the code looks at the filters in $p->fonctions.
Then, the code for the link is generated if necessary. As another tag can be given as a link — e.g. [(#LOGO_EXIF|#URL_DOCUMENT)] — we call the function calculer_champ to compute the content of this tag.

function balise_LOGO_EXIF($p) {

  // parse the filters
  $flag_fichier = false;
  $filtres = '';
  if (is_array($p->fonctions)) {
	foreach($p->fonctions as $nom) {
	  if (ereg('^(left|right|center|top|bottom)$', $nom))
		$align = $nom;
	  else if ($nom == 'lien') {
		$flag_lien_auto = true;
		$flag_stop = true;
	  else if ($nom == 'fichier') {
		$flag_fichier = true;
		$flag_stop = true;
	  // double || means we get a real filter
	  else if ($nom == '')
		$flag_stop = true;
	  else if (!$flag_stop) {
		$lien = $nom;
		$flag_stop = true;
	  // after an URL or || or |fichier we only get
	  // filters (except left...lien...fichier)
		$filtres[] = $nom;
	// return the other filters
	$p->fonctions = $filtres;

  $id_doc = champ_sql('id_document', $p);
  $code_lien = '';

  $url_doc = "(verifier_JPG_TIFF($id_doc))?(generer_url_logo_EXIF(generer_url_document($id_doc))):''";

	// Prepare the code for the link
	// 1. filter |lien
  if ($flag_lien_auto AND !$lien) {
	 $code_lien = "$url_doc";
  } else if ($lien) {
		// 2. raw link (possible SPIP tags in them : print#ID_ARTICLE.html)

		$code_lien = "'".texte_script(trim($lien))."'";
		while (ereg("^([^#]*)#([A-Za-z_]+)(.*)$", $code_lien, $match)) {
			$c = calculer_champ(array(), $match[2], $p->id_boucle, $p->boucles, $p->id_mere);
			$code_lien = str_replace('#'.$match[2], "'.".$c.".'", $code_lien);
		// supprimer les '' disgracieux
		$code_lien = ereg_replace("^''\.|\.''$", "", $code_lien);

  if($flag_fichier) {
	$p->code = "ereg_replace(\"^IMG/\",\"\",$url_doc)";
	$p->type = 'php';
	return $p;

	$code_lien = "''";

	$align = "''";

  $p->code = "generer_html_logo_EXIF($url_doc,$code_lien,$align)";
  $p->type = 'php';
  return $p;

A filter for the dates

The dates stored in the images are not in the same format as the one used by SPIP. Hence, it is not possible to use the filters on dates from SPIP. Here is a filter to apply before any date filter from SPIP:

function date_EXIF2SPIP($date) {
  return preg_replace('/^([0-9]*):([0-9]*):([0-9]*) /','\1-\2-\3 ',$date);

Application example

Here is a little loop that list all the documents of an article with some basic informations extracted from the EXIF metatags:

<BOUCLE_jpg(DOCUMENTS) {id_article} {mode=document} {extension=jpg}>
[<li>Model: (#EXIF{IFD0,Make})
[<li>Date: (#EXIF{EXIF,DateTimeOriginal}|date_EXIF2SPIP|sinon{#DATE}|affdate)
[<li>Comment: (#EXIF{COMMENT,0}|sinon{#DESCRIPTIF})</li>]

[<li>Exposure Time: (#EXIF{EXIF,ExposureTime})</li>]

Development version

This contrib is managed on spip-zone. You can checkout the last version with:

svn checkout svn://zone.spip.org/spip-zone/_contrib_/_balises_/exif/trunk/


[1A little cache in memory allow the system to read the data from the file only once when there is a consecutive access to the same image.

[2the name and number of fields being variable

[3cvs version of 17 Dec. 2004

[4This is generated by the camera for visualization on the camera screen, for example.


No discussion

Add a comment

Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :

  • Désactiver tous les plugins que vous ne voulez pas tester afin de vous assurer que le bug vient bien du plugin X. Cela vous évitera d’écrire sur le forum d’une contribution qui n’est finalement pas en cause.
  • Cherchez et notez les numéros de version de tout ce qui est en place au moment du test :
    • version de SPIP, en bas de la partie privée
    • version du plugin testé et des éventuels plugins nécessités
    • version de PHP (exec=info en partie privée)
    • version de MySQL / SQLite / PostgreSQL
  • Si votre problème concerne la partie publique de votre site, donnez une URL où le bug est visible, pour que les gens puissent voir par eux-mêmes.
  • En cas de page blanche, merci d’activer l’affichage des erreurs, et d’indiquer ensuite l’erreur qui apparait.

Merci d’avance pour les personnes qui vous aideront !

Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.

Who are you?
[Log in]

To show your avatar with your message, register it first on gravatar.com (free et painless) and don’t forget to indicate your Email addresse here.

Enter your comment here

This form accepts SPIP shortcuts {{bold}} {italic} -*list [text->url] <quote> <code> and HTML code <q> <del> <ins>. To create paragraphs, just leave empty lines.

Add a document

Follow the comments: RSS 2.0 | Atom