Mercurial > hg > mgallery
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 { |