Fast Gallery with Sidecar Files for EXIF Data... and more!
| Project: | Fast Gallery |
| Version: | 6.x-4.0-beta5 |
| Component: | Code |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | active |
The PHP function for reading EXIF information from an image is not very robust. I get errors reading EXIF data in Fast Gallery which remain when I directly use php:exif_read_data. EXIFTOOL is much more robust and offers a lot of options for formatting and output. The problem is you can't guarantee that your internet host can run exiftool. I edit my EXIF and IPTC tags in NIkon View NX, or Graphic Converter (which is great both for tag editing and bulk processing).
Solution: use EXIFTOOL on my local computer to create a JSON dump of my tags:
exiftool -json -exif:ALL -GPSLatitude -GPSLongitude -c "%.6f" -XMP-photoshop:ALL -iptc:ALL -nikon photo.jpg > photo.jpg.json
I script this into a loop so I do all the files at a throw. Then I upload the JSON sidecar files along with the rest of the gallery photos. PHP easily reads JSON into an array or object, including sub arrays like keywords list. I don't use the family:tag:data format. The different families (EXIF, IPTC, XMP, etc) reuse some tag names; doing the dump in one EXIFTOOL statement (as above) automatically creates uniqueness on the tags. One other glitch: Some IPTC tags have hyphens which can mess you up in PHP.
Here is the code to read a JSON Sidecar and over-write Fast Gallery tags if a sidecar file exists. It is a very simple modification to the updateExifDb function in the fast_gallery.class.php file. Doing a diff will show you the changes. (I can't guarantee there are no bugs).
/**
* Write the image's EXIF information to the database.
*
* @param file
* Path of the image file.
* @param gid
* ID of the picture assigned by the database.
*/
public function updateExifDb($file, $gid) {
// Get the file extension
$tmp = explode('.', $file);
$fileType = strtolower(end($tmp));
if ($fileType != 'jpg' && $fileType != 'jpeg') {
// Construct the custom thumbnail path
$tmp_ar = explode('.', $file);
array_pop($tmp_ar);
$tmp = implode('.', $tmp_ar);
$file = $tmp . '.screenshot.jpg';
}
// Grab the EXIF data that we're interested in
// NEW SIDECAR CODE:
// Sidecar JSON created from exiftool:
// exiftool -json -exif:ALL -GPSLatitude -GPSLongitude -c "%.6f" -XMP-photoshop:ALL -iptc:ALL -nikon photo.jpg > photo.jpg.json
// First look for sidecar; if no sidecar, continue with original EXIF data:
$file_json = $file . $file_json . ".json";
if (file_exists($file_json)) {
$fh = fopen($file_json, 'r');
$json = fread($fh, filesize($file_json));
fclose($fh);
//remove curly brackets to beware from regex errors
// strips outer json package brackets to return only the inside array.
$json = substr($json, strpos($json,'{')+1, strlen($json));
$json = substr($json, 0, strrpos($json,'}'));
$json = preg_replace('/(^|,)([\\s\\t]*)([^:]*) (([\\s\\t]*)):(([\\s\\t]*))/s', '$1"$3"$4:', trim($json));
// array for php to work with
$ar_json = json_decode('{'.$json.'}', true);
// var_dump($ar_json);
// If you prefer to work with the object:
$obj_json = (object) $ar_json;
// THE FOLLOWING code keys deals with hyphens so PHP can work with it.
// (hyphens (-) in IPCT codes crashes the php; similar problem with colons (:) ):
$byline = "By-line";
$bylinetitle = "By-lineTitle";
$captionabstract = "Caption-Abstract";
$provincestate = "Province-State";
$countryprimarylocationname = "Country-PrimaryLocationName";
$sublocation = "Sub-location";
// Ternary makes Latitude/Longitude negative if Southern/Western
$pieces = explode (" ", $obj_json->GPSLatitude );
$json_lat = ($pieces[1] == "S") ? -$pieces[0] : $pieces[0];
$pieces = explode (" ", $obj_json->GPSLongitude );
$json_long = ($pieces[1] == "W") ? -$pieces[0] : $pieces[0];
// Keywords list goes into $keywords; People list goes into $contact
$json_keywords = implode (", ", $obj_json->Keywords );
$json_contacts = implode (", ", $obj_json->Contact );
// Over-write normal fast_callery from sidecar:
$param['model'] = $obj_json->Model;
$param['exposureTime'] = $obj_json->ExposureTime;
$param['dateTaken'] = $obj_json->DateTimeOriginal;
$param['ISOSpeedRatings'] = $obj_json->ISO;
$param['description'] = $obj_json->$captionabstract;
$param['comment'] = $obj_json->Headline;
$param['fileCreated'] = $obj_json->DateCreated;
$param['copyright'] = $obj_json->CopyrightNotice;
$param['artist'] = $obj_json->$byline;
// Windows XP files data
$param['xptitle'] = $obj_json->ObjectName;
$param['xpcomments'] = $obj_json->$sublocation;
$param['xpkeywords'] = $json_keywords;
// END OF NEW SIDECAR CODE
} else {
// ORIGINAL EXIF_READ CODE:
if (file_exists(utf8_decode($file)))
$exif = exif_read_data(utf8_decode($file), 0, true);
$param['model'] = $exif['IFD0']['Model'];
$param['exposureTime'] = $exif['EXIF']['ExposureTime'];
$param['dateTaken'] = $exif['EXIF']['DateTimeOriginal'];
$param['ISOSpeedRatings'] = $exif['EXIF']['ISOSpeedRatings'];
$param['description'] = utf8_encode($exif['IFD0']['ImageDescription']);
$param['comment'] = utf8_encode($exif['COMPUTED']['UserComment']);
if (file_exists($file)) { //else will return error on videos
$param['fileCreated'] = filemtime(utf8_decode($file));
}
$param['copyright'] = utf8_encode($exif['COMPUTED']['Copyright']);
$param['artist'] = utf8_encode($exif['IFD0']['Artist']);
// Windows XP files data
$param['xptitle'] = utf8_encode($exif['WINXP']['Title']);
$param['xpcomments'] = utf8_encode($exif['WINXP']['Comments']);
$param['xpkeywords'] = utf8_encode($exif['WINXP']['Keywords']);
// End of original, non sidecar code
}
// Check for previous entries
$sql = "SELECT * FROM {fast_gallery_exif} WHERE gid='%s'";
$result = db_query($sql, $gid);
$row = db_fetch_object($result);
// Update entries if they exist
if ($row != NULL) {
$existing_record[$row->field] = $row->value;
while ($row = db_fetch_object($result))
$existing_record[$row->field] = $row->value;
if ($existing_record != NULL) {
foreach ($existing_record as $key => $value) {
// Make changes to database only if there was a change
if ($param[$key] != $value) {
$sql = "UPDATE {fast_gallery_exif} SET value='%s' WHERE gid='%s' AND field='%s'";
db_query($sql, $param[$key], $gid, $key);
}
}
}
}
// Or else write a new record to the database
else {
foreach ($param as $key => $value) {
$dbObject['field'] = $key;
$dbObject['value'] = $value;
$dbObject['gid'] = $gid;
drupal_write_record('fast_gallery_exif', $dbObject);
}
}
}