Balise #EXIF : récupérer les informations stockées dans les images.

Attention, page complètement obsolète, qui devrait être dépubliée : liens brisés, etc.

Au lieu d’utiliser les balises par défaut de SPIP pour la date, la description, le titre etc... d’une image JPG, on peut utiliser les informations stoquées à l’intérieur du fichier par les appareils photos et autres gestionaires de photos.

But

Avec le nouveau compilo, on peut créer des balises personnalisées. Sur la liste des utilisateurs, il y a eu une demande pour intégrer la lecture des metadata EXIF dans les fichiers jpg. Ces données permettent d’obtenir des informations sur le fichiers jpg (souvent annotée par les appareils photo digitaux) comme l’heure de prise de la photo, des commentaires, un copyright, etc...

Le code présenté ici doit être placé dans votre fichier mes_fonctions.php3.

php fournit une fonction pour accéder à ces données. On va commencer par coder deux petits wrappers pour l’utiliser simplement dans spip :

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;
}

-  La première fonction teste si un fichier peut contenir des tags EXIF (à la base, si c’est un fichier jpg ou tiff)
-  La deuxième retourne un champ dans les metadata. On y accède en passant une section et un tag qui nous intéresse dans cette section. Si on ne donne pas de section et de tag, alors la fonction retourne une liste de tous les tags existant dans le fichier [1].

La balise

On va donc déclarer une nouvelle balise #EXIF qui retourne les metadata. Commençons par une version simple de cette balise :

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;
}

Cette balise, en appelant tag_exif sans paramètres section et tag, va retourner une liste de tous les tags EXIF existants.

Avant tout, il nous faut récupérer le nom de fichier du document qui nous intéresse. Mais pour cela, on a besoin de l’ID du document. Le paramètre $param nous permet d’aller récupérer cette information. SPIP fournit une fonction pour simplifier la tâche : champ_sql retourne le code qui va nous donner la valeur d’id_document.

En fait, champ_sql est un raccourci pour index_pile :

index_pile retourne la position dans la pile du champ SQL $nom_champ en prenant la boucle la plus proche du sommet de pile (indique par $idb). Si on ne trouve rien, on considère que ça doit provenir du contexte (par l’URL ou l’include) qui a été recopié dans Pile[0] (un essai d’affinage a debouché sur un bug vicieux). Si ca référence un champ SQL, on le mémorise dans la structure $boucles afin de construire un requête SQL minimale (plutot qu’un brutal ’SELECT *’)

champ_sql va utiliser $param pour déduire « la boucle la plus proche », le 2e paramètre spécifie que l’on veut récupérer le champ 'id_document'

Le code que l’on veut exécuter pour cette balise ne doit pas être exécuté maintenant, mais dans le cache. Il faut donc retourner une chaîne contenant le code que l’on veut exécuter : "(verifier_JPG_TIFF($id_doc))?(tag_exif(generer_url_document($id_doc))):''"

Il faut aussi specifier à spip qu’on retourne du code php à executer — contrairement à du code html brut — en mettant : $params->type = 'php';

Passer des paramètres

Bon, il est clair que cette balise n’est pas très intéressante. En général, on n’a pas besoin de toutes les informations, mais d’une seule à la fois. Au lieu de créer plein de balises pour chacun des champs EXIF — qui ne sont pas toujours fixes —, on va passer la section et le tag EXIF que l’on veut récupérer comme paramètres à la balise.

Depuis la version 1.8 [2], SPIP fournis une fonction simple pour récupérer des paramètres passés à une balise. Cette fonction est utilisée dans le code suivant, pour une méthode plus archaïque (à la #EXPOSER|on,off), inspirez vous du code des LOGO expliqué plus bas.

Nous allons utiliser la même méthode pour passer les deux paramètres à notre balise EXIF, on pourra alors faire : #EXIF{FILE,FileName} ou #EXIF{IFD0,DateTime} par exemple.

Voici le nouveau 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;
}

Il n’y pas beaucoup de différence dans le code retourné. Par contre, on va récupérer, si ils sont donnés, les paramètres $section et $tag pour les passer à tag_exif.

On parcourt la liste de filtres donnée dans params->fonctions pour trouver celui qui correspond aux paramètres. On enlève ensuite ce filtre de la liste et on extrait les paramètres du nom du filtre.

Dans l’entête EXIF d’une image jpeg, il y a — souvent — une vignette de l’image contenue dans le fichier. Cette vignette est utilisée par les appareils photo pour rapidement afficher la photo sur son écran. Evidemment, cette vignette n’est pas de superbe qualité, mais c’est une bonne occasion de voir comment faire une balise LOGO. En plus, si on n’a ni GD, ni imagemagick sur son serveur, ça peut être utile :)

Commençons par deux petites fonctions :
-  generer_url_logo_EXIF cherche le logo exif — contenu dans le fichier nom_fichier.exif.jpg si celle-ci a déjà été extraire, sinon, il extrait la vignette pour la stocker dans un fichier sur le serveur. Dans les deux cas, il retourne le chemin vers ce fichier.
-  generer_html_logo_EXIF génére le code html du logo, avec un lien ou pas, et un alignement ou pas.

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);
	fclose($handle);
	return $thumbname;
  }
  
  return '';

}

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

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

  return $code;
}

Regardons maintenant comment faire pour gérer un LOGO. Le plus gros problème est en fait la gestion des filtres spéciaux d’alignement etc...

Le principe est le même que pour le code précédent. On regarde la liste des fonctions pour voir ce qu’on y trouve. On peut soit y trouver une indication d’alignement, soit fichier (pour afficher l’url du fichier), soit un lien à mettre sur le logo.

La première partie du code étudie les filtres contenus dans le tableau $p->fonctions.
Ensuite, on regarde si on a besoin de générer du code pour le lien, c’est là qu’il y a un intérêt : on peut passer comme lien, une autre balise (par exemple : [(#LOGO_EXIF|#URL_DOCUMENT)]).

On appelle donc la fonction calculer_champ sur la potentielle balise, c’est la fonction de base qui calcule toutes les balises pour SPIP.

function balise_LOGO_EXIF($p) {

  // analyser les filtres
  $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 || signifie "on passe aux filtres"
	  else if ($nom == '')
		$flag_stop = true;
	  else if (!$flag_stop) {
		$lien = $nom;
		$flag_stop = true;
	  }
	  // apres un URL ou || ou |fichier ce sont
	  // des filtres (sauf left...lien...fichier)
	  else
		$filtres[] = $nom;
	}
	// recuperer les autres filtres s'il y en a
	$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))):''";

  //
	// Preparer le code du lien
	//
	// 1. filtre |lien
  if ($flag_lien_auto AND !$lien) {
	 $code_lien = "$url_doc";
  } else if ($lien) {
		// 2. lien indique en clair (avec des balises : imprimer#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;
  }

  if(!$code_lien)
	$code_lien = "''";

  
  if(!$align)
	$align = "''";

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

Un filtre pour les dates

Les dates stockées dans les images sont sous un format différent de celui utilisé par SPIP, ainsi on ne peut pas directement utiliser les filtres fournis par SPIP.
Voici donc un filtre à appliquer avant les filtres de date SPIP pour que tout marche bien :

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

Exemple d’utilisation

Voici une boucle qui affiche les documents d’un article en liste avec un certain nombre d’informations contenue dans les champs EXIF :

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

[<li>ISO:(#EXIF{EXIF,ISOSpeedRatings})</li>]
[<li>Temps d'exposition: (#EXIF{EXIF,ExposureTime})</li>]
</ul>
</li>
</BOUCLE_jpg>
</ul>
</B_jpg>

Version de développement

Cette contrib est gérée sur spip-zone, on peut récupérer la dernière version de développement grâce à :

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

Notes

[1Il y a un cache, tout bête, en mémoire pour ne pas relire les données EXIF plus d’une fois si on accède consécutivement au même document.

[2version cvs du 17 Dec. 2004

Discussion

5 discussions

  • 2

    Lut’

    juste une petite question, si je souhaite afficher l’ouverture (ou d’autres données EXIF) je fais comment ? j’ai essayé des trucs avec aperture mais ... bug sur bug.

    Sinon le reste fonctionne tres bien ici.
    Super cette contrib.

    • Oui, l’ouverture, toujours ce même problème :(

      qu’est ce qui bug chez toi ?

      Normalement, il y a un champ EXIF,AppertureValue qui donne cette valeur. Le problème, c’est que c’est un truc exotique qui ne correspond en général pas à ce qu’on attend.

      Moi, j’y connais rien, j’avais essayé de bidouille un filtre, mais j’ai pas assez de données pour tester correctement. En fin de compte, je me suis rabatu sur la valeur FNumber, qui donne aussi l’ouverture si j’ai bien compris. Avec un filtre en plus :

      function exif_ouverture($fnumber) {
         list($a,$b) = split('/',$fnumber);
         return intval($a)/intval($b);
      }

      mais là encore, je ne suis pas certain que ce soit ce qui faut. Si tu veux donner un retour là dessus.

    • Si votre balise #EXIF est générée par le plugin Exif pour Spip 1.9,
      alors il suffit d’ajouter ce code à l’intérieur d’une boucle DOCUMENTS du squelette :

      <BOUCLE_exif(DOCUMENTS){id_document}{mode=document}{extension=jpg}{doublons}>
      [<li>Ouverture: f(#EXIF{EXIF,FNumber}|div{10})</li>]

      De cette manière pour chaque image, tu auras : « Ouverture : f2 » par exemple.

      Article complet avec plugin et squelette dispo ici

    Répondre à ce message

  • Il existe un plugin EXIF pour Spip qui permet de gérer directement et simplement la balise #EXIF.

    Ce plugin EXIF ne fonctionne pas (complètement) sous Spip 1.9.2,
    mais j’en ai fait une adaptation pour la version 1.9.2 que vous pouvez consulter ci-dessous,
    avec des exemples d’utilisation simple.

    Cela résout le problème de gestion des données EXIF pour les images et photos sous Spip 1.9.2.

    Voir ici pour le Plugin EXIF modifié pour Spip 1.9.2

    Répondre à ce message

  • 7

    J’ai quelques plantés dans ma galerie photoJe ne suis pas certain d’avoir totalement tout pigé dans cette contrib... Les scripts doivent bien être ajoutés à mes_fonctions.php3 ? A savoir que je n’userai pas du script pour le logo exif (j’use des vignettes) et que je n’ai pas forcément de contenu exif dans les images du fait de la compression des photos. Une idée pour résoudre les bugs ?

      1. Tu es en quelle version ?
      2. à part le bug sur les dates, j’en ai ratté un sur ta page ?
      3. tu peux me donner le lien vers le squelette que je regarde ce qui colle pas ?
    • spip 1.8 Faut que j’update. Autant pour moi.

    • j’ai updaté en 1.8.2 et ça ne passe toujours pas.
      J’ai mis ça dans le template dans une boucle document jpg :
      [Model : (#EXIFIFD0,Model)] / [Date : (#EXIFEXIF,DateTimeOriginal|date_EXIF2spip|sinon#DATE|jour)] / [ISO :(#EXIFEXIF,ISOSpeedRatings)] / [Exposition : (#EXIFEXIF,ExposureTime)]

      Spip m’indique l’inexistance des filtres.

    • bon, pour la 1.8.2, ce code n’est plus valide (je pensais que ct dans les forums).
      Il faut telecharcher le fichier
      et le copier à la racine du site puis mettre :

      include('plug_exif.php')

      dans le fichier mes_fonctions.php3.

    • J’en ai toujours un :

      -  Erreur : filtre « date_EXIF2spip » non défini, _imagezoom

    • Ha oui, ce filtre n’est pas fournis par le fichier suscité. Il faut donc le copier colé depuis l’article (lui est toujours valide) dans mes_fonctions.

      désolé

    • Pourquoi s’embêter avec des fonctions qui sont dépassées par les dernières versions de Spip ?

      Il existe un plugin EXIF pour Spip qui permet de gérer directement et simplement la balise #EXIF.

      Ce plugin EXIF ne fonctionne pas (complètement) sous Spip 1.9.2,
      mais j’en ai fait une adaptation pour la version 1.9.2 que je posterai d’ici peu avec des exemples d’utilisation simple.

      Cela résout le problème de gestion des données EXIF des images et photos.

      Voir ici pour le plugin EXIF mis en application
      (cliquer sur le lien « Données EXIF » en dessous de la photo)

    Répondre à ce message

  • 1

    Attention, contrairement à ce qui est écrit dans le dernier code, pour obtenir l’heure il faut affdate{H\hi} et non pas affdate{H\hi}

    • je ne vois pas bien la différence entre affdateH\hi et affdateH\hi

    Répondre à ce message

  • 1

    Le plugin ne semble pas marcher avec spip 1.9.2.
    Une fois activé, on ne peut plus ajouter d’image à un article
    Quelqu’un a une idée ?

    • Oui,

      il y a un fichier incompatible avec la nouvelle version de SPIP. En attendant une mise à jour, tu peux renomer le fichier : inc/getdocument.php en inc/getdocument.php.old ce qui corrigera le pbl de l’ajout des images.

      Les balises #EXIF seront toujours disponnibles par contre et l’utilité du plugin reste donc entière.

    Répondre à ce message

Ajouter un commentaire

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
  • 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 apparaît.

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.

Qui êtes-vous ?
[Se connecter]

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Ajouter un document

Suivre les commentaires : RSS 2.0 | Atom