PageRenderTime 41ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/add-ons/pjmt/Photoshop_File_Info.php

https://github.com/jcplat/console-seolan
PHP | 2498 lines | 1215 code | 514 blank | 769 comment | 221 complexity | 97e084ed80b56ce2e576afed3824bafe MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, GPL-3.0, Apache-2.0, BSD-3-Clause
  1. <?php
  2. /******************************************************************************
  3. *
  4. * Filename: Photoshop_File_Info.php
  5. *
  6. * Description: Provides functions that mimic the way Photoshop reads and writes
  7. * metadata in it's 'File Info' dialog
  8. *
  9. * Author: Evan Hunter
  10. *
  11. * Date: 11/11/2004
  12. *
  13. * Project: JPEG Metadata
  14. *
  15. * Revision: 1.11
  16. * Changes: 1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php
  17. *
  18. * URL: http://electronics.ozhiker.com
  19. *
  20. * License: This file is part of the PHP JPEG Metadata Toolkit.
  21. *
  22. * The PHP JPEG Metadata Toolkit is free software; you can
  23. * redistribute it and/or modify it under the terms of the
  24. * GNU General Public License as published by the Free Software
  25. * Foundation; either version 2 of the License, or (at your
  26. * option) any later version.
  27. *
  28. * The PHP JPEG Metadata Toolkit is distributed in the hope
  29. * that it will be useful, but WITHOUT ANY WARRANTY; without
  30. * even the implied warranty of MERCHANTABILITY or FITNESS
  31. * FOR A PARTICULAR PURPOSE. See the GNU General Public License
  32. * for more details.
  33. *
  34. * You should have received a copy of the GNU General Public
  35. * License along with the PHP JPEG Metadata Toolkit; if not,
  36. * write to the Free Software Foundation, Inc., 59 Temple
  37. * Place, Suite 330, Boston, MA 02111-1307 USA
  38. *
  39. * If you require a different license for commercial or other
  40. * purposes, please contact the author: evan@ozhiker.com
  41. *
  42. ******************************************************************************/
  43. // TODO: XMP sections: XAPMM, TIFF, EXIF
  44. include 'Toolkit_Version.php'; // Change: added as of version 1.11
  45. /******************************************************************************
  46. * Global Variable: Software Name
  47. *
  48. * Contents: The string that is appended to fields which store the name of
  49. * the software editor.
  50. *
  51. ******************************************************************************/
  52. $GLOBALS[ "Software Name" ] = "(PHP JPEG Metadata Toolkit v" . $GLOBALS['Toolkit_Version'] . ")"; // Change: Changed version numbers to reference Toolkit_Version.php - as of version 1.11
  53. /******************************************************************************
  54. * End of Global Variable: Software Name
  55. ******************************************************************************/
  56. /******************************************************************************
  57. *
  58. * Function: get_photoshop_file_info
  59. *
  60. * Description: Retrieves Photoshop 'File Info' metadata in the same way that Photoshop
  61. * does. The results are returned in an array as below:
  62. *
  63. * $file_info_array = array(
  64. * "title" => "",
  65. * "author" => "",
  66. * "authorsposition" => "", // Note: Not used in Photoshop 7 or higher
  67. * "caption" => "",
  68. * "captionwriter" => "",
  69. * "jobname" => "", // Note: Not used in Photoshop CS
  70. * "copyrightstatus" => "",
  71. * "copyrightnotice" => "",
  72. * "ownerurl" => "",
  73. * "keywords" => array( 0 => "", 1 => "", ... ),
  74. * "category" => "", // Note: Max 3 characters
  75. * "supplementalcategories" => array( 0 => "", 1 => "", ... ),
  76. * "date" => "", // Note: DATE MUST BE IN YYYY-MM-DD format
  77. * "city" => "",
  78. * "state" => "",
  79. * "country" => "",
  80. * "credit" => "",
  81. * "source" => "",
  82. * "headline" => "",
  83. * "instructions" => "",
  84. * "transmissionreference" => "",
  85. * "urgency" => "" );
  86. *
  87. * Parameters: Exif_array - an array containing the EXIF information to be
  88. * searched, as retrieved by get_EXIF_JPEG. (saves having to parse the EXIF again)
  89. * XMP_array - an array containing the XMP information to be
  90. * searched, as retrieved by read_XMP_array_from_text. (saves having to parse the XMP again)
  91. * IRB_array - an array containing the Photoshop IRB information
  92. * to be searched, as retrieved by get_Photoshop_IRB. (saves having to parse the IRB again)
  93. *
  94. * Returns: outputarray - an array as above, containing the Photoshop File Info data
  95. *
  96. ******************************************************************************/
  97. function get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array )
  98. {
  99. // Create a blank array to receive the output
  100. $outputarray = array(
  101. "title" => "",
  102. "author" => "",
  103. "authorsposition" => "",
  104. "caption" => "",
  105. "captionwriter" => "",
  106. "jobname" => "",
  107. "copyrightstatus" => "",
  108. "copyrightnotice" => "",
  109. "ownerurl" => "",
  110. "keywords" => array(),
  111. "category" => "",
  112. "supplementalcategories" => array(),
  113. "date" => "",
  114. "city" => "",
  115. "state" => "",
  116. "country" => "",
  117. "credit" => "",
  118. "source" => "",
  119. "headline" => "",
  120. "instructions" => "",
  121. "transmissionreference" => "",
  122. "urgency" => "" );
  123. /***************************************/
  124. // XMP Processing
  125. // Retrieve the dublin core section from the XMP header
  126. // Extract the Dublin Core section from the XMP
  127. $dublincore_block = find_XMP_block( $XMP_array, "dc" );
  128. // Check that the Dublin Core section exists
  129. if ( $dublincore_block != FALSE )
  130. {
  131. // Dublin Core Description Field contains caption
  132. // Extract Description
  133. $Item = find_XMP_item( $dublincore_block, "dc:description" );
  134. // Check if Description Tag existed
  135. if ( $Item != FALSE )
  136. {
  137. // Ensure that the Description value exists and save it.
  138. if ( ( array_key_exists( 'children', $Item ) ) &&
  139. ( $Item['children'][0]['tag'] == "rdf:Alt" ) &&
  140. ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) )
  141. {
  142. $outputarray = add_to_field( $outputarray, 'caption' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "\n" );
  143. }
  144. }
  145. /***************************************/
  146. // Dublin Core Creator Field contains author
  147. // Extract Description
  148. $Item = find_XMP_item( $dublincore_block, "dc:creator" );
  149. // Check if Creator Tag existed
  150. if ( $Item != FALSE )
  151. {
  152. // Ensure that the Creator value exists and save it.
  153. if ( ( array_key_exists( 'children', $Item ) ) &&
  154. ( $Item['children'][0]['tag'] =="rdf:Seq" ) &&
  155. ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) )
  156. {
  157. $outputarray = add_to_field( $outputarray, 'author' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "\n" );
  158. }
  159. }
  160. /***************************************/
  161. // Dublin Core Title Field contains title
  162. // Extract Title
  163. $Item = find_XMP_item( $dublincore_block, "dc:title" );
  164. // Check if Title Tag existed
  165. if ( $Item != FALSE )
  166. {
  167. // Ensure that the Title value exists and save it.
  168. if ( ( array_key_exists( 'children', $Item ) ) &&
  169. ( $Item['children'][0]['tag'] =="rdf:Alt" ) &&
  170. ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) )
  171. {
  172. $outputarray = add_to_field( $outputarray, 'title' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "," );
  173. }
  174. }
  175. /***************************************/
  176. // Dublin Core Rights Field contains copyrightnotice
  177. // Extract Rights
  178. $Item = find_XMP_item( $dublincore_block, "dc:rights" );
  179. // Check if Rights Tag existed
  180. if ( $Item != FALSE )
  181. {
  182. // Ensure that the Rights value exists and save it.
  183. if ( ( array_key_exists( 'children', $Item ) ) &&
  184. ( $Item['children'][0]['tag'] =="rdf:Alt" ) &&
  185. ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) )
  186. {
  187. $outputarray = add_to_field( $outputarray, 'copyrightnotice' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "," );
  188. }
  189. }
  190. /***************************************/
  191. // Dublin Core Subject Field contains keywords
  192. // Extract Subject
  193. $Item = find_XMP_item( $dublincore_block, "dc:subject" );
  194. // Check if Subject Tag existed
  195. if ( $Item != FALSE )
  196. {
  197. // Ensure that the Subject values exist
  198. if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) )
  199. {
  200. // Cycle through each Subject value and save them
  201. foreach ( $Item['children'][0]['children'] as $keywords )
  202. {
  203. if ( ! in_array ( HTML_UTF8_Escape( $keywords['value'] ), $outputarray['keywords']))
  204. {
  205. if ( array_key_exists( 'value', $keywords ) )
  206. {
  207. $outputarray['keywords'][] = HTML_UTF8_Escape( $keywords['value'] );
  208. }
  209. }
  210. }
  211. }
  212. }
  213. }
  214. /***************************************/
  215. // Find the Photoshop Information within the XMP block
  216. $photoshop_block = find_XMP_block( $XMP_array, "photoshop" );
  217. // Check that the Photoshop Information exists
  218. if ( $photoshop_block != FALSE )
  219. {
  220. // The Photoshop CaptionWriter tag contains captionwriter - Find it
  221. $Item = find_XMP_item( $photoshop_block, "photoshop:CaptionWriter" );
  222. // Check that the CaptionWriter Field exists and save the value
  223. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  224. {
  225. $outputarray = add_to_field( $outputarray, 'captionwriter' , HTML_UTF8_Escape( $Item['value'] ), "," );
  226. }
  227. /***************************************/
  228. // The Photoshop Headline tag contains headline - Find it
  229. $Item = find_XMP_item( $photoshop_block, "photoshop:Headline" );
  230. // Check that the Headline Field exists and save the value
  231. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  232. {
  233. $outputarray = add_to_field( $outputarray, 'headline' , HTML_UTF8_Escape( $Item['value'] ), "," );
  234. }
  235. /***************************************/
  236. // The Photoshop Instructions tag contains instructions - Find it
  237. $Item = find_XMP_item( $photoshop_block, "photoshop:Instructions" );
  238. // Check that the Instructions Field exists and save the value
  239. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  240. {
  241. $outputarray = add_to_field( $outputarray, 'instructions' , HTML_UTF8_Escape( $Item['value'] ), "\n" );
  242. }
  243. /***************************************/
  244. // The Photoshop AuthorsPosition tag contains authorsposition - Find it
  245. $Item = find_XMP_item( $photoshop_block, "photoshop:AuthorsPosition" );
  246. // Check that the AuthorsPosition Field exists and save the value
  247. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  248. {
  249. $outputarray = add_to_field( $outputarray, 'authorsposition' , HTML_UTF8_Escape( $Item['value'] ), "," );
  250. }
  251. /***************************************/
  252. // The Photoshop Credit tag contains credit - Find it
  253. $Item = find_XMP_item( $photoshop_block, "photoshop:Credit" );
  254. // Check that the Credit Field exists and save the value
  255. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  256. {
  257. $outputarray = add_to_field( $outputarray, 'credit' , HTML_UTF8_Escape( $Item['value'] ), "," );
  258. }
  259. /***************************************/
  260. // The Photoshop Source tag contains source - Find it
  261. $Item = find_XMP_item( $photoshop_block, "photoshop:Source" );
  262. // Check that the Credit Field exists and save the value
  263. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  264. {
  265. $outputarray = add_to_field( $outputarray, 'source' , HTML_UTF8_Escape( $Item['value'] ), "," );
  266. }
  267. /***************************************/
  268. // The Photoshop City tag contains city - Find it
  269. $Item = find_XMP_item( $photoshop_block, "photoshop:City" );
  270. // Check that the City Field exists and save the value
  271. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  272. {
  273. $outputarray = add_to_field( $outputarray, 'city' , HTML_UTF8_Escape( $Item['value'] ), "," );
  274. }
  275. /***************************************/
  276. // The Photoshop State tag contains state - Find it
  277. $Item = find_XMP_item( $photoshop_block, "photoshop:State" );
  278. // Check that the State Field exists and save the value
  279. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  280. {
  281. $outputarray = add_to_field( $outputarray, 'state' , HTML_UTF8_Escape( $Item['value'] ), "," );
  282. }
  283. /***************************************/
  284. // The Photoshop Country tag contains country - Find it
  285. $Item = find_XMP_item( $photoshop_block, "photoshop:Country" );
  286. // Check that the Country Field exists and save the value
  287. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  288. {
  289. $outputarray = add_to_field( $outputarray, 'country' , HTML_UTF8_Escape( $Item['value'] ), "," );
  290. }
  291. /***************************************/
  292. // The Photoshop TransmissionReference tag contains transmissionreference - Find it
  293. $Item = find_XMP_item( $photoshop_block, "photoshop:TransmissionReference" );
  294. // Check that the TransmissionReference Field exists and save the value
  295. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  296. {
  297. $outputarray = add_to_field( $outputarray, 'transmissionreference' , HTML_UTF8_Escape( $Item['value'] ), "," );
  298. }
  299. /***************************************/
  300. // The Photoshop Category tag contains category - Find it
  301. $Item = find_XMP_item( $photoshop_block, "photoshop:Category" );
  302. // Check that the TransmissionReference Field exists and save the value
  303. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  304. {
  305. $outputarray = add_to_field( $outputarray, 'category' , HTML_UTF8_Escape( $Item['value'] ), "," );
  306. }
  307. /***************************************/
  308. // The Photoshop DateCreated tag contains date - Find it
  309. $Item = find_XMP_item( $photoshop_block, "photoshop:DateCreated" );
  310. // Check that the DateCreated Field exists and save the value
  311. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  312. {
  313. $outputarray = add_to_field( $outputarray, 'date' , HTML_UTF8_Escape( $Item['value'] ), "," );
  314. }
  315. /***************************************/
  316. // The Photoshop Urgency tag contains urgency - Find it
  317. $Item = find_XMP_item( $photoshop_block, "photoshop:Urgency" );
  318. // Check that the Urgency Field exists and save the value
  319. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  320. {
  321. $outputarray = add_to_field( $outputarray, 'urgency' , HTML_UTF8_Escape( $Item['value'] ), "," );
  322. }
  323. /***************************************/
  324. // The Photoshop SupplementalCategories tag contains supplementalcategories - Find it
  325. $Item = find_XMP_item( $photoshop_block, "photoshop:SupplementalCategories" );
  326. // Check that the SupplementalCategories Field exists
  327. if ( $Item != FALSE )
  328. {
  329. // Check that the values exist
  330. if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) )
  331. {
  332. // Cycle through the values and save them
  333. foreach ( $Item['children'][0]['children'] as $sup_category )
  334. {
  335. if ( ( array_key_exists( 'value', $sup_category ) ) &&
  336. ( ! in_array ( HTML_UTF8_Escape( $sup_category['value'] ), $outputarray['supplementalcategories'])) )
  337. {
  338. if ( array_key_exists( 'value', $sup_category ) )
  339. {
  340. $outputarray['supplementalcategories'][] = HTML_UTF8_Escape( $sup_category['value'] );
  341. }
  342. }
  343. }
  344. }
  345. }
  346. }
  347. /***************************************/
  348. // Find the Job Reference Information within the XMP block
  349. $job_block = find_XMP_block( $XMP_array, "xapBJ" );
  350. // Check that the Job Reference Information exists
  351. if ( $job_block != FALSE )
  352. {
  353. // The JobRef Field contains jobname - Find it
  354. $Item = find_XMP_item( $job_block, "xapBJ:JobRef" );
  355. // Check that the JobRef Field exists
  356. if ( $Item != FALSE )
  357. {
  358. // Check that the value exists and save it
  359. if ( ( array_key_exists( 'children', $Item ) ) &&
  360. ( $Item['children'][0]['tag'] =="rdf:Bag" ) &&
  361. ( array_key_exists( 'children', $Item['children'][0] ) ) &&
  362. ( $Item['children'][0]['children'][0]['tag'] =="rdf:li" ) &&
  363. ( array_key_exists( 'children', $Item['children'][0]['children'][0] ) ) &&
  364. ( $Item['children'][0]['children'][0]['children'][0]['tag'] =="stJob:name" ) &&
  365. ( array_key_exists( 'value', $Item['children'][0]['children'][0]['children'][0] ) ) )
  366. {
  367. $outputarray = add_to_field( $outputarray, 'jobname' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['children'][0]['value'] ), "," );
  368. }
  369. }
  370. }
  371. /***************************************/
  372. // Find the Rights Information within the XMP block
  373. $rights_block = find_XMP_block( $XMP_array, "xapRights" );
  374. // Check that the Rights Information exists
  375. if ( $rights_block != FALSE )
  376. {
  377. // The WebStatement Field contains ownerurl - Find it
  378. $Item = find_XMP_item( $rights_block, "xapRights:WebStatement" );
  379. // Check that the WebStatement Field exists and save the value
  380. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  381. {
  382. $outputarray = add_to_field( $outputarray, 'ownerurl' , HTML_UTF8_Escape( $Item['value'] ), "\n" );
  383. }
  384. /***************************************/
  385. // The Marked Field contains copyrightstatus - Find it
  386. $Item = find_XMP_item( $rights_block, "xapRights:Marked" );
  387. // Check that the Marked Field exists and save the value
  388. if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
  389. {
  390. if ( $Item['value'] == "True" )
  391. {
  392. $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Copyrighted Work", "," );
  393. }
  394. else
  395. {
  396. $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Public Domain", "," );
  397. }
  398. }
  399. }
  400. /***************************************/
  401. // Photoshop IRB Processing
  402. // Check that the Photoshop IRB exists
  403. if ( $IRB_array != FALSE )
  404. {
  405. // Create a translation table to convert carriage returns to linefeeds
  406. $irbtrans = array("\x0d" => "\x0a");
  407. // The Photoshop IRB Copyright flag (0x040A) contains copyrightstatus - find it
  408. $IRB_copyright_flag = find_Photoshop_IRB_Resource( $IRB_array, 0x040A );
  409. // Check if the Copyright flag Field exists, and save the value
  410. if( $IRB_copyright_flag != FALSE )
  411. {
  412. // Check the value of the copyright flag
  413. if ( hexdec( bin2hex( $IRB_copyright_flag['ResData'] ) ) == 1 )
  414. {
  415. // Save the value
  416. $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Copyrighted Work", "," );
  417. }
  418. else
  419. {
  420. // Do nothing - copyrightstatus will be set to unmarked if still blank at end
  421. }
  422. }
  423. /***************************************/
  424. // The Photoshop IRB URL (0x040B) contains ownerurl - find it
  425. $IRB_url = find_Photoshop_IRB_Resource( $IRB_array, 0x040B );
  426. // Check if the URL Field exists and save the value
  427. if( $IRB_url != FALSE )
  428. {
  429. $outputarray = add_to_field( $outputarray, 'ownerurl' , strtr( $IRB_url['ResData'], $irbtrans ), "\n" );
  430. }
  431. /***************************************/
  432. // Extract any IPTC block from the Photoshop IRB information
  433. $IPTC_array = get_Photoshop_IPTC( $IRB_array );
  434. // Check if the IPTC block exits
  435. if ( ( $IPTC_array != FALSE ) && ( count( $IPTC_array ) != 0 ) )
  436. {
  437. // The IPTC Caption/Abstract Field contains caption - find it
  438. $record = find_IPTC_Resource( $IPTC_array, "2:120" );
  439. // Check if the Caption/Abstract Field exists and save the value
  440. if ( $record != FALSE )
  441. {
  442. $outputarray = add_to_field( $outputarray, 'caption' , strtr( $record['RecData'], $irbtrans ), "\n" );
  443. }
  444. /***************************************/
  445. // The IPTC Caption Writer/Editor Field contains captionwriter - find it
  446. $record = find_IPTC_Resource( $IPTC_array, "2:122" );
  447. // Check if the Caption Writer/Editor Field exists and save the value
  448. if ( $record != FALSE )
  449. {
  450. $outputarray = add_to_field( $outputarray, 'captionwriter' , strtr( $record['RecData'], $irbtrans ), "\n" );
  451. }
  452. /***************************************/
  453. // The IPTC Headline Field contains headline - find it
  454. $record = find_IPTC_Resource( $IPTC_array, "2:105" );
  455. // Check if the Headline Field exists and save the value
  456. if ( $record != FALSE )
  457. {
  458. $outputarray = add_to_field( $outputarray, 'headline' , strtr( $record['RecData'], $irbtrans ), "\n" );
  459. }
  460. /***************************************/
  461. // The IPTC Special Instructions Field contains instructions - find it
  462. $record = find_IPTC_Resource( $IPTC_array, "2:40" );
  463. // Check if the Special Instructions Field exists and save the value
  464. if ( $record != FALSE )
  465. {
  466. $outputarray = add_to_field( $outputarray, 'instructions' , strtr( $record['RecData'], $irbtrans ), "\n" );
  467. }
  468. /***************************************/
  469. // The IPTC By-Line Field contains author - find it
  470. $record = find_IPTC_Resource( $IPTC_array, "2:80" );
  471. // Check if the By-Line Field exists and save the value
  472. if ( $record != FALSE )
  473. {
  474. $outputarray = add_to_field( $outputarray, 'author' , strtr( $record['RecData'], $irbtrans ), "\n" );
  475. }
  476. /***************************************/
  477. // The IPTC By-Line Title Field contains authorsposition - find it
  478. $record = find_IPTC_Resource( $IPTC_array, "2:85" );
  479. // Check if the By-Line Title Field exists and save the value
  480. if ( $record != FALSE )
  481. {
  482. $outputarray = add_to_field( $outputarray, 'authorsposition' , strtr( $record['RecData'], $irbtrans ), "\n" );
  483. }
  484. /***************************************/
  485. // The IPTC Credit Field contains credit - find it
  486. $record = find_IPTC_Resource( $IPTC_array, "2:110" );
  487. // Check if the Credit Field exists and save the value
  488. if ( $record != FALSE )
  489. {
  490. $outputarray = add_to_field( $outputarray, 'credit' , strtr( $record['RecData'], $irbtrans ), "\n" );
  491. }
  492. /***************************************/
  493. // The IPTC Source Field contains source - find it
  494. $record = find_IPTC_Resource( $IPTC_array, "2:115" );
  495. // Check if the Source Field exists and save the value
  496. if ( $record != FALSE )
  497. {
  498. $outputarray = add_to_field( $outputarray, 'source' , strtr( $record['RecData'], $irbtrans ), "\n" );
  499. }
  500. /***************************************/
  501. // The IPTC Object Name Field contains title - find it
  502. $record = find_IPTC_Resource( $IPTC_array, "2:05" );
  503. // Check if the Object Name Field exists and save the value
  504. if ( $record != FALSE )
  505. {
  506. $outputarray = add_to_field( $outputarray, 'title' , strtr( $record['RecData'], $irbtrans ), "\n" );
  507. }
  508. /***************************************/
  509. // The IPTC Date Created Field contains date - find it
  510. $record = find_IPTC_Resource( $IPTC_array, "2:55" );
  511. // Check if the Date Created Field exists and save the value
  512. if ( $record != FALSE )
  513. {
  514. $date_array = unpack( "a4Year/a2Month/A2Day", $record['RecData'] );
  515. $tmpdate = $date_array['Year'] . "-" . $date_array['Month'] . "-" . $date_array['Day'];
  516. $outputarray = add_to_field( $outputarray, 'date' , strtr( $tmpdate, $irbtrans ), "," );
  517. }
  518. /***************************************/
  519. // The IPTC City Field contains city - find it
  520. $record = find_IPTC_Resource( $IPTC_array, "2:90" );
  521. // Check if the City Field exists and save the value
  522. if ( $record != FALSE )
  523. {
  524. $outputarray = add_to_field( $outputarray, 'city' , strtr( $record['RecData'], $irbtrans ), "\n" );
  525. }
  526. /***************************************/
  527. // The IPTC Province/State Field contains state - find it
  528. $record = find_IPTC_Resource( $IPTC_array, "2:95" );
  529. // Check if the Province/State Field exists and save the value
  530. if ( $record != FALSE )
  531. {
  532. $outputarray = add_to_field( $outputarray, 'state' , strtr( $record['RecData'], $irbtrans ), "\n" );
  533. }
  534. /***************************************/
  535. // The IPTC Country/Primary Location Name Field contains country - find it
  536. $record = find_IPTC_Resource( $IPTC_array, "2:101" );
  537. // Check if the Country/Primary Location Name Field exists and save the value
  538. if ( $record != FALSE )
  539. {
  540. $outputarray = add_to_field( $outputarray, 'country' , strtr( $record['RecData'], $irbtrans ), "\n" );
  541. }
  542. /***************************************/
  543. // The IPTC Original Transmission Reference Field contains transmissionreference - find it
  544. $record = find_IPTC_Resource( $IPTC_array, "2:103" );
  545. // Check if the Original Transmission Reference Field exists and save the value
  546. if ( $record != FALSE )
  547. {
  548. $outputarray = add_to_field( $outputarray, 'transmissionreference' , strtr( $record['RecData'], $irbtrans ), "\n" );
  549. }
  550. /***************************************/
  551. // The IPTC Category Field contains category - find it
  552. $record = find_IPTC_Resource( $IPTC_array, "2:15" );
  553. // Check if the Category Field exists and save the value
  554. if ( $record != FALSE )
  555. {
  556. $outputarray = add_to_field( $outputarray, 'category' , strtr( $record['RecData'], $irbtrans ), "\n" );
  557. }
  558. /***************************************/
  559. // Cycle through the IPTC records looking for Supplemental Category records
  560. foreach ($IPTC_array as $record)
  561. {
  562. // Check if a Supplemental Category record has been found
  563. if ( $record['IPTC_Type'] == "2:20" )
  564. {
  565. // A Supplemental Category record has been found, save it's value if the value doesn't already exist
  566. if ( ! in_array ( $record['RecData'], $outputarray['supplementalcategories']))
  567. {
  568. $outputarray['supplementalcategories'][] = strtr( $record['RecData'], array("\x0a" => "", "\x0d" => "&#xA;") ) ;
  569. }
  570. }
  571. }
  572. /***************************************/
  573. // The IPTC Urgency Field contains urgency - find it
  574. $record = find_IPTC_Resource( $IPTC_array, "2:10" );
  575. // Check if the Urgency Field exists and save the value
  576. if ( $record != FALSE )
  577. {
  578. $outputarray = add_to_field( $outputarray, 'urgency' , strtr( $record['RecData'], $irbtrans ), "\n" );
  579. }
  580. /***************************************/
  581. // Cycle through the IPTC records looking for Keywords records
  582. foreach ($IPTC_array as $record)
  583. {
  584. // Check if a Keywords record has been found
  585. if ( $record['IPTC_Type'] == "2:25" )
  586. {
  587. // A Keywords record has been found, save it's value if the value doesn't already exist
  588. if ( ! in_array ( $record['RecData'], $outputarray['keywords']))
  589. {
  590. $outputarray['keywords'][] = strtr( $record['RecData'], array("\x0a" => "", "\x0d" => "&#xA;") ) ;
  591. }
  592. }
  593. }
  594. /***************************************/
  595. // The IPTC Copyright Notice Field contains copyrightnotice - find it
  596. $record = find_IPTC_Resource( $IPTC_array, "2:116" );
  597. // Check if the Copyright Field exists and save the value
  598. if ( $record != FALSE )
  599. {
  600. $outputarray = add_to_field( $outputarray, 'copyrightnotice' , strtr( $record['RecData'], $irbtrans ), "\n" );
  601. }
  602. }
  603. }
  604. /***************************************/
  605. // EXIF Processing
  606. // Retreive Information from the EXIF data if it exists
  607. if ( ( $Exif_array != FALSE ) || ( count( $Exif_array ) == 0 ) )
  608. {
  609. // Check the Image Description Tag - it can contain the caption
  610. if ( array_key_exists( 270, $Exif_array[0] ) )
  611. {
  612. $outputarray = add_to_field( $outputarray, 'caption' , $Exif_array[0][270]['Data'][0], "\n" );
  613. }
  614. /***************************************/
  615. // Check the Copyright Information Tag - it contains the copyrightnotice
  616. if ( array_key_exists( 33432, $Exif_array[0] ) )
  617. {
  618. $outputarray = add_to_field( $outputarray, 'copyrightnotice' , HTML_UTF8_UnEscape( $Exif_array[0][33432]['Data'][0] ), "\n" );
  619. }
  620. /***************************************/
  621. // Check the Artist Name Tag - it contains the author
  622. if ( array_key_exists( 315, $Exif_array[0] ) )
  623. {
  624. $outputarray = add_to_field( $outputarray, 'author' , HTML_UTF8_UnEscape( $Exif_array[0][315]['Data'][0] ), "\n" );
  625. }
  626. }
  627. /***************************/
  628. // FINISHED RETRIEVING INFORMATION
  629. // Perform final processing
  630. // Check if any urgency information was found
  631. if ( $outputarray["urgency"] == "" )
  632. {
  633. // No urgency information was found - set it to default (None)
  634. $outputarray["urgency"] = "none";
  635. }
  636. // Check if any copyrightstatus information was found
  637. if ( $outputarray["copyrightstatus"] == "" )
  638. {
  639. // No copyrightstatus information was found - set it to default (Unmarked)
  640. $outputarray["copyrightstatus"] = "unmarked";
  641. }
  642. // Return the resulting Photoshop File Info Array
  643. return $outputarray;
  644. }
  645. /******************************************************************************
  646. * End of Function: get_photoshop_file_info
  647. ******************************************************************************/
  648. /******************************************************************************
  649. *
  650. * Function: put_photoshop_file_info
  651. *
  652. * Description: Stores Photoshop 'File Info' metadata in the same way that Photoshop
  653. * does. The 'File Info' metadata must be in an array similar to that
  654. * returned by get_photoshop_file_info, as follows:
  655. *
  656. * $file_info_array = array(
  657. * "title" => "",
  658. * "author" => "",
  659. * "authorsposition" => "", // Note: Not used in Photoshop 7 or higher
  660. * "caption" => "",
  661. * "captionwriter" => "",
  662. * "jobname" => "", // Note: Not used in Photoshop CS
  663. * "copyrightstatus" => "",
  664. * "copyrightnotice" => "",
  665. * "ownerurl" => "",
  666. * "keywords" => array( 0 => "", 1 => "", ... ),
  667. * "category" => "", // Note: Max 3 characters
  668. * "supplementalcategories" => array( 0 => "", 1 => "", ... ),
  669. * "date" => "", // Note: DATE MUST BE IN YYYY-MM-DD format
  670. * "city" => "",
  671. * "state" => "",
  672. * "country" => "",
  673. * "credit" => "",
  674. * "source" => "",
  675. * "headline" => "",
  676. * "instructions" => "",
  677. * "transmissionreference" => "",
  678. * "urgency" => "" );
  679. *
  680. * Parameters: jpeg_header_data - a JPEG header data array in the same format
  681. * as from get_jpeg_header_data. This contains the
  682. * header information which is to be updated.
  683. * new_ps_file_info_array - An array as above, which contains the
  684. * 'File Info' metadata information to be
  685. * written.
  686. * Old_Exif_array - an array containing the EXIF information to be
  687. * updated, as retrieved by get_EXIF_JPEG. (saves having to parse the EXIF again)
  688. * Old_XMP_array - an array containing the XMP information to be
  689. * updated, as retrieved by read_XMP_array_from_text. (saves having to parse the XMP again)
  690. * Old_IRB_array - an array containing the Photoshop IRB information
  691. * to be updated, as retrieved by get_Photoshop_IRB. (saves having to parse the IRB again)
  692. *
  693. * Returns: jpeg_header_data - a JPEG header data array in the same format
  694. * as from get_jpeg_header_data, containing the
  695. * Photshop 'File Info' metadata. This can then
  696. * be written to a file using put_jpeg_header_data.
  697. *
  698. ******************************************************************************/
  699. function put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Old_Exif_array, $Old_XMP_array, $Old_IRB_array )
  700. {
  701. /*******************************************/
  702. // PREPROCESSING
  703. // Check that the date is in the correct format (YYYY-MM-DD)
  704. // Explode the date into pieces using the - symbol
  705. $date_pieces = explode( "-", $new_ps_file_info_array[ 'date' ] );
  706. // If there are not 3 pieces to the date, it is invalid
  707. if ( count( $date_pieces ) != 3 )
  708. {
  709. // INVALID DATE
  710. echo "Invalid Date - must be YYYY-MM-DD format<br>";
  711. return FALSE;
  712. }
  713. // Cycle through each piece of the date
  714. foreach( $date_pieces as $piece )
  715. {
  716. // If the piece is not numeric, then the date is invalid.
  717. if ( ! is_numeric( $piece ) )
  718. {
  719. // INVALID DATE
  720. echo "Invalid Date - must be YYYY-MM-DD format<br>";
  721. return FALSE;
  722. }
  723. }
  724. // Make a unix timestamp at midnight on the date specified
  725. $date_stamp = mktime( 0,0,0, $date_pieces[1], $date_pieces[2], $date_pieces[0] );
  726. // Create a translation table to remove carriage return characters
  727. $trans = array( "\x0d" => "" );
  728. // Cycle through each of the File Info elements
  729. foreach( $new_ps_file_info_array as $valkey => $val )
  730. {
  731. // If the element is 'Keywords' or 'Supplemental Categories', then
  732. // it is an array, and needs to be treated as one
  733. if ( ( $valkey != 'supplementalcategories' ) && ( $valkey != 'keywords' ) )
  734. {
  735. // Not Keywords or Supplemental Categories
  736. // Convert escaped HTML characters to UTF8 and remove carriage returns
  737. $new_ps_file_info_array[ $valkey ] = strtr( HTML_UTF8_UnEscape( $val ), $trans );
  738. }
  739. else
  740. {
  741. // Either Keywords or Supplemental Categories
  742. // Cycle through the array,
  743. foreach( $val as $subvalkey => $subval )
  744. {
  745. // Convert escaped HTML characters to UTF8 and remove carriage returns
  746. $new_ps_file_info_array[ $valkey ][ $subvalkey ] = strtr( HTML_UTF8_UnEscape( $subval ), $trans );
  747. }
  748. }
  749. }
  750. /*******************************************/
  751. // EXIF Processing
  752. // Check if the EXIF array exists
  753. if( $Old_Exif_array == FALSE )
  754. {
  755. // EXIF Array doesn't exist - create a new one
  756. $new_Exif_array = array ( 'Byte_Align' => "MM",
  757. 'Makernote_Tag' => false,
  758. 'Tags Name' => "TIFF",
  759. 0 => array( "Tags Name" => "TIFF" ) );
  760. }
  761. else
  762. {
  763. // EXIF Array Does Exist - use it
  764. $new_Exif_array = $Old_Exif_array;
  765. }
  766. // Update the EXIF Image Description Tag with the new value
  767. $new_Exif_array[0][270] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 270 ]['Name'],
  768. "Tag Number" => 270,
  769. "Data Type" => 2,
  770. "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 270 ]['Type'],
  771. "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'caption' ]) ));
  772. // Update the EXIF Artist Name Tag with the new value
  773. $new_Exif_array[0][315] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 315 ]['Name'],
  774. "Tag Number" => 315,
  775. "Data Type" => 2,
  776. "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 315 ]['Type'],
  777. "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'author' ] ) ) );
  778. // Update the EXIF Copyright Information Tag with the new value
  779. $new_Exif_array[0][33432] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 33432 ]['Name'],
  780. "Tag Number" => 33432,
  781. "Data Type" => 2,
  782. "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 33432 ]['Type'],
  783. "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'copyrightnotice' ]) ) );
  784. // Photoshop checks if the "Date and Time of Original" and "Date and Time when Digitized" tags exist
  785. // If they don't exist, it means that the EXIF date may be wiped out if it is changed, so Photoshop
  786. // copies the EXIF date to these two tags
  787. if ( ( array_key_exists( 306, $new_Exif_array[0] ) )&&
  788. ( array_key_exists( 34665, $new_Exif_array[0] ) ) &&
  789. ( array_key_exists( 0, $new_Exif_array[0][34665] ) ) )
  790. {
  791. // Replace "Date and Time of Original" if it doesn't exist
  792. if ( ! array_key_exists( 36867, $new_Exif_array[0][34665][0] ) )
  793. {
  794. $new_Exif_array[0][34665][0][36867] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36867 ]['Name'],
  795. "Tag Number" => 36867,
  796. "Data Type" => 2,
  797. "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36867 ]['Type'],
  798. "Data" => $new_Exif_array[0][306]['Data'] );
  799. }
  800. // Replace "Date and Time when Digitized" if it doesn't exist
  801. if ( ! array_key_exists( 36868, $new_Exif_array[0][34665][0] ) )
  802. {
  803. $new_Exif_array[0][34665][0][36868] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36868 ]['Name'],
  804. "Tag Number" => 36868,
  805. "Data Type" => 2,
  806. "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36868 ]['Type'],
  807. "Data" => $new_Exif_array[0][306]['Data'] );
  808. }
  809. }
  810. // Photoshop changes the EXIF date Tag (306) to the current date, not the date that was entered in File Info
  811. $exif_date = date ( "Y:m:d H:i:s" );
  812. // Update the EXIF Date and Time Tag with the new value
  813. $new_Exif_array[0][306] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 306 ]['Name'],
  814. "Tag Number" => 306,
  815. "Data Type" => 2,
  816. "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 306 ]['Type'],
  817. "Data" => array( $exif_date ) );
  818. // Photoshop replaces the EXIF Software or Firmware Tag with "Adobe Photoshop ..."
  819. // This toolkit instead preserves existing value and appends the toolkit name to the end of it
  820. // Check if the EXIF Software or Firmware Tag exists
  821. if ( array_key_exists( 305, $new_Exif_array[0] ) )
  822. {
  823. // An existing EXIF Software or Firmware Tag was found
  824. // Check if the existing Software or Firmware Tag already contains the Toolkit's name
  825. if ( stristr ( $new_Exif_array[0][305]['Data'][0], $GLOBALS[ "Software Name" ]) == FALSE )
  826. {
  827. // Toolkit Name string not found in the existing Software/Firmware string - append it.
  828. $firmware_str = $new_Exif_array[0][305]['Data'][0] . " " . $GLOBALS[ "Software Name" ];
  829. }
  830. else
  831. {
  832. // Toolkit name already exists in Software/Firmware string - don't put another copy in the string
  833. $firmware_str = $new_Exif_array[0][305]['Data'][0];
  834. }
  835. }
  836. else
  837. {
  838. // No Software/Firmware string exists - create one
  839. $firmware_str = $GLOBALS[ "Software Name" ];
  840. }
  841. // Update the EXIF Software/Firmware Tag with the new value
  842. $new_Exif_array[0][305] = array( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 305 ]['Name'],
  843. "Tag Number" => 305,
  844. "Data Type" => 2,
  845. "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 305 ]['Type'],
  846. "Data" => array( HTML_UTF8_Escape( $firmware_str ) ) );
  847. /*******************************************/
  848. // Photoshop IRB Processing
  849. // Check if there is an existing Photoshop IRB array
  850. if ($Old_IRB_array == FALSE )
  851. {
  852. // No existing IRB array - create one
  853. $new_IRB_array = array();
  854. }
  855. else
  856. {
  857. // There is an existing Photoshop IRB array - use it
  858. $new_IRB_array = $Old_IRB_array;
  859. }
  860. // Remove any existing Copyright Flag, URL, or IPTC resources - these will be re-written
  861. foreach( $new_IRB_array as $resno => $res )
  862. {
  863. if ( ( $res[ 'ResID' ] == 0x040A ) ||
  864. ( $res[ 'ResID' ] == 0x040B ) ||
  865. ( $res[ 'ResID' ] == 0x0404 ) )
  866. {
  867. array_splice( $new_IRB_array, $resno, 1 );
  868. }
  869. }
  870. // Add a new Copyright Flag resource
  871. if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Copyrighted Work" )
  872. {
  873. $PS_copyright_flag = "\x01"; // Copyrighted
  874. }
  875. else
  876. {
  877. $PS_copyright_flag = "\x00"; // Public domain or Unmarked
  878. }
  879. $new_IRB_array[] = array( 'ResID' => 0x040A,
  880. 'ResName' => $GLOBALS[ "Photoshop_ID_Names" ][0x040A],
  881. 'ResDesc' => $GLOBALS[ "Photoshop_ID_Descriptions" ][0x040A],
  882. 'ResEmbeddedName' => "",
  883. 'ResData' => $PS_copyright_flag );
  884. // Add a new URL resource
  885. $new_IRB_array[] = array( 'ResID' => 0x040B,
  886. 'ResName' => $GLOBALS[ "Photoshop_ID_Names" ][0x040B],
  887. 'ResDesc' => $GLOBALS[ "Photoshop_ID_Descriptions" ][0x040B],
  888. 'ResEmbeddedName' => "",
  889. 'ResData' => $new_ps_file_info_array[ 'ownerurl' ] );
  890. // Create IPTC resource
  891. // IPTC requires date to be in the following format YYYYMMDD
  892. $iptc_date = date( "Ymd", $date_stamp );
  893. // Create the new IPTC array
  894. $new_IPTC_array = array (
  895. 0 =>
  896. array (
  897. 'IPTC_Type' => '2:00',
  898. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:00'],
  899. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:00'],
  900. 'RecData' => "\x00\x02",
  901. ),
  902. 1 =>
  903. array (
  904. 'IPTC_Type' => '2:120',
  905. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:120'],
  906. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:120'],
  907. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'caption' ] ), 0 , 2000 ),
  908. ),
  909. 2 =>
  910. array (
  911. 'IPTC_Type' => '2:122',
  912. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:122'],
  913. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:122'],
  914. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'captionwriter' ] ), 0 , 32 ),
  915. ),
  916. 3 =>
  917. array (
  918. 'IPTC_Type' => '2:105',
  919. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:105'],
  920. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:105'],
  921. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'headline' ] ), 0 , 256 ),
  922. ),
  923. 4 =>
  924. array (
  925. 'IPTC_Type' => '2:40',
  926. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:40'],
  927. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:40'],
  928. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'instructions' ] ), 0, 256 ),
  929. ),
  930. 5 =>
  931. array (
  932. 'IPTC_Type' => '2:80',
  933. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:80'],
  934. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:80'],
  935. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'author' ] ), 0, 32 ),
  936. ),
  937. 6 =>
  938. array (
  939. 'IPTC_Type' => '2:85',
  940. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:85'],
  941. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:85'],
  942. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'authorsposition' ] ), 0, 32 ),
  943. ),
  944. 7 =>
  945. array (
  946. 'IPTC_Type' => '2:110',
  947. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:110'],
  948. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:110'],
  949. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'credit' ] ), 0, 32 ),
  950. ),
  951. 8 =>
  952. array (
  953. 'IPTC_Type' => '2:115',
  954. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:115'],
  955. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:115'],
  956. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'source' ] ), 0, 32 ),
  957. ),
  958. 9 =>
  959. array (
  960. 'IPTC_Type' => '2:05',
  961. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:05'],
  962. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:05'],
  963. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'title' ] ), 0, 64 ),
  964. ),
  965. 10 =>
  966. array (
  967. 'IPTC_Type' => '2:55',
  968. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:55'],
  969. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:55'],
  970. 'RecData' => "$iptc_date",
  971. ),
  972. 11 =>
  973. array (
  974. 'IPTC_Type' => '2:90',
  975. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:90'],
  976. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:90'],
  977. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'city' ] ), 0, 32 ),
  978. ),
  979. 12 =>
  980. array (
  981. 'IPTC_Type' => '2:95',
  982. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:95'],
  983. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:95'],
  984. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'state' ] ), 0, 32 ),
  985. ),
  986. 13 =>
  987. array (
  988. 'IPTC_Type' => '2:101',
  989. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:101'],
  990. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:101'],
  991. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'country' ] ), 0, 64 ),
  992. ),
  993. 14 =>
  994. array (
  995. 'IPTC_Type' => '2:103',
  996. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:103'],
  997. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:103'],
  998. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'transmissionreference' ] ), 0, 32 ),
  999. ),
  1000. 15 =>
  1001. array (
  1002. 'IPTC_Type' => '2:15',
  1003. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:15'],
  1004. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:15'],
  1005. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'category' ] ), 0, 3 ),
  1006. ),
  1007. 21 =>
  1008. array (
  1009. 'IPTC_Type' => '2:116',
  1010. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:10'],
  1011. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:10'],
  1012. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'copyrightnotice' ] ), 0, 128 ),
  1013. ),
  1014. );
  1015. // Check the value of urgency is valid
  1016. if ( ( $new_ps_file_info_array[ 'urgency' ] > 0 ) && ( $new_ps_file_info_array[ 'urgency' ] < 9 ) )
  1017. {
  1018. // Add the Urgency item to the IPTC array
  1019. $new_IPTC_array[] = array (
  1020. 'IPTC_Type' => '2:10',
  1021. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:10'],
  1022. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:10'],
  1023. 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'urgency' ] ), 0, 1 ),
  1024. );
  1025. }
  1026. // Cycle through the Supplemental Categories,
  1027. foreach( $new_ps_file_info_array[ 'supplementalcategories' ] as $supcat )
  1028. {
  1029. // Add this Supplemental Category to the IPTC array
  1030. $new_IPTC_array[] = array (
  1031. 'IPTC_Type' => '2:20',
  1032. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:20'],
  1033. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:20'],
  1034. 'RecData' => HTML_UTF8_Escape( $supcat ),
  1035. );
  1036. }
  1037. // Cycle through the Keywords,
  1038. foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword )
  1039. {
  1040. // Add this Keyword to the IPTC array
  1041. $new_IPTC_array[] = array (
  1042. 'IPTC_Type' => '2:25',
  1043. 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:25'],
  1044. 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:25'],
  1045. 'RecData' => $keyword,
  1046. );
  1047. }
  1048. /***********************************/
  1049. // XMP Processing
  1050. // Check if XMP existed previously
  1051. if ($Old_XMP_array == FALSE )
  1052. {
  1053. // XMP didn't exist - create a new one based on a blank structure
  1054. $new_XMP_array = XMP_Check( $GLOBALS[ 'Blank XMP Structure' ], array( ) );
  1055. }
  1056. else
  1057. {
  1058. // XMP does exist
  1059. // Some old XMP processors used x:xapmeta, check for this
  1060. if ( $Old_XMP_array[0]['tag'] == 'x:xapmeta' )
  1061. {
  1062. // x:xapmeta found - change it to x:xmpmeta
  1063. $Old_XMP_array[0]['tag'] = 'x:xmpmeta';
  1064. }
  1065. // Ensure that the existing XMP has all required fields, and add any that are missing
  1066. $new_XMP_array = XMP_Check( $GLOBALS[ 'Blank XMP Structure' ], $Old_XMP_array );
  1067. }
  1068. // Process the XMP Photoshop block
  1069. // Find the Photoshop Information within the XMP block
  1070. $photoshop_block = & find_XMP_block( $new_XMP_array, "photoshop" );
  1071. // The Photoshop CaptionWriter tag contains captionwriter - Find it and Update the value
  1072. $Item = & find_XMP_item( $photoshop_block, "photoshop:CaptionWriter" );
  1073. $Item[ 'value' ] = $new_ps_file_info_array[ 'captionwriter' ];
  1074. // The Photoshop Category tag contains category - Find it and Update the value
  1075. $Item = & find_XMP_item( $photoshop_block, "photoshop:Category" );
  1076. $Item[ 'value' ] = $new_ps_file_info_array[ 'category' ];
  1077. // The Photoshop DateCreated tag contains date - Find it and Update the value
  1078. $Item = & find_XMP_item( $photoshop_block, "photoshop:DateCreated" );
  1079. $Item[ 'value' ] = $new_ps_file_info_array[ 'date' ];
  1080. // The Photoshop City tag contains city - Find it and Update the value
  1081. $Item = & find_XMP_item( $photoshop_block, "photoshop:City" );
  1082. $Item[ 'value' ] = $new_ps_file_info_array[ 'city' ];
  1083. // The Photoshop State tag contains state - Find it and Update the value
  1084. $Item = & find_XMP_item( $photoshop_block, "photoshop:State" );
  1085. $Item[ 'value' ] = $new_ps_file_info_array[ 'state' ];
  1086. // The Photoshop Country tag contains country - Find it and Update the value
  1087. $Item = & find_XMP_item( $photoshop_block, "photoshop:Country" );
  1088. $Item[ 'value' ] = $new_ps_file_info_array[ 'country' ];
  1089. // The Photoshop AuthorsPosition tag contains authorsposition - Find it and Update the value
  1090. $Item = & find_XMP_item( $photoshop_block, "photoshop:AuthorsPosition" );
  1091. $Item[ 'value' ] = $new_ps_file_info_array[ 'authorsposition' ];
  1092. // The Photoshop Credit tag contains credit - Find it and Update the value
  1093. $Item = & find_XMP_item( $photoshop_block, "photoshop:Credit" );
  1094. $Item[ 'value' ] = $new_ps_file_info_array[ 'credit' ];
  1095. // The Photoshop Source tag contains source - Find it and Update the value
  1096. $Item = & find_XMP_item( $photoshop_block, "photoshop:Source" );
  1097. $Item[ 'value' ] = $new_ps_file_info_array[ 'source' ];
  1098. // The Photoshop Headline tag contains headline - Find it and Update the value
  1099. $Item = & find_XMP_item( $photoshop_block, "photoshop:Headline" );
  1100. $Item[ 'value' ] = $new_ps_file_info_array[ 'headline' ];
  1101. // The Photoshop Instructions tag contains instructions - Find it and Update the value
  1102. $Item = & find_XMP_item( $photoshop_block, "photoshop:Instructions" );
  1103. $Item[ 'value' ] = $new_ps_file_info_array[ 'instructions' ];
  1104. // The Photoshop TransmissionReference tag contains transmissionreference - Find it and Update the value
  1105. $Item = & find_XMP_item( $photoshop_block, "photoshop:TransmissionReference" );
  1106. $Item[ 'value' ] = $new_ps_file_info_array[ 'transmissionreference' ];
  1107. // The Photoshop Urgency tag contains urgency - Find it and Update the value
  1108. $Item = & find_XMP_item( $photoshop_block, "photoshop:Urgency" );
  1109. $Item[ 'value' ] = $new_ps_file_info_array[ 'urgency' ];
  1110. // The Photoshop SupplementalCategories tag contains supplementalcategories - Find it
  1111. $Item = & find_XMP_item( $photoshop_block, "photoshop:SupplementalCategories" );
  1112. // Create an array to receive the XML list items for the Supplemental Categories
  1113. $new_supcat_array = array( );
  1114. // Cycle through the Supplemental Categories
  1115. foreach ( $new_ps_file_info_array[ 'supplementalcategories' ] as $sup_category )
  1116. {
  1117. // Add a new list item for this Supplemental Category
  1118. $new_supcat_array[] = array( 'tag' => 'rdf:li', 'value' => $sup_category );
  1119. }
  1120. // Add the array of Supplemental Category List Items to the Photoshop SupplementalCategories tag
  1121. $Item[ 'children' ][ 0 ][ 'children' ] = $new_supcat_array;
  1122. // Process the XMP XAP block
  1123. // Find the XAP Information within the XMP block
  1124. $XAP_block = & find_XMP_block( $new_XMP_array, "xap" );
  1125. // The XAP CreateDate tag contains date XMP was first created - Find it and Update the value
  1126. $Item = & find_XMP_item( $XAP_block, "xap:CreateDate" );
  1127. // Check if the CreateDate is blank
  1128. if ( $Item[ 'value' ] == "" )
  1129. {
  1130. // CreateDate is blank - we must have just added it - set it to the current date
  1131. $Item[ 'value' ] = date( "Y-m-d\TH:i:s" );
  1132. $Item[ 'value' ] .= get_Local_Timezone_Offset( );
  1133. }
  1134. // The XAP ModifyDate tag contains last resource change date - Find it and Update the value to the current date
  1135. $Item = & find_XMP_item( $XAP_block, "xap:ModifyDate" );
  1136. $Item[ 'value' ] = date( "Y-m-d\TH:i:s" );
  1137. $Item[ 'value' ] .= get_Local_Timezone_Offset( );
  1138. // The XAP ModifyDate tag contains last XMP change date - Find it and Update the value to the current date
  1139. $Item = & find_XMP_item( $XAP_block, "xap:MetadataDate" );
  1140. $Item[ 'value' ] = date( "Y-m-d\TH:i:s" );
  1141. $Item[ 'value' ] .= get_Local_Timezone_Offset( );
  1142. // The XAP CreatorTool tag contains name of the software editor - Find it
  1143. $Item = & find_XMP_item( $XAP_block, "xap:CreatorTool" );
  1144. // Photoshop replaces the CreatorTool with "Adobe Photoshop ..."
  1145. // This toolkit instead preserves existing value and appends the toolkit name to the end of it
  1146. // Check if a CreatorTool already exists
  1147. if ( $Item[ 'value' ] != "" )
  1148. {
  1149. // An existing CreatorTool was found
  1150. // Check if the existing CreatorTool already contains the Toolkit's name
  1151. if ( stristr ( $Item[ 'value' ], $GLOBALS[ "Software Name" ]) == FALSE )
  1152. {
  1153. // Toolkit Name string not found in the existing CreatorTool string - append it.
  1154. $Item[ 'value' ] = $Item[ 'value' ] . " " . $GLOBALS[ "Software Name" ];
  1155. }
  1156. else
  1157. {
  1158. // Toolkit name already exists in CreatorTool string - leave as is
  1159. }
  1160. }
  1161. else
  1162. {
  1163. // No CreatorTool string exists - create one
  1164. $Item[ 'value' ] = $GLOBALS[ "Software Name" ];
  1165. }
  1166. // Process the XMP Basic Job Information block
  1167. // Find the XAP Basic Job Information within the XMP block
  1168. $XAPBJ_block = & find_XMP_block( $new_XMP_array, "xapBJ" );
  1169. // The XAP Basic Job JobRef tag contains urgency - Find it and Update the value
  1170. $Item = & find_XMP_item( $XAPBJ_block, "xapBJ:JobRef" );
  1171. $Item[ 'children' ][ 0 ][ 'children' ] =
  1172. array( array ( 'tag' => 'rdf:li',
  1173. 'attributes' => array ( 'rdf:parseType' => 'Resource' ),
  1174. 'children' => array ( 0 => array ( 'tag' => 'stJob:name',
  1175. 'value' => $new_ps_file_info_array[ 'jobname' ] ),
  1176. ),
  1177. ),
  1178. );
  1179. // Process the XMP XAP Rights Information block
  1180. // Find the XAP Rights Information within the XMP block
  1181. $XAPRights_block = & find_XMP_block( $new_XMP_array, "xapRights" );
  1182. // The XAP Rights Marked tag should only be present if copyrightstatus is 'Copyrighted Work' or 'Public Domain'
  1183. // If copyrightstatus 'Unmarked' or anything else, the XAP Rights Marked tag should be missing
  1184. // Remove any existing XAP Rights Marked tags - they will be replaced
  1185. foreach( $XAPRights_block as $tagno => $tag )
  1186. {
  1187. if ( $tag[ 'tag' ] == "xapRights:Marked" )
  1188. {
  1189. array_splice( $XAPRights_block, $tagno, 1 );
  1190. }
  1191. }
  1192. // Check the value of the copyrightstatus flag
  1193. if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Copyrighted Work" )
  1194. {
  1195. // Copyrighted - add the tag
  1196. $XAPRights_block[] = array ( 'tag' => 'xapRights:Marked', 'value' => 'True' );
  1197. }
  1198. else if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Public Domain" )
  1199. {
  1200. // Public domain - add the tag
  1201. $XAPRights_block[] = array ( 'tag' => 'xapRights:Marked', 'value' => 'False' );
  1202. }
  1203. else
  1204. {
  1205. // Unmarked or Other - Do nothing - don't add a Marked tag
  1206. }
  1207. // The XAP Rights WebStatement tag contains ownerurl - Find it and Update the value
  1208. $Item = & find_XMP_item( $XAPRights_block, "xapRights:WebStatement" );
  1209. $Item[ 'value' ] = $new_ps_file_info_array[ 'ownerurl' ];
  1210. // Process the XMP Dublin Core block
  1211. // Find the Dublin Core Information within the XMP block
  1212. $DC_block = & find_XMP_block( $new_XMP_array, "dc" );
  1213. // The Dublin Core description tag contains caption - Find it and Update the value
  1214. $Item = & find_XMP_item( $DC_block, "dc:description" );
  1215. $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li",
  1216. 'value' => $new_ps_file_info_array[ 'caption' ],
  1217. 'attributes' => array( 'xml:lang' => "x-default" ) ) );
  1218. // The Dublin Core title tag contains title - Find it and Update the value
  1219. $Item = & find_XMP_item( $DC_block, "dc:title" );
  1220. $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li",
  1221. 'value' => $new_ps_file_info_array[ 'title' ],
  1222. 'attributes' => array( 'xml:lang' => "x-default" ) ) );
  1223. // The Dublin Core rights tag contains copyrightnotice - Find it and Update the value
  1224. $Item = & find_XMP_item( $DC_block, "dc:rights" );
  1225. $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li",
  1226. 'value' => $new_ps_file_info_array[ 'copyrightnotice' ],
  1227. 'attributes' => array( 'xml:lang' => "x-default" ) ) );
  1228. // The Dublin Core creator tag contains author - Find it and Update the value
  1229. $Item = & find_XMP_item( $DC_block, "dc:creator" );
  1230. $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li",
  1231. 'value' => $new_ps_file_info_array[ 'author' ]) );
  1232. // The Dublin Core subject tag contains keywords - Find it
  1233. $Item = & find_XMP_item( $DC_block, "dc:subject" );
  1234. // Create an array to receive the Keywords List Items
  1235. $new_keywords_array = array( );
  1236. // Cycle through each keyword
  1237. foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword )
  1238. {
  1239. // Add a List item for this keyword
  1240. $new_keywords_array[] = array( 'tag' => "rdf:li", 'value' => $keyword );
  1241. }
  1242. // Add the Keywords List Items array to the Dublin Core subject tag
  1243. $Item[ 'children' ][ 0 ][ 'children' ] = $new_keywords_array;
  1244. /***************************************/
  1245. // FINISHED UPDATING VALUES
  1246. // Insert the new IPTC array into the Photoshop IRB array
  1247. $new_IRB_array = put_Photoshop_IPTC( $new_IRB_array, $new_IPTC_array );
  1248. // Write the EXIF array to the JPEG header
  1249. $jpeg_header_data = put_EXIF_JPEG( $new_Exif_array, $jpeg_header_data );
  1250. // Convert the XMP array to XMP text
  1251. $xmp_text = write_XMP_array_to_text( $new_XMP_array );
  1252. // Write the XMP text to the JPEG Header
  1253. $jpeg_header_data = put_XMP_text( $jpeg_header_data, $xmp_text );
  1254. // Write the Photoshop IRB array to the JPEG header
  1255. $jpeg_header_data = put_Photoshop_IRB( $jpeg_header_data, $new_IRB_array );
  1256. return $jpeg_header_data;
  1257. }
  1258. /******************************************************************************
  1259. * End of Function: put_photoshop_file_info
  1260. ******************************************************************************/
  1261. /******************************************************************************
  1262. *
  1263. * INTERNAL FUNCTIONS
  1264. *
  1265. ******************************************************************************/
  1266. /******************************************************************************
  1267. *
  1268. * Function: get_Local_Timezone_Offset
  1269. *
  1270. * Description: Returns a string indicating the time difference between the local
  1271. * timezone and GMT in hours and minutes, e.g. +10:00 or -06:30
  1272. *
  1273. * Parameters: None
  1274. *
  1275. * Returns: $tz_str - a string containing the timezone offset
  1276. *
  1277. ******************************************************************************/
  1278. function get_Local_Timezone_Offset( )
  1279. {
  1280. // Retrieve the Timezone offset in seconds
  1281. $tz_seconds = date( "Z" );
  1282. // Check if the offset is less than zero
  1283. if ( $tz_seconds < 0 )
  1284. {
  1285. // Offset is less than zero - add a Minus sign to the output
  1286. $tz_str = "-";
  1287. }
  1288. else
  1289. {
  1290. // Offset is greater than or equal to zero - add a Plus sign to the output
  1291. $tz_str = "+";
  1292. }
  1293. // Add the absolute offset to the output, formatted as HH:MM
  1294. $tz_str .= gmdate( "H:i", abs($tz_seconds) );
  1295. // Return the result
  1296. return $tz_str;
  1297. }
  1298. /******************************************************************************
  1299. * End of Function: get_Local_Timezone_Offset
  1300. ******************************************************************************/
  1301. /******************************************************************************
  1302. *
  1303. * Function: XMP_Check
  1304. *
  1305. * Description: Checks a given XMP array against a reference array, and adds any
  1306. * missing blocks and tags
  1307. *
  1308. * NOTE: This is a recursive function
  1309. *
  1310. * Parameters: reference_array - The standard XMP array which contains all required tags
  1311. * check_array - The XMP array to check
  1312. *
  1313. * Returns: output - a string containing the timezone offset
  1314. *
  1315. ******************************************************************************/
  1316. function XMP_Check( $reference_array, $check_array)
  1317. {
  1318. // Cycle through each of the elements of the reference XMP array
  1319. foreach( $reference_array as $valkey => $val )
  1320. {
  1321. // Search for the current reference tag within the XMP array to be checked
  1322. $tagpos = find_XMP_Tag( $check_array, $val );
  1323. // Check if the tag was found
  1324. if ( $tagpos === FALSE )
  1325. {
  1326. // Tag not found - Add tag to array being checked
  1327. $tagpos = count( $check_array );
  1328. $check_array[ $tagpos ] = $val;
  1329. }
  1330. // Check if the reference tag has children
  1331. if ( array_key_exists( 'children', $val ) )
  1332. {
  1333. // Reference tag has children - these need to be checked too
  1334. // Determine if the array being checked has children for this tag
  1335. if ( ! array_key_exists( 'children', $check_array[ $tagpos ] ) )
  1336. {
  1337. // Array being checked has no children - add a blank children array
  1338. $check_array[ $tagpos ][ 'children' ] = array( );
  1339. }
  1340. // Recurse, checking the children tags against the reference children
  1341. $check_array[ $tagpos ][ 'children' ] = XMP_Check( $val[ 'children' ] , $check_array[ $tagpos ][ 'children' ] );
  1342. }
  1343. else
  1344. {
  1345. // No children - don't need to check anything else
  1346. }
  1347. }
  1348. // Return the checked XMP array
  1349. return $check_array;
  1350. }
  1351. /******************************************************************************
  1352. * End of Function: XMP_Check
  1353. ******************************************************************************/
  1354. /******************************************************************************
  1355. *
  1356. * Function: find_XMP_Tag
  1357. *
  1358. * Description: Searches one level of an XMP array for a specific tag, and
  1359. * returns the tag position. Does not descend the XMP tree.
  1360. *
  1361. * Parameters: XMP_array - The XMP array which should be searched
  1362. * tag - The XMP tag to search for (in same format as would be found in XMP array)
  1363. *
  1364. * Returns: output - a string containing the timezone offset
  1365. *
  1366. ******************************************************************************/
  1367. function find_XMP_Tag( $XMP_array, $tag )
  1368. {
  1369. $namespacestr = "";
  1370. // Some tags have a namespace attribute which defines them (i.e. rdf:Description tags)
  1371. // Check if the tag being searched for has attributs
  1372. if ( array_key_exists( 'attributes', $tag ) )
  1373. {
  1374. // Tag has attributes - cycle through them
  1375. foreach( $tag['attributes'] as $key => $val )
  1376. {
  1377. // Check if the current attribute is the namespace attribute - i.e. starts with xmlns:
  1378. if ( strcasecmp( substr($key,0,6), "xmlns:" ) == 0 )
  1379. {
  1380. // Found a namespace attribute - save it for later.
  1381. $namespacestr = $key;
  1382. }
  1383. }
  1384. }
  1385. // Cycle through the elements of the XMP array to be searched.
  1386. foreach( $XMP_array as $valkey => $val )
  1387. {
  1388. // Check if the current element is a rdf:Description tag
  1389. if ( strcasecmp ( $tag[ 'tag' ], 'rdf:Description' ) == 0 )
  1390. {
  1391. // Current element is a rdf:Description tag
  1392. // Check if the namespace attribute is the same as in the tag that is being searched for
  1393. if ( array_key_exists( $namespacestr, $val['attributes'] ) )
  1394. {
  1395. // Namespace is the same - this is the correct tag - return it's position
  1396. return $valkey;
  1397. }
  1398. }
  1399. // Otherwise check if the current element has the same name as the tag in question
  1400. else if ( strcasecmp ( $val[ 'tag' ], $tag[ 'tag' ] ) == 0 )
  1401. {
  1402. // Tags have same name - this is the correct tag - return it's position
  1403. return $valkey;
  1404. }
  1405. }
  1406. // Cycled through all tags without finding the correct one - return error value
  1407. return FALSE;
  1408. }
  1409. /******************************************************************************
  1410. * End of Function: find_XMP_Tag
  1411. ******************************************************************************/
  1412. /******************************************************************************
  1413. *
  1414. * Function: create_GUID
  1415. *
  1416. * Description: Creates a Globally Unique IDentifier, in the format that is used
  1417. * by XMP (and Windows). This value is not guaranteed to be 100% unique,
  1418. * but it is ridiculously unlikely that two identical values will be produced
  1419. *
  1420. * Parameters: none
  1421. *
  1422. * Returns: output - a string containing the timezone offset
  1423. *
  1424. ******************************************************************************/
  1425. function create_GUID( )
  1426. {
  1427. // Create a md5 sum of a random number - this is a 32 character hex string
  1428. $raw_GUID = md5( uniqid( getmypid() . rand( ) . (double)microtime()*1000000, TRUE ) );
  1429. // Format the string into 8-4-4-4-12 (numbers are the number of characters in each block)
  1430. return substr($raw_GUID,0,8) . "-" . substr($raw_GUID,8,4) . "-" . substr($raw_GUID,12,4) . "-" . substr($raw_GUID,16,4) . "-" . substr($raw_GUID,20,12);
  1431. }
  1432. /******************************************************************************
  1433. * End of Function: create_GUID
  1434. ******************************************************************************/
  1435. /******************************************************************************
  1436. *
  1437. * Function: add_to_field
  1438. *
  1439. * Description: Adds a value to a particular field in a Photoshop File Info array,
  1440. * first checking whether the value is already there. If the value is
  1441. * already in the array, it is not changed, otherwise the value is appended
  1442. * to whatever is already in that field of the array
  1443. *
  1444. * Parameters: field_array - The Photoshop File Info array to receive the new value
  1445. * field - The File Info field which the value is for
  1446. * value - The value to be written into the File Info
  1447. * separator - The string to place between values when having to append the value
  1448. *
  1449. * Returns: output - the Photoshop File Info array with the value added
  1450. *
  1451. ******************************************************************************/
  1452. function add_to_field( $field_array, $field, $value, $separator )
  1453. {
  1454. // Check if the value is blank
  1455. if ( $value == "" )
  1456. {
  1457. // Value is blank - return File Info array unchanged
  1458. return $field_array;
  1459. }
  1460. // Check if the value can be found anywhere within the existing value for this field
  1461. if ( stristr ( $field_array[ $field ], $value ) == FALSE)
  1462. {
  1463. // Value could not be found
  1464. // Check if the existing value for the field is blank
  1465. if ( $field_array[$field] != "" )
  1466. {
  1467. // Existing value for field is not blank - append a separator
  1468. $field_array[$field] .= $separator;
  1469. }
  1470. // Append the value to the field
  1471. $field_array[$field] .= $value;
  1472. }
  1473. // Return the File Info Array
  1474. return $field_array;
  1475. }
  1476. /******************************************************************************
  1477. * End of Function: add_to_field
  1478. ******************************************************************************/
  1479. /******************************************************************************
  1480. *
  1481. * Function: find_IPTC_Resource
  1482. *
  1483. * Description: Searches an IPTC array for a particular record, and returns it if found
  1484. *
  1485. * Parameters: IPTC_array - The IPTC array to search
  1486. * record_type - The IPTC record number to search for (e.g. 2:151 )
  1487. *
  1488. * Returns: output - the contents of the record if found
  1489. * FALSE - otherwise
  1490. *
  1491. ******************************************************************************/
  1492. function find_IPTC_Resource( $IPTC_array, $record_type )
  1493. {
  1494. // Cycle through the ITPC records
  1495. foreach ($IPTC_array as $record)
  1496. {
  1497. // Check the IPTC type against the required type
  1498. if ( $record['IPTC_Type'] == $record_type )
  1499. {
  1500. // IPTC type matches - return this record
  1501. return $record;
  1502. }
  1503. }
  1504. // No matching record found - return error code
  1505. return FALSE;
  1506. }
  1507. /******************************************************************************
  1508. * End of Function: find_IPTC_Resource
  1509. ******************************************************************************/
  1510. /******************************************************************************
  1511. *
  1512. * Function: find_Photoshop_IRB_Resource
  1513. *
  1514. * Description: Searches a Photoshop IRB array for a particular resource, and returns it if found
  1515. *
  1516. * Parameters: IRB_array - The IRB array to search
  1517. * resource_ID - The IRB resource number to search for (e.g. 0x03F9 )
  1518. *
  1519. * Returns: output - the contents of the resource if found
  1520. * FALSE - otherwise
  1521. *
  1522. ******************************************************************************/
  1523. function find_Photoshop_IRB_Resource( $IRB_array, $resource_ID )
  1524. {
  1525. // Cycle through the IRB resources
  1526. foreach( $IRB_array as $IRB_Resource )
  1527. {
  1528. // Check the IRB resource ID against the required ID
  1529. if ( $resource_ID == $IRB_Resource['ResID'] )
  1530. {
  1531. // Resource ID matches - return this resource
  1532. return $IRB_Resource;
  1533. }
  1534. }
  1535. // No matching resource found - return error code
  1536. return FALSE;
  1537. }
  1538. /******************************************************************************
  1539. * End of Function: find_Photoshop_IRB_Resource
  1540. ******************************************************************************/
  1541. /******************************************************************************
  1542. *
  1543. * Function: find_XMP_item
  1544. *
  1545. * Description: Searches a one level of a XMP array for a particular item by name, and returns it if found.
  1546. * Does not descend through the XMP array
  1547. *
  1548. * Parameters: Item_Array - The XMP array to search
  1549. * item_name - The name of the tag to serch for (e.g. photoshop:CaptionWriter )
  1550. *
  1551. * Returns: output - the contents of the tag if found
  1552. * FALSE - otherwise
  1553. *
  1554. ******************************************************************************/
  1555. function & find_XMP_item( & $Item_Array, $item_name )
  1556. {
  1557. // Cycle through the top level of the XMP array
  1558. foreach( $Item_Array as $Item_Key => $Item )
  1559. {
  1560. // Check this tag name against the required tag name
  1561. if( $Item['tag'] == $item_name )
  1562. {
  1563. // The tag names match - return the item
  1564. return $Item_Array[ $Item_Key ];
  1565. }
  1566. }
  1567. // No matching tag found - return error code
  1568. return FALSE;
  1569. }
  1570. /******************************************************************************
  1571. * End of Function: find_XMP_item
  1572. ******************************************************************************/
  1573. /******************************************************************************
  1574. *
  1575. * Function: find_XMP_block
  1576. *
  1577. * Description: Searches a for a particular rdf:Description block within a XMP array, and returns its children if found.
  1578. *
  1579. * Parameters: XMP_array - The XMP array to search as returned by read_XMP_array_from_text
  1580. * block_name - The namespace of the XMP block to be found (e.g. photoshop or xapRights )
  1581. *
  1582. * Returns: output - the children of the tag if found
  1583. * FALSE - otherwise
  1584. *
  1585. ******************************************************************************/
  1586. function & find_XMP_block( & $XMP_array, $block_name )
  1587. {
  1588. // Check that the rdf:RDF section can be found (which contains the rdf:Description tags
  1589. if ( ( $XMP_array !== FALSE ) &&
  1590. ( ( $XMP_array[0]['tag'] == "x:xapmeta" ) ||
  1591. ( $XMP_array[0]['tag'] == "x:xmpmeta" ) ) &&
  1592. ( $XMP_array[0]['children'][0]['tag'] == "rdf:RDF" ) )
  1593. {
  1594. // Found rdf:RDF
  1595. // Make it's children easily accessible
  1596. $RDF_Contents = $XMP_array[0]['children'][0]['children'];
  1597. // Cycle through the children (rdf:Description tags)
  1598. foreach ($RDF_Contents as $RDF_Key => $RDF_Item)
  1599. {
  1600. // Check if this is a rdf:description tag that has children
  1601. if ( ( $RDF_Item['tag'] == "rdf:Description" ) &&
  1602. ( array_key_exists( 'children', $RDF_Item ) ) )
  1603. {
  1604. // RDF Description tag has children,
  1605. // Cycle through it's attributes
  1606. foreach( $RDF_Item['attributes'] as $key => $val )
  1607. {
  1608. // Check if this attribute matches the namespace block name required
  1609. if ( $key == "xmlns:$block_name" )
  1610. {
  1611. // Namespace matches required block name - return it's children
  1612. return $XMP_array[0]['children'][0]['children'][ $RDF_Key ]['children'];
  1613. }
  1614. }
  1615. }
  1616. }
  1617. }
  1618. // No matching rdf:Description block found
  1619. return FALSE;
  1620. }
  1621. /******************************************************************************
  1622. * End of Function: find_XMP_block
  1623. ******************************************************************************/
  1624. /******************************************************************************
  1625. * Global Variable: Blank XMP Structure
  1626. *
  1627. * Contents: A template XMP array which can be used to create a new XMP segment
  1628. *
  1629. ******************************************************************************/
  1630. // Create a GUID to be used in this template array
  1631. $new_GUID = create_GUID( );
  1632. $GLOBALS[ 'Blank XMP Structure' ] =
  1633. array (
  1634. 0 =>
  1635. array (
  1636. 'tag' => 'x:xmpmeta',
  1637. 'attributes' =>
  1638. array (
  1639. 'xmlns:x' => 'adobe:ns:meta/',
  1640. 'x:xmptk' => 'XMP toolkit 3.0-28, framework 1.6',
  1641. ),
  1642. 'children' =>
  1643. array (
  1644. 0 =>
  1645. array (
  1646. 'tag' => 'rdf:RDF',
  1647. 'attributes' =>
  1648. array (
  1649. 'xmlns:rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
  1650. 'xmlns:iX' => 'http://ns.adobe.com/iX/1.0/',
  1651. ),
  1652. 'children' =>
  1653. array (
  1654. 1 =>
  1655. array (
  1656. 'tag' => 'rdf:Description',
  1657. 'attributes' =>
  1658. array (
  1659. 'rdf:about' => "uuid:$new_GUID",
  1660. 'xmlns:pdf' => 'http://ns.adobe.com/pdf/1.3/',
  1661. ),
  1662. ),
  1663. 2 =>
  1664. array (
  1665. 'tag' => 'rdf:Description',
  1666. 'attributes' =>
  1667. array (
  1668. 'rdf:about' => "uuid:$new_GUID",
  1669. 'xmlns:photoshop' => 'http://ns.adobe.com/photoshop/1.0/',
  1670. ),
  1671. 'children' =>
  1672. array (
  1673. 0 =>
  1674. array (
  1675. 'tag' => 'photoshop:CaptionWriter',
  1676. 'value' => '',
  1677. ),
  1678. 1 =>
  1679. array (
  1680. 'tag' => 'photoshop:Category',
  1681. 'value' => '',
  1682. ),
  1683. 2 =>
  1684. array (
  1685. 'tag' => 'photoshop:DateCreated',
  1686. 'value' => '',
  1687. ),
  1688. 3 =>
  1689. array (
  1690. 'tag' => 'photoshop:City',
  1691. 'value' => '',
  1692. ),
  1693. 4 =>
  1694. array (
  1695. 'tag' => 'photoshop:State',
  1696. 'value' => '',
  1697. ),
  1698. 5 =>
  1699. array (
  1700. 'tag' => 'photoshop:Country',
  1701. 'value' => '',
  1702. ),
  1703. 6 =>
  1704. array (
  1705. 'tag' => 'photoshop:Credit',
  1706. 'value' => '',
  1707. ),
  1708. 7 =>
  1709. array (
  1710. 'tag' => 'photoshop:Source',
  1711. 'value' => '',
  1712. ),
  1713. 8 =>
  1714. array (
  1715. 'tag' => 'photoshop:Headline',
  1716. 'value' => '',
  1717. ),
  1718. 9 =>
  1719. array (
  1720. 'tag' => 'photoshop:Instructions',
  1721. 'value' => '',
  1722. ),
  1723. 10 =>
  1724. array (
  1725. 'tag' => 'photoshop:TransmissionReference',
  1726. 'value' => '',
  1727. ),
  1728. 11 =>
  1729. array (
  1730. 'tag' => 'photoshop:Urgency',
  1731. 'value' => '',
  1732. ),
  1733. 12 =>
  1734. array (
  1735. 'tag' => 'photoshop:SupplementalCategories',
  1736. 'children' =>
  1737. array (
  1738. 0 =>
  1739. array (
  1740. 'tag' => 'rdf:Bag',
  1741. ),
  1742. ),
  1743. ),
  1744. 13 =>
  1745. array (
  1746. 'tag' => 'photoshop:AuthorsPosition',
  1747. 'value' => '',
  1748. ),
  1749. ),
  1750. ),
  1751. 4 =>
  1752. array (
  1753. 'tag' => 'rdf:Description',
  1754. 'attributes' =>
  1755. array (
  1756. 'rdf:about' => "uuid:$new_GUID",
  1757. 'xmlns:xap' => 'http://ns.adobe.com/xap/1.0/',
  1758. ),
  1759. 'children' =>
  1760. array (
  1761. 0 =>
  1762. array (
  1763. 'tag' => 'xap:CreateDate',
  1764. 'value' => '',
  1765. ),
  1766. 1 =>
  1767. array (
  1768. 'tag' => 'xap:ModifyDate',
  1769. 'value' => '',
  1770. ),
  1771. 2 =>
  1772. array (
  1773. 'tag' => 'xap:MetadataDate',
  1774. 'value' => '',
  1775. ),
  1776. 3 =>
  1777. array (
  1778. 'tag' => 'xap:CreatorTool',
  1779. 'value' => '',
  1780. ),
  1781. ),
  1782. ),
  1783. 5 =>
  1784. array (
  1785. 'tag' => 'rdf:Description',
  1786. 'attributes' =>
  1787. array (
  1788. 'about' => "uuid:$new_GUID",
  1789. 'xmlns:stJob' => 'http://ns.adobe.com/xap/1.0/sType/Job#',
  1790. 'xmlns:xapBJ' => 'http://ns.adobe.com/xap/1.0/bj/',
  1791. ),
  1792. 'children' =>
  1793. array (
  1794. 0 =>
  1795. array (
  1796. 'tag' => 'xapBJ:JobRef',
  1797. 'children' =>
  1798. array (
  1799. 0 =>
  1800. array (
  1801. 'tag' => 'rdf:Bag',
  1802. 'children' =>
  1803. array (
  1804. ),
  1805. ),
  1806. ),
  1807. ),
  1808. ),
  1809. ),
  1810. 6 =>
  1811. array (
  1812. 'tag' => 'rdf:Description',
  1813. 'attributes' =>
  1814. array (
  1815. 'rdf:about' => "uuid:$new_GUID",
  1816. 'xmlns:xapRights' => 'http://ns.adobe.com/xap/1.0/rights/',
  1817. ),
  1818. 'children' =>
  1819. array (
  1820. 1 =>
  1821. array (
  1822. 'tag' => 'xapRights:WebStatement',
  1823. 'value' => '',
  1824. ),
  1825. ),
  1826. ),
  1827. 7 =>
  1828. array (
  1829. 'tag' => 'rdf:Description',
  1830. 'attributes' =>
  1831. array (
  1832. 'rdf:about' => "uuid:$new_GUID",
  1833. 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
  1834. ),
  1835. 'children' =>
  1836. array (
  1837. 0 =>
  1838. array (
  1839. 'tag' => 'dc:format',
  1840. 'value' => 'image/jpeg',
  1841. ),
  1842. 1 =>
  1843. array (
  1844. 'tag' => 'dc:title',
  1845. 'children' =>
  1846. array (
  1847. 0 =>
  1848. array (
  1849. 'tag' => 'rdf:Alt',
  1850. ),
  1851. ),
  1852. ),
  1853. 2 =>
  1854. array (
  1855. 'tag' => 'dc:description',
  1856. 'children' =>
  1857. array (
  1858. 0 =>
  1859. array (
  1860. 'tag' => 'rdf:Alt',
  1861. ),
  1862. ),
  1863. ),
  1864. 3 =>
  1865. array (
  1866. 'tag' => 'dc:rights',
  1867. 'children' =>
  1868. array (
  1869. 0 =>
  1870. array (
  1871. 'tag' => 'rdf:Alt',
  1872. ),
  1873. ),
  1874. ),
  1875. 4 =>
  1876. array (
  1877. 'tag' => 'dc:creator',
  1878. 'children' =>
  1879. array (
  1880. 0 =>
  1881. array (
  1882. 'tag' => 'rdf:Seq',
  1883. ),
  1884. ),
  1885. ),
  1886. 5 =>
  1887. array (
  1888. 'tag' => 'dc:subject',
  1889. 'children' =>
  1890. array (
  1891. 0 =>
  1892. array (
  1893. 'tag' => 'rdf:Bag',
  1894. ),
  1895. ),
  1896. ),
  1897. ),
  1898. ),
  1899. /* 0 =>
  1900. array (
  1901. 'tag' => 'rdf:Description',
  1902. 'attributes' =>
  1903. array (
  1904. 'rdf:about' => "uuid:$new_GUID",
  1905. 'xmlns:exif' => 'http://ns.adobe.com/exif/1.0/',
  1906. ),
  1907. 'children' =>
  1908. array (
  1909. //EXIF DATA GOES HERE - Not Implemented yet
  1910. ),
  1911. ),
  1912. */
  1913. /*
  1914. 2 =>
  1915. array (
  1916. 'tag' => 'rdf:Description',
  1917. 'attributes' =>
  1918. array (
  1919. 'rdf:about' => "uuid:$new_GUID",
  1920. 'xmlns:tiff' => 'http://ns.adobe.com/tiff/1.0/',
  1921. ),
  1922. 'children' =>
  1923. array (
  1924. // TIFF DATA GOES HERE - Not Implemented yet
  1925. 0 =>
  1926. array (
  1927. 'tag' => 'tiff:Make',
  1928. 'value' => 'NIKON CORPORATION',
  1929. ),
  1930. ),
  1931. ),
  1932. */
  1933. /*
  1934. 3 =>
  1935. array (
  1936. 'tag' => 'rdf:Description',
  1937. 'attributes' =>
  1938. array (
  1939. 'rdf:about' => "uuid:$new_GUID",
  1940. 'xmlns:stRef' => 'http://ns.adobe.com/xap/1.0/sType/ResourceRef#',
  1941. 'xmlns:xapMM' => 'http://ns.adobe.com/xap/1.0/mm/',
  1942. ),
  1943. 'children' =>
  1944. array (
  1945. // XAPMM DATA GOES HERE - Not Implemented yet
  1946. 0 =>
  1947. array (
  1948. 'tag' => 'xapMM:DocumentID',
  1949. 'value' => 'adobe:docid:photoshop:dceba4c2-e699-11d8-94b2-b6ec48319f2d',
  1950. ),
  1951. 1 =>
  1952. array (
  1953. 'tag' => 'xapMM:DerivedFrom',
  1954. 'attributes' =>
  1955. array (
  1956. 'rdf:parseType' => 'Resource',
  1957. ),
  1958. 'children' =>
  1959. array (
  1960. 0 =>
  1961. array (
  1962. 'tag' => 'stRef:documentID',
  1963. 'value' => 'adobe:docid:photoshop:5144475b-e698-11d8-94b2-b6ec48319f2d',
  1964. ),
  1965. 1 =>
  1966. array (
  1967. 'tag' => 'stRef:instanceID',
  1968. 'value' => "uuid:$new_GUID",
  1969. ),
  1970. ),
  1971. ),
  1972. ),
  1973. ),
  1974. */
  1975. ),
  1976. ),
  1977. ),
  1978. ),
  1979. );
  1980. /******************************************************************************
  1981. * End of Global Variable: Blank XMP Structure
  1982. ******************************************************************************/
  1983. ?>