nggMeta

Image METADATA PHP class for the WordPress plugin NextGEN Gallery nggmeta.lib.php.

Defined (1)

The class is defined in the following location(s).

/lib/meta.php  
  1. class nggMeta{ 
  2.  
  3. /**** Image Data ****/ 
  4. var $image = ''; // The image object 
  5. var $size = false; // The image size 
  6. var $exif_data = false; // EXIF data array 
  7. var $iptc_data = false; // IPTC data array 
  8. var $xmp_data = false; // XMP data array 
  9. /**** Filtered Data ****/ 
  10. var $exif_array = false; // EXIF data array 
  11. var $iptc_array = false; // IPTC data array 
  12. var $xmp_array = false; // XMP data array 
  13.  
  14. var $sanitize = false; // sanitize meta data on request 
  15.  
  16. /** 
  17. * Parses the nggMeta data only if needed 
  18. * @param int $image path to a image 
  19. * @param bool $onlyEXIF parse only exif if needed 
  20. * @return 
  21. */ 
  22. function nggMeta($pic_id, $onlyEXIF = false) { 
  23.  
  24. //get the path and other data about the image 
  25. $this->image = nggdb::find_image( $pic_id ); 
  26.  
  27. $this->image = apply_filters( 'ngg_find_image_meta', $this->image ); 
  28.  
  29. if ( !file_exists( $this->image->imagePath ) ) 
  30. return false; 
  31.  
  32. $this->size = @getimagesize ( $this->image->imagePath , $metadata ); 
  33.  
  34. if ($this->size && is_array($metadata)) { 
  35.  
  36. // get exif - data 
  37. if ( is_callable('exif_read_data')) 
  38. $this->exif_data = @exif_read_data($this->image->imagePath , 0, true ); 
  39.  
  40. // stop here if we didn't need other meta data 
  41. if ($onlyEXIF) 
  42. return true; 
  43.  
  44. // get the iptc data - should be in APP13 
  45. if ( is_callable('iptcparse') && isset($metadata['APP13']) ) 
  46. $this->iptc_data = @iptcparse($metadata['APP13']); 
  47.  
  48. // get the xmp data in a XML format 
  49. if ( is_callable('xml_parser_create')) 
  50. $this->xmp_data = $this->extract_XMP($this->image->imagePath ); 
  51.  
  52. return true; 
  53.  
  54. return false; 
  55.  
  56. /** 
  57. * return the saved meta data from the database 
  58. * @since 1.4.0 
  59. * @param string $object (optional) 
  60. * @return array|mixed return either the complete array or the single object 
  61. */ 
  62. function get_saved_meta($object = false) { 
  63.  
  64. $meta = $this->image->meta_data; 
  65.  
  66. //check if we already import the meta data to the database 
  67. if (!is_array($meta) || ($meta['saved'] != true)) 
  68. return false; 
  69.  
  70. // return one element if requested 
  71. if ($object) 
  72. return $meta[$object]; 
  73.  
  74. //removed saved parameter we don't need that to show 
  75. unset($meta['saved']); 
  76.  
  77. // and remove empty tags or arrays 
  78. foreach ($meta as $key => $value) { 
  79. if ( empty($value) OR is_array($value)) 
  80. unset($meta[$key]); 
  81.  
  82. // on request sanitize the output 
  83. if ( $this->sanitize == true ) 
  84. array_walk( $meta , create_function('&$value', '$value = esc_html($value);')); 
  85.  
  86. return $meta; 
  87.  
  88. /** 
  89. * nggMeta::get_EXIF() 
  90. * See also http://trac.wordpress.org/changeset/6313 
  91. * @return structured EXIF data 
  92. */ 
  93. function get_EXIF($object = false) { 
  94.  
  95. if ( !$this->exif_data ) 
  96. return false; 
  97.  
  98. if (!is_array($this->exif_array)) { 
  99.  
  100. $meta= array(); 
  101.  
  102. if ( isset($this->exif_data['EXIF']) ) { 
  103. $exif = $this->exif_data['EXIF']; 
  104.  
  105. if (!empty($exif['FNumber'])) 
  106. $meta['aperture'] = 'F ' . round( $this->exif_frac2dec( $exif['FNumber'] ), 2 ); 
  107. if (!empty($exif['Model'])) 
  108. $meta['camera'] = trim( $exif['Model'] ); 
  109. if (!empty($exif['DateTimeDigitized'])) 
  110. $meta['created_timestamp'] = date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $this->exif_date2ts($exif['DateTimeDigitized'])); 
  111. else if (!empty($exif['DateTimeOriginal'])) 
  112. $meta['created_timestamp'] = date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $this->exif_date2ts($exif['DateTimeOriginal'])); 
  113. if (!empty($exif['FocalLength'])) 
  114. $meta['focal_length'] = $this->exif_frac2dec( $exif['FocalLength'] ) . __(' mm', 'nggallery'); 
  115. if (!empty($exif['ISOSpeedRatings'])) 
  116. $meta['iso'] = $exif['ISOSpeedRatings']; 
  117. if (!empty($exif['ExposureTime'])) { 
  118. $meta['shutter_speed'] = $this->exif_frac2dec ($exif['ExposureTime']); 
  119. $meta['shutter_speed'] =($meta['shutter_speed'] > 0.0 and $meta['shutter_speed'] < 1.0) ? ( '1/' . round( 1 / $meta['shutter_speed'], -1) ) : ($meta['shutter_speed']); 
  120. $meta['shutter_speed'] .= __(' sec', 'nggallery'); 
  121. //Bit 0 indicates the flash firing status 
  122. if (!empty($exif['Flash'])) 
  123. $meta['flash'] = ( $exif['Flash'] & 1 ) ? __('Fired', 'nggallery') : __('Not fired', ' nggallery'); 
  124.  
  125. // additional information 
  126. if ( isset($this->exif_data['IFD0']) ) { 
  127. $exif = $this->exif_data['IFD0']; 
  128.  
  129. if (!empty($exif['Model'])) 
  130. $meta['camera'] = $exif['Model']; 
  131. if (!empty($exif['Make'])) 
  132. $meta['make'] = $exif['Make']; 
  133. if (!empty($exif['ImageDescription'])) 
  134. $meta['title'] = utf8_encode($exif['ImageDescription']); 
  135. if (!empty($exif['Orientation'])) 
  136. $meta['Orientation'] = $exif['Orientation']; 
  137.  
  138. // this is done by Windows 
  139. if ( isset($this->exif_data['WINXP']) ) { 
  140. $exif = $this->exif_data['WINXP']; 
  141.  
  142. if (!empty($exif['Title']) && empty($meta['title'])) 
  143. $meta['title'] = utf8_encode($exif['Title']); 
  144. if (!empty($exif['Author'])) 
  145. $meta['author'] = utf8_encode($exif['Author']); 
  146. if (!empty($exif['Keywords'])) 
  147. $meta['tags'] = utf8_encode($exif['Keywords']); 
  148. if (!empty($exif['Subject'])) 
  149. $meta['subject'] = utf8_encode($exif['Subject']); 
  150. if (!empty($exif['Comments'])) 
  151. $meta['caption'] = utf8_encode($exif['Comments']); 
  152.  
  153. $this->exif_array = $meta; 
  154.  
  155. // return one element if requested 
  156. if ( $object == true ) { 
  157. $value = isset($this->exif_array[$object]) ? $this->exif_array[$object] : false; 
  158. return $value; 
  159.  
  160. // on request sanitize the output 
  161. if ( $this->sanitize == true ) 
  162. array_walk( $this->exif_array , create_function('&$value', '$value = esc_html($value);')); 
  163.  
  164. return $this->exif_array; 
  165.  
  166.  
  167. // convert a fraction string to a decimal 
  168. function exif_frac2dec($str) { 
  169. @list( $n, $d ) = explode( '/', $str ); 
  170. if ( !empty($d) ) 
  171. return $n / $d; 
  172. return $str; 
  173.  
  174. // convert the exif date format to a unix timestamp 
  175. function exif_date2ts($str) { 
  176. // seriously, who formats a date like 'YYYY:MM:DD hh:mm:ss'? 
  177. @list( $date, $time ) = explode( ' ', trim($str) ); 
  178. @list( $y, $m, $d ) = explode( ':', $date ); 
  179.  
  180. return strtotime( "{$y}-{$m}-{$d} {$time}" ); 
  181.  
  182. /** 
  183. * nggMeta::readIPTC() - IPTC Data Information for EXIF Display 
  184. * @param mixed $output_tag 
  185. * @return IPTC-tags 
  186. */ 
  187. function get_IPTC($object = false) { 
  188.  
  189. if (!$this->iptc_data) 
  190. return false; 
  191.  
  192. if (!is_array($this->iptc_array)) { 
  193.  
  194. // --------- Set up Array Functions --------- // 
  195. $iptcTags = array ( 
  196. "2#005" => 'title',  
  197. "2#007" => 'status',  
  198. "2#012" => 'subject',  
  199. "2#015" => 'category',  
  200. "2#025" => 'keywords',  
  201. "2#055" => 'created_date',  
  202. "2#060" => 'created_time',  
  203. "2#080" => 'author',  
  204. "2#085" => 'position',  
  205. "2#090" => 'city',  
  206. "2#092" => 'location',  
  207. "2#095" => 'state',  
  208. "2#100" => 'country_code',  
  209. "2#101" => 'country',  
  210. "2#105" => 'headline',  
  211. "2#110" => 'credit',  
  212. "2#115" => 'source',  
  213. "2#116" => 'copyright',  
  214. "2#118" => 'contact',  
  215. "2#120" => 'caption' 
  216. ); 
  217.  
  218. $meta = array(); 
  219. foreach ($iptcTags as $key => $value) { 
  220. if (isset ( $this->iptc_data[$key] ) ) 
  221. $meta[$value] = trim(utf8_encode(implode(", ", $this->iptc_data[$key]))); 
  222.  
  223. $this->iptc_array = $meta; 
  224.  
  225. // return one element if requested 
  226. if ($object) 
  227. return (isset($this->iptc_array[$object])) ? $this->iptc_array[$object] : NULL; 
  228.  
  229. // on request sanitize the output 
  230. if ( $this->sanitize == true ) 
  231. array_walk( $this->iptc_array , create_function('&$value', '$value = esc_html($value);')); 
  232.  
  233. return $this->iptc_array; 
  234.  
  235. /** 
  236. * nggMeta::extract_XMP() 
  237. * get XMP DATA 
  238. * code by Pekka Saarinen http://photography-on-the.net 
  239. * @param mixed $filename 
  240. * @return XML data 
  241. */ 
  242. function extract_XMP( $filename ) { 
  243.  
  244. //TODO:Require a lot of memory, could be better 
  245. ob_start(); 
  246. @readfile($filename); 
  247. $source = ob_get_contents(); 
  248. ob_end_clean(); 
  249.  
  250. $start = strpos( $source, "<x:xmpmeta" ); 
  251. $end = strpos( $source, "</x:xmpmeta>" ); 
  252. if ((!$start === false) && (!$end === false)) { 
  253. $lenght = $end - $start; 
  254. $xmp_data = substr($source, $start, $lenght+12 ); 
  255. unset($source); 
  256. return $xmp_data; 
  257.  
  258. unset($source); 
  259. return false; 
  260.  
  261. /** 
  262. * nggMeta::get_XMP() 
  263. * @package Taken from http://php.net/manual/en/function.xml-parse-into-struct.php 
  264. * @author Alf Marius Foss Olsen & Alex Rabe 
  265. * @return XML Array or object 
  266. */ 
  267. function get_XMP($object = false) { 
  268.  
  269. if(!$this->xmp_data) 
  270. return false; 
  271.  
  272. if (!is_array($this->xmp_array)) { 
  273.  
  274. $parser = xml_parser_create(); 
  275. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); // Dont mess with my cAsE sEtTings 
  276. xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); // Dont bother with empty info 
  277. xml_parse_into_struct($parser, $this->xmp_data, $values); 
  278. xml_parser_free($parser); 
  279.  
  280. $xmlarray = array(); // The XML array 
  281. $this->xmp_array = array(); // The returned array 
  282. $stack = array(); // tmp array used for stacking 
  283. $list_array = array(); // tmp array for list elements 
  284. $list_element = false; // rdf:li indicator 
  285.  
  286. foreach($values as $val) { 
  287.  
  288. if($val['type'] == "open") { 
  289. array_push($stack, $val['tag']); 
  290.  
  291. } elseif($val['type'] == "close") { 
  292. // reset the compared stack 
  293. if ($list_element == false) 
  294. array_pop($stack); 
  295. // reset the rdf:li indicator & array 
  296. $list_element = false; 
  297. $list_array = array(); 
  298.  
  299. } elseif($val['type'] == "complete") { 
  300. if ($val['tag'] == "rdf:li") { 
  301. // first go one element back 
  302. if ($list_element == false) 
  303. array_pop($stack); 
  304. $list_element = true; 
  305. // do not parse empty tags 
  306. if ( empty($val['value']) ) continue; 
  307. // save it in our temp array 
  308. $list_array[] = $val['value']; 
  309. // in the case it's a list element we seralize it 
  310. $value = implode(", ", $list_array); 
  311. $this->setArrayValue($xmlarray, $stack, $value); 
  312. } else { 
  313. array_push($stack, $val['tag']); 
  314. // do not parse empty tags 
  315. if ( !empty($val['value']) ) 
  316. $this->setArrayValue($xmlarray, $stack, $val['value']); 
  317. array_pop($stack); 
  318.  
  319. } // foreach 
  320.  
  321. // don't parse a empty array 
  322. if( empty($xmlarray) || empty($xmlarray['x:xmpmeta']) ) 
  323. return false; 
  324.  
  325. // cut off the useless tags 
  326. $xmlarray = $xmlarray['x:xmpmeta']['rdf:RDF']['rdf:Description']; 
  327.  
  328. // --------- Some values from the XMP format--------- // 
  329. $xmpTags = array ( 
  330. 'xap:CreateDate' => 'created_timestamp',  
  331. 'xap:ModifyDate' => 'last_modfied',  
  332. 'xap:CreatorTool' => 'tool',  
  333. 'dc:format' => 'format',  
  334. 'dc:title' => 'title',  
  335. 'dc:creator' => 'author',  
  336. 'dc:subject' => 'keywords',  
  337. 'dc:description' => 'caption',  
  338. 'photoshop:AuthorsPosition' => 'position',  
  339. 'photoshop:City' => 'city',  
  340. 'photoshop:Country' => 'country' 
  341. ); 
  342.  
  343. foreach ($xmpTags as $key => $value) { 
  344. // if the kex exist 
  345. if ( isset($xmlarray[$key]) ) { 
  346. switch ($key) { 
  347. case 'xap:CreateDate': 
  348. case 'xap:ModifyDate': 
  349. $this->xmp_array[$value] = date_i18n(get_option('date_format').' '.get_option('time_format'), strtotime($xmlarray[$key])); 
  350. break; 
  351. default : 
  352. $this->xmp_array[$value] = $xmlarray[$key]; 
  353.  
  354.  
  355. // return one element if requested 
  356. if ($object != false ) 
  357. return isset($this->xmp_array[$object]) ? $this->xmp_array[$object] : false; 
  358.  
  359. // on request sanitize the output 
  360. if ( $this->sanitize == true ) 
  361. array_walk( $this->xmp_array , create_function('&$value', '$value = esc_html($value);')); 
  362.  
  363. return $this->xmp_array; 
  364.  
  365. function setArrayValue(&$array, $stack, $value) { 
  366. if ($stack) { 
  367. $key = array_shift($stack); 
  368. $this->setArrayValue($array[$key], $stack, $value); 
  369. return $array; 
  370. } else { 
  371. $array = $value; 
  372.  
  373. /** 
  374. * nggMeta::get_META() - return a meta value form the available list 
  375. * @param string $object 
  376. * @return mixed $value 
  377. */ 
  378. function get_META($object = false) { 
  379.  
  380. // defined order first look into database, then XMP, IPTC and EXIF. 
  381. if ($value = $this->get_saved_meta($object)) 
  382. return $value; 
  383. if ($value = $this->get_XMP($object)) 
  384. return $value; 
  385. if ($value = $this->get_IPTC($object)) 
  386. return $value; 
  387. if ($value = $this->get_EXIF($object)) 
  388. return $value; 
  389.  
  390. // nothing found ? 
  391. return false; 
  392.  
  393. /** 
  394. * nggMeta::i8n_name() - localize the tag name 
  395. * @param mixed $key 
  396. * @return translated $key 
  397. */ 
  398. function i8n_name($key) { 
  399.  
  400. $tagnames = array( 
  401. 'aperture' => __('Aperture', 'nggallery'),  
  402. 'credit' => __('Credit', 'nggallery'),  
  403. 'camera' => __('Camera', 'nggallery'),  
  404. 'caption' => __('Caption', 'nggallery'),  
  405. 'created_timestamp' => __('Date/Time', 'nggallery'),  
  406. 'copyright' => __('Copyright', 'nggallery'),  
  407. 'focal_length' => __('Focal length', 'nggallery'),  
  408. 'iso' => __('ISO', 'nggallery'),  
  409. 'shutter_speed' => __('Shutter speed', 'nggallery'),  
  410. 'title' => __('Title', 'nggallery'),  
  411. 'author' => __('Author', 'nggallery'),  
  412. 'tags' => __('Tags', 'nggallery'),  
  413. 'subject' => __('Subject', 'nggallery'),  
  414. 'make' => __('Make', 'nggallery'),  
  415. 'status' => __('Edit Status', 'nggallery'),  
  416. 'category' => __('Category', 'nggallery'),  
  417. 'keywords' => __('Keywords', 'nggallery'),  
  418. 'created_date' => __('Date Created', 'nggallery'),  
  419. 'created_time' => __('Time Created', 'nggallery'),  
  420. 'position' => __('Author Position', 'nggallery'),  
  421. 'city' => __('City', 'nggallery'),  
  422. 'location' => __('Location', 'nggallery'),  
  423. 'state' => __('Province/State', 'nggallery'),  
  424. 'country_code' => __('Country code', 'nggallery'),  
  425. 'country' => __('Country', 'nggallery'),  
  426. 'headline' => __('Headline', 'nggallery'),  
  427. 'credit' => __('Credit', 'nggallery'),  
  428. 'source' => __('Source', 'nggallery'),  
  429. 'copyright' => __('Copyright Notice', 'nggallery'),  
  430. 'contact' => __('Contact', 'nggallery'),  
  431. 'last_modfied' => __('Last modified', 'nggallery'),  
  432. 'tool' => __('Program tool', 'nggallery'),  
  433. 'format' => __('Format', 'nggallery'),  
  434. 'width' => __('Image Width', 'nggallery'),  
  435. 'height' => __('Image Height', 'nggallery'),  
  436. 'flash' => __('Flash', 'nggallery') 
  437. ); 
  438.  
  439. if ( isset($tagnames[$key]) ) 
  440. $key = $tagnames[$key]; 
  441.  
  442. return($key); 
  443.  
  444.  
  445. /** 
  446. * Return the Timestamp from the image , if possible it's read from exif data 
  447. * @return 
  448. */ 
  449. function get_date_time() { 
  450.  
  451. $date_time = time(); 
  452.  
  453. // get exif - data 
  454. if ( isset( $this->exif_data['EXIF']) ) { 
  455.  
  456. // try to read the date / time from the exif 
  457. foreach (array('DateTimeDigitized', 'DateTimeOriginal', 'FileDateTime') as $key) { 
  458. if (isset($this->exif_data['EXIF'][$key])) { 
  459. $date_time = strtotime($this->exif_data['EXIF'][$key]); 
  460. break; 
  461. } else { 
  462. // if no other date available, get the filetime 
  463. $date_time = @filectime($this->image->imagePath ); 
  464.  
  465. // Return the MySQL format 
  466. $date_time = date( 'Y-m-d H:i:s', $date_time ); 
  467.  
  468. return $date_time; 
  469.  
  470. /** 
  471. * This function return the most common metadata, via a filter we can add more 
  472. * Reason : GD manipulation removes that options 
  473. * @since V1.4.0 
  474. * @return array|boolean 
  475. */ 
  476. function get_common_meta() { 
  477. global $wpdb; 
  478.  
  479. $meta = array( 
  480. 'aperture' => 0,  
  481. 'credit' => '',  
  482. 'camera' => '',  
  483. 'caption' => '',  
  484. 'created_timestamp' => 0,  
  485. 'copyright' => '',  
  486. 'focal_length' => 0,  
  487. 'iso' => 0,  
  488. 'shutter_speed' => 0,  
  489. 'flash' => 0,  
  490. 'title' => '',  
  491. 'keywords' => '' 
  492. ); 
  493.  
  494. $meta = apply_filters( 'ngg_read_image_metadata', $meta ); 
  495.  
  496. // meta should be still an array 
  497. if ( !is_array($meta) ) 
  498. return false; 
  499.  
  500. foreach ($meta as $key => $value) { 
  501. $meta[$key] = $this->get_META($key); 
  502.  
  503. //let's add now the size of the image 
  504. $meta['width'] = $this->size[0]; 
  505. $meta['height'] = $this->size[1]; 
  506.  
  507. return $meta; 
  508.  
  509. /** 
  510. * If needed sanitize each value before output 
  511. * @return void 
  512. */ 
  513. function sanitize () { 
  514. $this->sanitize = true; 
  515.