Afficher les infos EXIF et/ou IPTC d'une photo

J’ai ouvert un photoblog il y a quelque temps et vu ma feignasse attitude qui me prend en ce moment, j’ai pensé que ce serait plus simple pour moi que les informations stockées avec la photo pendant la prise de vue par l’APN ou ajoutées ensuite dans mon logiciel de gestion de mon stock photo favori soient extraites et reprises sur l’affichage des billets. Pour résumer, voilà un exemple de ce que ça donne :

infos-exif.png

Dans l’exemple montré ici, j’ai choisi d’afficher le titre de la photo, sa légende s’il en existe une, le lieu (ville et pays) où elle a été prise — informations que j’ai saisies dans Lightroom® —, le mois et l’année de la prise de vue et les informations techniques correspondantes à la vitesse, l’ouverture, la sensibilité ISO et la longueur de focale, valeurs qui sont enregistrées par l’appareil et qui peuvent intéresser les photographes amateurs.

Je vous propose de détailler le moyen d’obtenir cela.

Vous avez compris que le prérequis pour obtenir ces informations est que la photo utilisée contiennent ses informations. Sur le photoblog, j’affiche un format réduit de la photo (en 600 x 400 pixels) et je mets un lien vers la photo originale (en 800 x 600 pixels). C’est sur cette dernière que les informations EXIF sont stockées — Lightroom® permet de conserver ces informations lors d’une exportation en JPEG. De plus, Dotclear est capable de récupérer ses informations et les stocke dans une table de la base de données pour leur utilisation ultérieure dès qu’on parcourt le dossier où la photo est stockée. Du coup il ne reste plus qu’à coder le nécessaire pour les récupérer et les afficher.

Pour ne pas réinventer ce qui avait déjà été fait ailleurs — et pas par le premier venu — j’ai sollicité Olivier Meunier qui avait développé pour ses propres besoins de quoi me satisfaire. J’ai récupéré le code qu’il utilise chez lui et je l’ai adapté ici.

if (!defined('DC_RC_PATH')) { return; }

$core->addBehavior('publicAfterContentFilter',array('neokraftBehaviors','publicAfterContentFilter'));

class neokraftBehaviors
{
	protected static $post_imgs = array();
	protected static $post_imgs_reg = '#(?:<p>\s*)(?:<a[^>]+href="(.+?)"(.+?)>\s*</a>)\s*</p>#msu';
	
	public static function publicAfterContentFilter($core,$tag,$args)
	{
		global $_ctx;
		
		if ($tag != 'EntryContent' || ($core->url->type != 'post' && $core->url->type != 'preview')) {
			return;
		}
		
		self::$post_imgs = array();
		$str =& $args[0];
		
		if (preg_match_all(self::$post_imgs_reg,$str,$m) > 0)
		{
			foreach ($m[1] as $i => $v) {
				self::$post_imgs[$v] = array(
					'w' => null,
					'h' => null,
					'title' => null,
					'meta' => null
				);
			}
			
			$m = new neokraftMedia($core);
			$m->getMediaItems(self::$post_imgs);
			
			$str = preg_replace_callback(self::$post_imgs_reg,array('self','photoMeta'),$str);
		}
	}
	
	protected static function photoMeta($m)
	{
		if (!self::$post_imgs[$m[1]]['w']) {
			return $m[0];
		}
		
		$i = self::$post_imgs[$m[1]];
		
		# read meta
		$meta = $i['meta'];
		$meta_title = array('Title','City','Country');
		
		$title = array();
		foreach ($meta_title as $t) {
			if ((string) $meta->$t) {
				$title[] = html::escapeHTML((string) $meta->$t);
			}
		}
		$title[] = dt::dt2str(
			'<span class="exif-datetime">'.
			'<span class="exif-weekday"> %A</span>'.
			'<span class="exif-daynum"> %e</span>'.
			'<span class="exif-month"> %B</span>'.
			'<span class="exif-year"> %Y</span>'.
			'<span class="exif-time">, %H:%M</span>'.
			'</span>',
			(string) $meta->DateTimeOriginal);
		
		$description = '';
		if ((string) $meta->Description && (string) $meta->Title != (string) $meta->Description) {
			$description = '<p class="exif-desc">'.html::escapeHTML((string) $meta->Description).'</p>';
		}
		
		$info = array();
		if ((string) $meta->Exposure) {
			$info['speed'] = (string) $meta->Exposure.'s';
		}
		if ((string) $meta->FNumber) {
			$info['aperture'] = (string) $meta->FNumber;
			$aperture = sscanf($info['aperture'],'%d/%d');
			if ($aperture) {
				$info['aperture'] = 'f/'.$aperture[0]/$aperture[1];
			}
		}
		if ((string) $meta->ISOSpeedRatings) {
			$info['ISO'] = (string) $meta->ISOSpeedRatings;
		}
		if ((string) $meta->FocalLength) {
			$info['focal length'] = (string) $meta->FocalLength;
			$flength = sscanf($info['focal length'],'%d/%d');
			if ($flength) {
				$info['focal length'] = $flength[0]/$flength[1].'mm';
			}
		}
		if (!empty($info)) {
			foreach ($info as $k => &$v) {
				$v = $k.': <span class="exif-value">'.$v.'</span>';
			}
			$info = '<p class="exif-info">'.implode(', ',$info).'</p>';
		} else {
			$info = '';
		}
		
		$s = preg_replace('#(</p>)$#',
			'$1'.
			'<div class="exif">'.
			'<p class="exif-title">'.implode(', ',$title).'</p>'.
			$description.
			$info.
			'</div>',
			$m[0]
		);
		
		return $s;
	}
}

class neokraftMedia extends dcMedia
{
	public function getMediaItems(&$post_imgs)
	{
		$paths = array_keys($post_imgs);
		
		foreach ($paths as &$v) {
			$v = preg_replace('/^'.preg_quote($this->core->blog->settings->public_url,'/').'(\/?)/','',$v);
		}

		$rs = $this->con->select(
			'SELECT media_id, media_path, media_title, '.
			'media_file, media_meta, media_dt, media_creadt, '.
			'media_upddt, media_private, user_id '.
			'FROM '.$this->table.' '.
			"WHERE media_path = '".$this->path."' ".
			'AND media_file '.$this->con->in($paths)
		);
		
		while ($rs->fetch()) {
			$u = $this->core->blog->settings->public_url.'/'.$rs->media_file;
			if (!isset($post_imgs[$u])) {
				continue;
			}
			
			$o = $this->fileRecord($rs);
			$s = getimagesize($o->file);
			$post_imgs[$u]['w'] = $s[0];
			$post_imgs[$u]['h'] = $s[1];
			$post_imgs[$u]['title'] = $o->media_title;
			$post_imgs[$u]['meta'] = $o->media_meta;
		}
	}
}

Ce code est à placer dans le fichier _public.php du thème utilisé — fichier à créer s’il n’existe pas en rajoutant une première ligne contenant <?php et une ligne à la fin contenant ?>. L’idée retenue est de retrouver la forme du code utilisé pour afficher les photos, d’en extraire l’URL pour retrouver le fichier correspondant, celui dans lequel les infos EXIF sont stockées, puis ensuite de retrouver ces infos dans la base et de les afficher à la suite de ladite photo. Ceci pour chacune des photos rencontrées dans le billet.

Le code HTML qui sera rajouté juste après le paragraphe qui contient la photo aura cette structure :

<div class="exif">
	<p class="exif-title">titre, ville, pays, 
		<span class="exif-datetime">
			<span class="exif-weekday">nom du jour</span>
			<span class="exif-numday">numéro du jour</span>
			<span class="exif-month">nom du mois</span>
			<span class="exif-year">année</span>
			<span class="exif-time">heure</span>
		</span>
	</p>
	<p class="exif-desc">description</p>
	<p class="exif-info">
		speed: <span class="exif-value">vitesse</span>
		aperture: <span class="exif-value">ouverture</span>
		ISO: <span class="exif-value">sensibilité</span>
		focal length: <span class="exif-value">longueur de focale</span>
	</p>
</div>

Et voilà enfin le code CSS spécialement dédié à la mise en forme de ces informations :

/* Info EXIF
-------------------------------------------------------- */

.exif {	
	margin: 10px 0;
}

.exif-title { }

.exif-datetime { }

.exif-weekday { 
	display: none;
}

.exif-daynum { 	
	display: none;
}

.exif-month { }

.exif-year { }

.exif-time { 
	display: none;
}

.exif-desc { }

.exif-info {
	padding: 5px 20px 10px;
	color:  #999;
	font-size: 0.9em;
}

.exif-value {
	padding: 0 4px;
	color: #fff;
	background-color: #555;
	-moz-border-radius: 4px;
	-webkit-border-radius: 4px;
}

À vous de jouer et d’adapter si nécessaire ;-)

Ajouter un commentaire

Les champs suivis d'un * sont obligatoires

Les commentaires peuvent être formatés en utilisant la syntaxe Markdown Extra.

Ajouter un rétrolien

URL de rétrolien : https://open-time.net/trackback/3422

Haut de page