comparison mgtool.php @ 279:54a54921426c

Add functions for extracting XMP information from files and parsing the XMP to get out some relevant fields of data not present in EXIF tags.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 16 May 2019 21:45:59 +0300
parents 4080b9bde2ac
children cec96665993a
comparison
equal deleted inserted replaced
278:6770ef8b3575 279:54a54921426c
25 [ MG_STR, "lensmodel" , "UndefinedTag:0xA434" ], 25 [ MG_STR, "lensmodel" , "UndefinedTag:0xA434" ],
26 [ MG_DVA, "focallength" , "FocalLength" ], 26 [ MG_DVA, "focallength" , "FocalLength" ],
27 [ MG_DATE, "datetime" , "DateTimeOriginal" ], 27 [ MG_DATE, "datetime" , "DateTimeOriginal" ],
28 [ MG_DATE, "datetime" , "DateTimeDigitized" ], 28 [ MG_DATE, "datetime" , "DateTimeDigitized" ],
29 [ MG_INT, "filesize" , "FileSize" ], 29 [ MG_INT, "filesize" , "FileSize" ],
30 [ MG_STR, "keywords" , "keywords" ],
30 ]; 31 ];
31 32
32 33
33 // 34 //
34 // Operating modes and update flags 35 // Operating modes and update flags
204 $img->writeImage($outFilename); 205 $img->writeImage($outFilename);
205 $img->destroy(); 206 $img->destroy();
206 } 207 }
207 208
208 return TRUE; 209 return TRUE;
210 }
211
212
213 //
214 // Read and parse XMP data from given file
215 // .. a horrible hack.
216 //
217 function mgReadXMPData($filename, $xmpBlockSize = 1024*64)
218 {
219 if (($fh = @fopen($filename, 'rb')) === FALSE)
220 return FALSE;
221
222 $xmpStartTag = "<x:xmpmeta";
223 $xmpEndTag = "</x:xmpmeta>";
224
225 // Check for start tag
226 $buffer = "";
227 $xmpOK = FALSE;
228 while (!feof($fh))
229 {
230 if (($tmp = @fread($fh, $xmpBlockSize)) === FALSE)
231 return FALSE;
232
233 $buffer .= $tmp;
234 if (($spos1 = strpos($buffer, "<")) !== FALSE)
235 {
236 $buffer = substr($buffer, $spos1);
237 if (($spos2 = strpos($buffer, $xmpStartTag)) !== FALSE)
238 {
239 $buffer = substr($buffer, $spos2);
240 $xmpOK = TRUE;
241 break;
242 }
243 }
244 else
245 $buffer = "";
246 }
247
248 // Check for end tag if start tag was found
249 if ($xmpOK)
250 {
251 $xmpOK = FALSE;
252 $buffer2 = $buffer;
253 do
254 {
255 if (($spos1 = strpos($buffer2, "<")) !== FALSE)
256 {
257 $buffer2 = substr($buffer2, $spos1);
258 if (($spos2 = strpos($buffer2, $xmpEndTag)) !== FALSE)
259 {
260 $xmpOK = TRUE;
261 break;
262 }
263 }
264
265 if (($tmp = @fread($fh, $xmpBlockSize)) !== FALSE)
266 {
267 $buffer2 .= $tmp;
268 $buffer .= $tmp;
269 }
270 else
271 {
272 $xmpOK = FALSE;
273 break;
274 }
275 } while (!$xmpOK);
276
277 if ($xmpOK)
278 {
279 if (($spos = strpos($buffer, $xmpEndTag)) !== FALSE)
280 $buffer = substr($buffer, 0, $spos + strlen($xmpEndTag));
281 else
282 $xmpOK = FALSE;
283 }
284 }
285
286 fclose($fh);
287
288 return $xmpOK ? $buffer : FALSE;
289 }
290
291
292 function mgParseXMPData($xmpStr)
293 {
294 // SimpleXML apparently can't handle namespaces,
295 // so we will crudely remove them with some regexes
296 $xmpPatterns =
297 [
298 "/[a-zA-Z]+:/",
299 "/\/[a-zA-Z]+:/",
300 "/<\/?(Bag|Alt|Seq)>/"
301 ];
302
303 $xmpReplacements =
304 [
305 "",
306 "\/",
307 "",
308 ];
309
310 $xmpStr = preg_replace($xmpPatterns, $xmpReplacements, $xmpStr);
311
312 // Parse XML to a SimpleXMLElement structure
313 if (($xmpOb = @simplexml_load_string($xmpStr)) === FALSE)
314 return FALSE;
315
316 // Process structure to simple flat array of data
317 // for the desired elements only
318 $xmpData = [];
319
320 //if (($tmp = $xmpOb->xpath("RDF/Description/description/li")) !== FALSE)
321 // $xmpData["description"] = (string) $xe[0];
322
323 if (($xres = $xmpOb->xpath("RDF/Description/subject/li")) !== FALSE)
324 {
325 $xmpData["keywords"] = array_map(function($xkw) { return (string) $xkw; }, $xres);
326 }
327
328 return $xmpData;
209 } 329 }
210 330
211 331
212 // 332 //
213 // Converts one value (mainly from EXIF tag information) 333 // Converts one value (mainly from EXIF tag information)
720 $updFlags |= GUPD_EXIF_INFO; 840 $updFlags |= GUPD_EXIF_INFO;
721 841
722 if (file_exists($capFilename) && 842 if (file_exists($capFilename) &&
723 mgNeedUpdate($galEntry, "mtime", filemtime($capFilename))) 843 mgNeedUpdate($galEntry, "mtime", filemtime($capFilename)))
724 $updFlags |= GUPD_CAPTION; 844 $updFlags |= GUPD_CAPTION;
845
846 // Check for XMP info
847 // TODO XXX: Support XMP sidecar files
848 if (($updFlags & GUPD_EXIF_INFO) &&
849 ($xmpStr = mgReadXMPData($efilename)) !== FALSE &&
850 ($xmp = mgParseXMPData($xmpStr)) !== FALSE)
851 {
852 foreach ($galExifConversions as $conv)
853 mgCopyEntryData($edata, $xmp, $conv[0], $conv[1], $conv[2]);
854 }
725 855
726 // Check for EXIF info 856 // Check for EXIF info
727 if (($updFlags & GUPD_EXIF_INFO) && 857 if (($updFlags & GUPD_EXIF_INFO) &&
728 ($exif = @exif_read_data($efilename)) !== FALSE) 858 ($exif = @exif_read_data($efilename)) !== FALSE)
729 { 859 {