1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php * * URL: http://electronics.ozhiker.com * * License: This file is part of the PHP JPEG Metadata Toolkit. * * The PHP JPEG Metadata Toolkit is free software; you can * redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your * option) any later version. * * The PHP JPEG Metadata Toolkit is distributed in the hope * that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public * License along with the PHP JPEG Metadata Toolkit; if not, * write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA * * If you require a different license for commercial or other * purposes, please contact the author: evan@ozhiker.com * ******************************************************************************/ // TODO: XMP sections: XAPMM, TIFF, EXIF require_once 'Toolkit_Version.php'; // Change: added as of version 1.11 /****************************************************************************** * Global Variable: Software Name * * Contents: The string that is appended to fields which store the name of * the software editor. * ******************************************************************************/ $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 /****************************************************************************** * End of Global Variable: Software Name ******************************************************************************/ /****************************************************************************** * * Function: get_photoshop_file_info * * Description: Retrieves Photoshop 'File Info' metadata in the same way that Photoshop * does. The results are returned in an array as below: * * $file_info_array = array( * "title" => "", * "author" => "", * "authorsposition" => "", // Note: Not used in Photoshop 7 or higher * "caption" => "", * "captionwriter" => "", * "jobname" => "", // Note: Not used in Photoshop CS * "copyrightstatus" => "", * "copyrightnotice" => "", * "ownerurl" => "", * "keywords" => array( 0 => "", 1 => "", ... ), * "category" => "", // Note: Max 3 characters * "supplementalcategories" => array( 0 => "", 1 => "", ... ), * "date" => "", // Note: DATE MUST BE IN YYYY-MM-DD format * "city" => "", * "state" => "", * "country" => "", * "credit" => "", * "source" => "", * "headline" => "", * "instructions" => "", * "transmissionreference" => "", * "urgency" => "" ); * * Parameters: Exif_array - an array containing the EXIF information to be * searched, as retrieved by get_EXIF_JPEG. (saves having to parse the EXIF again) * XMP_array - an array containing the XMP information to be * searched, as retrieved by read_XMP_array_from_text. (saves having to parse the XMP again) * IRB_array - an array containing the Photoshop IRB information * to be searched, as retrieved by get_Photoshop_IRB. (saves having to parse the IRB again) * * Returns: outputarray - an array as above, containing the Photoshop File Info data * ******************************************************************************/ function get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array ) { // Create a blank array to receive the output $outputarray = array( "title" => "", "author" => "", "authorsposition" => "", "caption" => "", "captionwriter" => "", "jobname" => "", "copyrightstatus" => "", "copyrightnotice" => "", "ownerurl" => "", "keywords" => array(), "category" => "", "supplementalcategories" => array(), "date" => "", "city" => "", "state" => "", "country" => "", "credit" => "", "source" => "", "headline" => "", "instructions" => "", "transmissionreference" => "", "urgency" => "" ); /***************************************/ // XMP Processing // Retrieve the dublin core section from the XMP header // Extract the Dublin Core section from the XMP $dublincore_block = find_XMP_block( $XMP_array, "dc" ); // Check that the Dublin Core section exists if ( $dublincore_block != FALSE ) { // Dublin Core Description Field contains caption // Extract Description $Item = find_XMP_item( $dublincore_block, "dc:description" ); // Check if Description Tag existed if ( $Item != FALSE ) { // Ensure that the Description value exists and save it. if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] == "rdf:Alt" ) && ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) ) { $outputarray = add_to_field( $outputarray, 'caption' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "\n" ); } } /***************************************/ // Dublin Core Creator Field contains author // Extract Description $Item = find_XMP_item( $dublincore_block, "dc:creator" ); // Check if Creator Tag existed if ( $Item != FALSE ) { // Ensure that the Creator value exists and save it. if ( ( !empty( $Item['children'][0] ) ) && ( $Item['children'][0]['tag'] =="rdf:Seq" ) && ( !empty( $Item['children'][0]['children'][0]['empty'] ) ) ) { $outputarray = add_to_field( $outputarray, 'author' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "\n" ); } } /***************************************/ // Dublin Core Title Field contains title // Extract Title $Item = find_XMP_item( $dublincore_block, "dc:title" ); // Check if Title Tag existed if ( $Item != FALSE ) { // Ensure that the Title value exists and save it. if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Alt" ) && ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) ) { $outputarray = add_to_field( $outputarray, 'title' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "," ); } } /***************************************/ // Dublin Core Rights Field contains copyrightnotice // Extract Rights $Item = find_XMP_item( $dublincore_block, "dc:rights" ); // Check if Rights Tag existed if ( $Item != FALSE ) { // Ensure that the Rights value exists and save it. if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Alt" ) && ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) ) { $outputarray = add_to_field( $outputarray, 'copyrightnotice' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "," ); } } /***************************************/ // Dublin Core Subject Field contains keywords // Extract Subject $Item = find_XMP_item( $dublincore_block, "dc:subject" ); // Check if Subject Tag existed if ( $Item != FALSE ) { // Ensure that the Subject values exist if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) ) { // Cycle through each Subject value and save them foreach ( $Item['children'][0]['children'] as $keywords ) { if ( ! in_array ( HTML_UTF8_Escape( $keywords['value'] ), $outputarray['keywords'])) { if ( array_key_exists( 'value', $keywords ) ) { $outputarray['keywords'][] = HTML_UTF8_Escape( $keywords['value'] ); } } } } } } /***************************************/ // Find the Photoshop Information within the XMP block $photoshop_block = find_XMP_block( $XMP_array, "photoshop" ); // Check that the Photoshop Information exists if ( $photoshop_block != FALSE ) { // The Photoshop CaptionWriter tag contains captionwriter - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:CaptionWriter" ); // Check that the CaptionWriter Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'captionwriter' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop Headline tag contains headline - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:Headline" ); // Check that the Headline Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'headline' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop Instructions tag contains instructions - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:Instructions" ); // Check that the Instructions Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'instructions' , HTML_UTF8_Escape( $Item['value'] ), "\n" ); } /***************************************/ // The Photoshop AuthorsPosition tag contains authorsposition - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:AuthorsPosition" ); // Check that the AuthorsPosition Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'authorsposition' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop Credit tag contains credit - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:Credit" ); // Check that the Credit Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'credit' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop Source tag contains source - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:Source" ); // Check that the Credit Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'source' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop City tag contains city - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:City" ); // Check that the City Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'city' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop State tag contains state - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:State" ); // Check that the State Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'state' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop Country tag contains country - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:Country" ); // Check that the Country Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'country' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop TransmissionReference tag contains transmissionreference - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:TransmissionReference" ); // Check that the TransmissionReference Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'transmissionreference' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop Category tag contains category - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:Category" ); // Check that the TransmissionReference Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'category' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop DateCreated tag contains date - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:DateCreated" ); // Check that the DateCreated Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'date' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop Urgency tag contains urgency - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:Urgency" ); // Check that the Urgency Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'urgency' , HTML_UTF8_Escape( $Item['value'] ), "," ); } /***************************************/ // The Photoshop SupplementalCategories tag contains supplementalcategories - Find it $Item = find_XMP_item( $photoshop_block, "photoshop:SupplementalCategories" ); // Check that the SupplementalCategories Field exists if ( $Item != FALSE ) { // Check that the values exist if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) ) { // Cycle through the values and save them foreach ( $Item['children'][0]['children'] as $sup_category ) { if ( ( array_key_exists( 'value', $sup_category ) ) && ( ! in_array ( HTML_UTF8_Escape( $sup_category['value'] ), $outputarray['supplementalcategories'])) ) { if ( array_key_exists( 'value', $sup_category ) ) { $outputarray['supplementalcategories'][] = HTML_UTF8_Escape( $sup_category['value'] ); } } } } } } /***************************************/ // Find the Job Reference Information within the XMP block $job_block = find_XMP_block( $XMP_array, "xapBJ" ); // Check that the Job Reference Information exists if ( $job_block != FALSE ) { // The JobRef Field contains jobname - Find it $Item = find_XMP_item( $job_block, "xapBJ:JobRef" ); // Check that the JobRef Field exists if ( $Item != FALSE ) { // Check that the value exists and save it if ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) && ( array_key_exists( 'children', $Item['children'][0] ) ) && ( $Item['children'][0]['children'][0]['tag'] =="rdf:li" ) && ( array_key_exists( 'children', $Item['children'][0]['children'][0] ) ) && ( $Item['children'][0]['children'][0]['children'][0]['tag'] =="stJob:name" ) && ( array_key_exists( 'value', $Item['children'][0]['children'][0]['children'][0] ) ) ) { $outputarray = add_to_field( $outputarray, 'jobname' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['children'][0]['value'] ), "," ); } } } /***************************************/ // Find the Rights Information within the XMP block $rights_block = find_XMP_block( $XMP_array, "xapRights" ); // Check that the Rights Information exists if ( $rights_block != FALSE ) { // The WebStatement Field contains ownerurl - Find it $Item = find_XMP_item( $rights_block, "xapRights:WebStatement" ); // Check that the WebStatement Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { $outputarray = add_to_field( $outputarray, 'ownerurl' , HTML_UTF8_Escape( $Item['value'] ), "\n" ); } /***************************************/ // The Marked Field contains copyrightstatus - Find it $Item = find_XMP_item( $rights_block, "xapRights:Marked" ); // Check that the Marked Field exists and save the value if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) ) { if ( $Item['value'] == "True" ) { $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Copyrighted Work", "," ); } else { $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Public Domain", "," ); } } } /***************************************/ // Photoshop IRB Processing // Check that the Photoshop IRB exists if ( $IRB_array != FALSE ) { // Create a translation table to convert carriage returns to linefeeds $irbtrans = array("\x0d" => "\x0a"); // The Photoshop IRB Copyright flag (0x040A) contains copyrightstatus - find it $IRB_copyright_flag = find_Photoshop_IRB_Resource( $IRB_array, 0x040A ); // Check if the Copyright flag Field exists, and save the value if( $IRB_copyright_flag != FALSE ) { // Check the value of the copyright flag if ( hexdec( bin2hex( $IRB_copyright_flag['ResData'] ) ) == 1 ) { // Save the value $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Copyrighted Work", "," ); } else { // Do nothing - copyrightstatus will be set to unmarked if still blank at end } } /***************************************/ // The Photoshop IRB URL (0x040B) contains ownerurl - find it $IRB_url = find_Photoshop_IRB_Resource( $IRB_array, 0x040B ); // Check if the URL Field exists and save the value if( $IRB_url != FALSE ) { $outputarray = add_to_field( $outputarray, 'ownerurl' , strtr( $IRB_url['ResData'], $irbtrans ), "\n" ); } /***************************************/ // Extract any IPTC block from the Photoshop IRB information $IPTC_array = get_Photoshop_IPTC( $IRB_array ); // Check if the IPTC block exits if ( ( $IPTC_array != FALSE ) && ( count( $IPTC_array ) != 0 ) ) { // The IPTC Caption/Abstract Field contains caption - find it $record = find_IPTC_Resource( $IPTC_array, "2:120" ); // Check if the Caption/Abstract Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'caption' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Caption Writer/Editor Field contains captionwriter - find it $record = find_IPTC_Resource( $IPTC_array, "2:122" ); // Check if the Caption Writer/Editor Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'captionwriter' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Headline Field contains headline - find it $record = find_IPTC_Resource( $IPTC_array, "2:105" ); // Check if the Headline Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'headline' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Special Instructions Field contains instructions - find it $record = find_IPTC_Resource( $IPTC_array, "2:40" ); // Check if the Special Instructions Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'instructions' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC By-Line Field contains author - find it $record = find_IPTC_Resource( $IPTC_array, "2:80" ); // Check if the By-Line Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'author' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC By-Line Title Field contains authorsposition - find it $record = find_IPTC_Resource( $IPTC_array, "2:85" ); // Check if the By-Line Title Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'authorsposition' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Credit Field contains credit - find it $record = find_IPTC_Resource( $IPTC_array, "2:110" ); // Check if the Credit Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'credit' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Source Field contains source - find it $record = find_IPTC_Resource( $IPTC_array, "2:115" ); // Check if the Source Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'source' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Object Name Field contains title - find it $record = find_IPTC_Resource( $IPTC_array, "2:05" ); // Check if the Object Name Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'title' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Date Created Field contains date - find it $record = find_IPTC_Resource( $IPTC_array, "2:55" ); // Check if the Date Created Field exists and save the value if ( $record != FALSE ) { $date_array = unpack( "a4Year/a2Month/A2Day", $record['RecData'] ); $tmpdate = $date_array['Year'] . "-" . $date_array['Month'] . "-" . $date_array['Day']; $outputarray = add_to_field( $outputarray, 'date' , strtr( $tmpdate, $irbtrans ), "," ); } /***************************************/ // The IPTC City Field contains city - find it $record = find_IPTC_Resource( $IPTC_array, "2:90" ); // Check if the City Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'city' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Province/State Field contains state - find it $record = find_IPTC_Resource( $IPTC_array, "2:95" ); // Check if the Province/State Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'state' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Country/Primary Location Name Field contains country - find it $record = find_IPTC_Resource( $IPTC_array, "2:101" ); // Check if the Country/Primary Location Name Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'country' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Original Transmission Reference Field contains transmissionreference - find it $record = find_IPTC_Resource( $IPTC_array, "2:103" ); // Check if the Original Transmission Reference Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'transmissionreference' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // The IPTC Category Field contains category - find it $record = find_IPTC_Resource( $IPTC_array, "2:15" ); // Check if the Category Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'category' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // Cycle through the IPTC records looking for Supplemental Category records foreach ($IPTC_array as $record) { // Check if a Supplemental Category record has been found if ( $record['IPTC_Type'] == "2:20" ) { // A Supplemental Category record has been found, save it's value if the value doesn't already exist if ( ! in_array ( $record['RecData'], $outputarray['supplementalcategories'])) { $outputarray['supplementalcategories'][] = strtr( $record['RecData'], array("\x0a" => "", "\x0d" => " ") ) ; } } } /***************************************/ // The IPTC Urgency Field contains urgency - find it $record = find_IPTC_Resource( $IPTC_array, "2:10" ); // Check if the Urgency Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'urgency' , strtr( $record['RecData'], $irbtrans ), "\n" ); } /***************************************/ // Cycle through the IPTC records looking for Keywords records foreach ($IPTC_array as $record) { // Check if a Keywords record has been found if ( $record['IPTC_Type'] == "2:25" ) { // A Keywords record has been found, save it's value if the value doesn't already exist if ( ! in_array ( $record['RecData'], $outputarray['keywords'])) { $outputarray['keywords'][] = strtr( $record['RecData'], array("\x0a" => "", "\x0d" => " ") ) ; } } } /***************************************/ // The IPTC Copyright Notice Field contains copyrightnotice - find it $record = find_IPTC_Resource( $IPTC_array, "2:116" ); // Check if the Copyright Field exists and save the value if ( $record != FALSE ) { $outputarray = add_to_field( $outputarray, 'copyrightnotice' , strtr( $record['RecData'], $irbtrans ), "\n" ); } } } /***************************************/ // EXIF Processing // Retreive Information from the EXIF data if it exists if ( ( $Exif_array != FALSE ) || ( count( $Exif_array ) == 0 ) ) { // Check the Image Description Tag - it can contain the caption if ( array_key_exists( 270, $Exif_array[0] ) ) { $outputarray = add_to_field( $outputarray, 'caption' , $Exif_array[0][270]['Data'][0], "\n" ); } /***************************************/ // Check the Copyright Information Tag - it contains the copyrightnotice if ( array_key_exists( 33432, $Exif_array[0] ) ) { $outputarray = add_to_field( $outputarray, 'copyrightnotice' , HTML_UTF8_UnEscape( $Exif_array[0][33432]['Data'][0] ), "\n" ); } /***************************************/ // Check the Artist Name Tag - it contains the author if ( array_key_exists( 315, $Exif_array[0] ) ) { $outputarray = add_to_field( $outputarray, 'author' , HTML_UTF8_UnEscape( $Exif_array[0][315]['Data'][0] ), "\n" ); } } /***************************/ // FINISHED RETRIEVING INFORMATION // Perform final processing // Check if any urgency information was found if ( $outputarray["urgency"] == "" ) { // No urgency information was found - set it to default (None) $outputarray["urgency"] = "none"; } // Check if any copyrightstatus information was found if ( $outputarray["copyrightstatus"] == "" ) { // No copyrightstatus information was found - set it to default (Unmarked) $outputarray["copyrightstatus"] = "unmarked"; } // Return the resulting Photoshop File Info Array return $outputarray; } /****************************************************************************** * End of Function: get_photoshop_file_info ******************************************************************************/ /****************************************************************************** * * Function: put_photoshop_file_info * * Description: Stores Photoshop 'File Info' metadata in the same way that Photoshop * does. The 'File Info' metadata must be in an array similar to that * returned by get_photoshop_file_info, as follows: * * $file_info_array = array( * "title" => "", * "author" => "", * "authorsposition" => "", // Note: Not used in Photoshop 7 or higher * "caption" => "", * "captionwriter" => "", * "jobname" => "", // Note: Not used in Photoshop CS * "copyrightstatus" => "", * "copyrightnotice" => "", * "ownerurl" => "", * "keywords" => array( 0 => "", 1 => "", ... ), * "category" => "", // Note: Max 3 characters * "supplementalcategories" => array( 0 => "", 1 => "", ... ), * "date" => "", // Note: DATE MUST BE IN YYYY-MM-DD format * "city" => "", * "state" => "", * "country" => "", * "credit" => "", * "source" => "", * "headline" => "", * "instructions" => "", * "transmissionreference" => "", * "urgency" => "" ); * * Parameters: jpeg_header_data - a JPEG header data array in the same format * as from get_jpeg_header_data. This contains the * header information which is to be updated. * new_ps_file_info_array - An array as above, which contains the * 'File Info' metadata information to be * written. * Old_Exif_array - an array containing the EXIF information to be * updated, as retrieved by get_EXIF_JPEG. (saves having to parse the EXIF again) * Old_XMP_array - an array containing the XMP information to be * updated, as retrieved by read_XMP_array_from_text. (saves having to parse the XMP again) * Old_IRB_array - an array containing the Photoshop IRB information * to be updated, as retrieved by get_Photoshop_IRB. (saves having to parse the IRB again) * * Returns: jpeg_header_data - a JPEG header data array in the same format * as from get_jpeg_header_data, containing the * Photshop 'File Info' metadata. This can then * be written to a file using put_jpeg_header_data. * ******************************************************************************/ function put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Old_Exif_array, $Old_XMP_array, $Old_IRB_array ) { /*******************************************/ // PREPROCESSING // Check that the date is in the correct format (YYYY-MM-DD) // Explode the date into pieces using the - symbol $date_pieces = explode( "-", $new_ps_file_info_array[ 'date' ] ); // If there are not 3 pieces to the date, it is invalid if ( count( $date_pieces ) != 3 ) { // INVALID DATE echo "Invalid Date - must be YYYY-MM-DD format
"; return FALSE; } // Cycle through each piece of the date foreach( $date_pieces as $piece ) { // If the piece is not numeric, then the date is invalid. if ( ! is_numeric( $piece ) ) { // INVALID DATE echo "Invalid Date - must be YYYY-MM-DD format
"; return FALSE; } } // Make a unix timestamp at midnight on the date specified $date_stamp = mktime( 0,0,0, $date_pieces[1], $date_pieces[2], $date_pieces[0] ); // Create a translation table to remove carriage return characters $trans = array( "\x0d" => "" ); // Cycle through each of the File Info elements foreach( $new_ps_file_info_array as $valkey => $val ) { // If the element is 'Keywords' or 'Supplemental Categories', then // it is an array, and needs to be treated as one if ( ( $valkey != 'supplementalcategories' ) && ( $valkey != 'keywords' ) ) { // Not Keywords or Supplemental Categories // Convert escaped HTML characters to UTF8 and remove carriage returns $new_ps_file_info_array[ $valkey ] = strtr( HTML_UTF8_UnEscape( $val ), $trans ); } else { // Either Keywords or Supplemental Categories // Cycle through the array, foreach( $val as $subvalkey => $subval ) { // Convert escaped HTML characters to UTF8 and remove carriage returns $new_ps_file_info_array[ $valkey ][ $subvalkey ] = strtr( HTML_UTF8_UnEscape( $subval ), $trans ); } } } /*******************************************/ // EXIF Processing // Check if the EXIF array exists if( $Old_Exif_array == FALSE ) { // EXIF Array doesn't exist - create a new one $new_Exif_array = array ( 'Byte_Align' => "MM", 'Makernote_Tag' => false, 'Tags Name' => "TIFF", 0 => array( "Tags Name" => "TIFF" ) ); } else { // EXIF Array Does Exist - use it $new_Exif_array = $Old_Exif_array; } // Update the EXIF Image Description Tag with the new value $new_Exif_array[0][270] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 270 ]['Name'], "Tag Number" => 270, "Data Type" => 2, "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 270 ]['Type'], "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'caption' ]) )); // Update the EXIF Artist Name Tag with the new value $new_Exif_array[0][315] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 315 ]['Name'], "Tag Number" => 315, "Data Type" => 2, "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 315 ]['Type'], "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'author' ] ) ) ); // Update the EXIF Copyright Information Tag with the new value $new_Exif_array[0][33432] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 33432 ]['Name'], "Tag Number" => 33432, "Data Type" => 2, "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 33432 ]['Type'], "Data" => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'copyrightnotice' ]) ) ); // Photoshop checks if the "Date and Time of Original" and "Date and Time when Digitized" tags exist // If they don't exist, it means that the EXIF date may be wiped out if it is changed, so Photoshop // copies the EXIF date to these two tags if ( ( array_key_exists( 306, $new_Exif_array[0] ) )&& ( array_key_exists( 34665, $new_Exif_array[0] ) ) && ( array_key_exists( 0, $new_Exif_array[0][34665] ) ) ) { // Replace "Date and Time of Original" if it doesn't exist if ( ! array_key_exists( 36867, $new_Exif_array[0][34665][0] ) ) { $new_Exif_array[0][34665][0][36867] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36867 ]['Name'], "Tag Number" => 36867, "Data Type" => 2, "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36867 ]['Type'], "Data" => $new_Exif_array[0][306]['Data'] ); } // Replace "Date and Time when Digitized" if it doesn't exist if ( ! array_key_exists( 36868, $new_Exif_array[0][34665][0] ) ) { $new_Exif_array[0][34665][0][36868] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36868 ]['Name'], "Tag Number" => 36868, "Data Type" => 2, "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36868 ]['Type'], "Data" => $new_Exif_array[0][306]['Data'] ); } } // Photoshop changes the EXIF date Tag (306) to the current date, not the date that was entered in File Info $exif_date = date ( "Y:m:d H:i:s" ); // Update the EXIF Date and Time Tag with the new value $new_Exif_array[0][306] = array ( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 306 ]['Name'], "Tag Number" => 306, "Data Type" => 2, "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 306 ]['Type'], "Data" => array( $exif_date ) ); // Photoshop replaces the EXIF Software or Firmware Tag with "Adobe Photoshop ..." // This toolkit instead preserves existing value and appends the toolkit name to the end of it // Check if the EXIF Software or Firmware Tag exists if ( array_key_exists( 305, $new_Exif_array[0] ) ) { // An existing EXIF Software or Firmware Tag was found // Check if the existing Software or Firmware Tag already contains the Toolkit's name if ( stristr ( $new_Exif_array[0][305]['Data'][0], $GLOBALS[ "Software Name" ]) == FALSE ) { // Toolkit Name string not found in the existing Software/Firmware string - append it. $firmware_str = $new_Exif_array[0][305]['Data'][0] . " " . $GLOBALS[ "Software Name" ]; } else { // Toolkit name already exists in Software/Firmware string - don't put another copy in the string $firmware_str = $new_Exif_array[0][305]['Data'][0]; } } else { // No Software/Firmware string exists - create one $firmware_str = $GLOBALS[ "Software Name" ]; } // Update the EXIF Software/Firmware Tag with the new value $new_Exif_array[0][305] = array( "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 305 ]['Name'], "Tag Number" => 305, "Data Type" => 2, "Type" => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 305 ]['Type'], "Data" => array( HTML_UTF8_Escape( $firmware_str ) ) ); /*******************************************/ // Photoshop IRB Processing // Check if there is an existing Photoshop IRB array if ($Old_IRB_array == FALSE ) { // No existing IRB array - create one $new_IRB_array = array(); } else { // There is an existing Photoshop IRB array - use it $new_IRB_array = $Old_IRB_array; } // Remove any existing Copyright Flag, URL, or IPTC resources - these will be re-written foreach( $new_IRB_array as $resno => $res ) { if ( ( $res[ 'ResID' ] == 0x040A ) || ( $res[ 'ResID' ] == 0x040B ) || ( $res[ 'ResID' ] == 0x0404 ) ) { array_splice( $new_IRB_array, $resno, 1 ); } } // Add a new Copyright Flag resource if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Copyrighted Work" ) { $PS_copyright_flag = "\x01"; // Copyrighted } else { $PS_copyright_flag = "\x00"; // Public domain or Unmarked } $new_IRB_array[] = array( 'ResID' => 0x040A, 'ResName' => $GLOBALS[ "Photoshop_ID_Names" ][0x040A], 'ResDesc' => $GLOBALS[ "Photoshop_ID_Descriptions" ][0x040A], 'ResEmbeddedName' => "", 'ResData' => $PS_copyright_flag ); // Add a new URL resource $new_IRB_array[] = array( 'ResID' => 0x040B, 'ResName' => $GLOBALS[ "Photoshop_ID_Names" ][0x040B], 'ResDesc' => $GLOBALS[ "Photoshop_ID_Descriptions" ][0x040B], 'ResEmbeddedName' => "", 'ResData' => $new_ps_file_info_array[ 'ownerurl' ] ); // Create IPTC resource // IPTC requires date to be in the following format YYYYMMDD $iptc_date = date( "Ymd", $date_stamp ); // Create the new IPTC array $new_IPTC_array = array ( 0 => array ( 'IPTC_Type' => '2:00', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:00'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:00'], 'RecData' => "\x00\x02", ), 1 => array ( 'IPTC_Type' => '2:120', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:120'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:120'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'caption' ] ), 0 , 2000 ), ), 2 => array ( 'IPTC_Type' => '2:122', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:122'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:122'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'captionwriter' ] ), 0 , 32 ), ), 3 => array ( 'IPTC_Type' => '2:105', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:105'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:105'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'headline' ] ), 0 , 256 ), ), 4 => array ( 'IPTC_Type' => '2:40', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:40'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:40'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'instructions' ] ), 0, 256 ), ), 5 => array ( 'IPTC_Type' => '2:80', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:80'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:80'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'author' ] ), 0, 32 ), ), 6 => array ( 'IPTC_Type' => '2:85', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:85'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:85'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'authorsposition' ] ), 0, 32 ), ), 7 => array ( 'IPTC_Type' => '2:110', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:110'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:110'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'credit' ] ), 0, 32 ), ), 8 => array ( 'IPTC_Type' => '2:115', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:115'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:115'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'source' ] ), 0, 32 ), ), 9 => array ( 'IPTC_Type' => '2:05', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:05'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:05'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'title' ] ), 0, 64 ), ), 10 => array ( 'IPTC_Type' => '2:55', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:55'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:55'], 'RecData' => "$iptc_date", ), 11 => array ( 'IPTC_Type' => '2:90', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:90'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:90'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'city' ] ), 0, 32 ), ), 12 => array ( 'IPTC_Type' => '2:95', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:95'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:95'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'state' ] ), 0, 32 ), ), 13 => array ( 'IPTC_Type' => '2:101', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:101'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:101'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'country' ] ), 0, 64 ), ), 14 => array ( 'IPTC_Type' => '2:103', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:103'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:103'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'transmissionreference' ] ), 0, 32 ), ), 15 => array ( 'IPTC_Type' => '2:15', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:15'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:15'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'category' ] ), 0, 3 ), ), 21 => array ( 'IPTC_Type' => '2:116', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:10'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:10'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'copyrightnotice' ] ), 0, 128 ), ), ); // Check the value of urgency is valid if ( ( $new_ps_file_info_array[ 'urgency' ] > 0 ) && ( $new_ps_file_info_array[ 'urgency' ] < 9 ) ) { // Add the Urgency item to the IPTC array $new_IPTC_array[] = array ( 'IPTC_Type' => '2:10', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:10'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:10'], 'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'urgency' ] ), 0, 1 ), ); } // Cycle through the Supplemental Categories, foreach( $new_ps_file_info_array[ 'supplementalcategories' ] as $supcat ) { // Add this Supplemental Category to the IPTC array $new_IPTC_array[] = array ( 'IPTC_Type' => '2:20', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:20'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:20'], 'RecData' => HTML_UTF8_Escape( $supcat ), ); } // Cycle through the Keywords, foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword ) { // Add this Keyword to the IPTC array $new_IPTC_array[] = array ( 'IPTC_Type' => '2:25', 'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:25'], 'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:25'], 'RecData' => $keyword, ); } /***********************************/ // XMP Processing // Check if XMP existed previously if ($Old_XMP_array == FALSE ) { // XMP didn't exist - create a new one based on a blank structure $new_XMP_array = XMP_Check( $GLOBALS[ 'Blank XMP Structure' ], array( ) ); } else { // XMP does exist // Some old XMP processors used x:xapmeta, check for this if ( $Old_XMP_array[0]['tag'] == 'x:xapmeta' ) { // x:xapmeta found - change it to x:xmpmeta $Old_XMP_array[0]['tag'] = 'x:xmpmeta'; } // Ensure that the existing XMP has all required fields, and add any that are missing $new_XMP_array = XMP_Check( $GLOBALS[ 'Blank XMP Structure' ], $Old_XMP_array ); } // Process the XMP Photoshop block // Find the Photoshop Information within the XMP block $photoshop_block = & find_XMP_block( $new_XMP_array, "photoshop" ); // The Photoshop CaptionWriter tag contains captionwriter - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:CaptionWriter" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'captionwriter' ]; // The Photoshop Category tag contains category - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:Category" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'category' ]; // The Photoshop DateCreated tag contains date - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:DateCreated" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'date' ]; // The Photoshop City tag contains city - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:City" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'city' ]; // The Photoshop State tag contains state - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:State" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'state' ]; // The Photoshop Country tag contains country - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:Country" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'country' ]; // The Photoshop AuthorsPosition tag contains authorsposition - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:AuthorsPosition" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'authorsposition' ]; // The Photoshop Credit tag contains credit - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:Credit" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'credit' ]; // The Photoshop Source tag contains source - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:Source" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'source' ]; // The Photoshop Headline tag contains headline - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:Headline" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'headline' ]; // The Photoshop Instructions tag contains instructions - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:Instructions" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'instructions' ]; // The Photoshop TransmissionReference tag contains transmissionreference - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:TransmissionReference" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'transmissionreference' ]; // The Photoshop Urgency tag contains urgency - Find it and Update the value $Item = & find_XMP_item( $photoshop_block, "photoshop:Urgency" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'urgency' ]; // The Photoshop SupplementalCategories tag contains supplementalcategories - Find it $Item = & find_XMP_item( $photoshop_block, "photoshop:SupplementalCategories" ); // Create an array to receive the XML list items for the Supplemental Categories $new_supcat_array = array( ); // Cycle through the Supplemental Categories foreach ( $new_ps_file_info_array[ 'supplementalcategories' ] as $sup_category ) { // Add a new list item for this Supplemental Category $new_supcat_array[] = array( 'tag' => 'rdf:li', 'value' => $sup_category ); } // Add the array of Supplemental Category List Items to the Photoshop SupplementalCategories tag $Item[ 'children' ][ 0 ][ 'children' ] = $new_supcat_array; // Process the XMP XAP block // Find the XAP Information within the XMP block $XAP_block = & find_XMP_block( $new_XMP_array, "xap" ); // The XAP CreateDate tag contains date XMP was first created - Find it and Update the value $Item = & find_XMP_item( $XAP_block, "xap:CreateDate" ); // Check if the CreateDate is blank if ( $Item[ 'value' ] == "" ) { // CreateDate is blank - we must have just added it - set it to the current date $Item[ 'value' ] = date( "Y-m-d\TH:i:s" ); $Item[ 'value' ] .= get_Local_Timezone_Offset( ); } // The XAP ModifyDate tag contains last resource change date - Find it and Update the value to the current date $Item = & find_XMP_item( $XAP_block, "xap:ModifyDate" ); $Item[ 'value' ] = date( "Y-m-d\TH:i:s" ); $Item[ 'value' ] .= get_Local_Timezone_Offset( ); // The XAP ModifyDate tag contains last XMP change date - Find it and Update the value to the current date $Item = & find_XMP_item( $XAP_block, "xap:MetadataDate" ); $Item[ 'value' ] = date( "Y-m-d\TH:i:s" ); $Item[ 'value' ] .= get_Local_Timezone_Offset( ); // The XAP CreatorTool tag contains name of the software editor - Find it $Item = & find_XMP_item( $XAP_block, "xap:CreatorTool" ); // Photoshop replaces the CreatorTool with "Adobe Photoshop ..." // This toolkit instead preserves existing value and appends the toolkit name to the end of it // Check if a CreatorTool already exists if ( $Item[ 'value' ] != "" ) { // An existing CreatorTool was found // Check if the existing CreatorTool already contains the Toolkit's name if ( stristr ( $Item[ 'value' ], $GLOBALS[ "Software Name" ]) == FALSE ) { // Toolkit Name string not found in the existing CreatorTool string - append it. $Item[ 'value' ] = $Item[ 'value' ] . " " . $GLOBALS[ "Software Name" ]; } else { // Toolkit name already exists in CreatorTool string - leave as is } } else { // No CreatorTool string exists - create one $Item[ 'value' ] = $GLOBALS[ "Software Name" ]; } // Process the XMP Basic Job Information block // Find the XAP Basic Job Information within the XMP block $XAPBJ_block = & find_XMP_block( $new_XMP_array, "xapBJ" ); // The XAP Basic Job JobRef tag contains urgency - Find it and Update the value $Item = & find_XMP_item( $XAPBJ_block, "xapBJ:JobRef" ); $Item[ 'children' ][ 0 ][ 'children' ] = array( array ( 'tag' => 'rdf:li', 'attributes' => array ( 'rdf:parseType' => 'Resource' ), 'children' => array ( 0 => array ( 'tag' => 'stJob:name', 'value' => $new_ps_file_info_array[ 'jobname' ] ), ), ), ); // Process the XMP XAP Rights Information block // Find the XAP Rights Information within the XMP block $XAPRights_block = & find_XMP_block( $new_XMP_array, "xapRights" ); // The XAP Rights Marked tag should only be present if copyrightstatus is 'Copyrighted Work' or 'Public Domain' // If copyrightstatus 'Unmarked' or anything else, the XAP Rights Marked tag should be missing // Remove any existing XAP Rights Marked tags - they will be replaced foreach( $XAPRights_block as $tagno => $tag ) { if ( $tag[ 'tag' ] == "xapRights:Marked" ) { array_splice( $XAPRights_block, $tagno, 1 ); } } // Check the value of the copyrightstatus flag if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Copyrighted Work" ) { // Copyrighted - add the tag $XAPRights_block[] = array ( 'tag' => 'xapRights:Marked', 'value' => 'True' ); } else if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Public Domain" ) { // Public domain - add the tag $XAPRights_block[] = array ( 'tag' => 'xapRights:Marked', 'value' => 'False' ); } else { // Unmarked or Other - Do nothing - don't add a Marked tag } // The XAP Rights WebStatement tag contains ownerurl - Find it and Update the value $Item = & find_XMP_item( $XAPRights_block, "xapRights:WebStatement" ); $Item[ 'value' ] = $new_ps_file_info_array[ 'ownerurl' ]; // Process the XMP Dublin Core block // Find the Dublin Core Information within the XMP block $DC_block = & find_XMP_block( $new_XMP_array, "dc" ); // The Dublin Core description tag contains caption - Find it and Update the value $Item = & find_XMP_item( $DC_block, "dc:description" ); $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", 'value' => $new_ps_file_info_array[ 'caption' ], 'attributes' => array( 'xml:lang' => "x-default" ) ) ); // The Dublin Core title tag contains title - Find it and Update the value $Item = & find_XMP_item( $DC_block, "dc:title" ); $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", 'value' => $new_ps_file_info_array[ 'title' ], 'attributes' => array( 'xml:lang' => "x-default" ) ) ); // The Dublin Core rights tag contains copyrightnotice - Find it and Update the value $Item = & find_XMP_item( $DC_block, "dc:rights" ); $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", 'value' => $new_ps_file_info_array[ 'copyrightnotice' ], 'attributes' => array( 'xml:lang' => "x-default" ) ) ); // The Dublin Core creator tag contains author - Find it and Update the value $Item = & find_XMP_item( $DC_block, "dc:creator" ); $Item[ 'children' ][ 0 ][ 'children' ] = array( array( 'tag' => "rdf:li", 'value' => $new_ps_file_info_array[ 'author' ]) ); // The Dublin Core subject tag contains keywords - Find it $Item = & find_XMP_item( $DC_block, "dc:subject" ); // Create an array to receive the Keywords List Items $new_keywords_array = array( ); // Cycle through each keyword foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword ) { // Add a List item for this keyword $new_keywords_array[] = array( 'tag' => "rdf:li", 'value' => $keyword ); } // Add the Keywords List Items array to the Dublin Core subject tag $Item[ 'children' ][ 0 ][ 'children' ] = $new_keywords_array; /***************************************/ // FINISHED UPDATING VALUES // Insert the new IPTC array into the Photoshop IRB array $new_IRB_array = put_Photoshop_IPTC( $new_IRB_array, $new_IPTC_array ); // Write the EXIF array to the JPEG header $jpeg_header_data = put_EXIF_JPEG( $new_Exif_array, $jpeg_header_data ); // Convert the XMP array to XMP text $xmp_text = write_XMP_array_to_text( $new_XMP_array ); // Write the XMP text to the JPEG Header $jpeg_header_data = put_XMP_text( $jpeg_header_data, $xmp_text ); // Write the Photoshop IRB array to the JPEG header $jpeg_header_data = put_Photoshop_IRB( $jpeg_header_data, $new_IRB_array ); return $jpeg_header_data; } /****************************************************************************** * End of Function: put_photoshop_file_info ******************************************************************************/ /****************************************************************************** * * INTERNAL FUNCTIONS * ******************************************************************************/ /****************************************************************************** * * Function: get_Local_Timezone_Offset * * Description: Returns a string indicating the time difference between the local * timezone and GMT in hours and minutes, e.g. +10:00 or -06:30 * * Parameters: None * * Returns: $tz_str - a string containing the timezone offset * ******************************************************************************/ function get_Local_Timezone_Offset( ) { // Retrieve the Timezone offset in seconds $tz_seconds = date( "Z" ); // Check if the offset is less than zero if ( $tz_seconds < 0 ) { // Offset is less than zero - add a Minus sign to the output $tz_str = "-"; } else { // Offset is greater than or equal to zero - add a Plus sign to the output $tz_str = "+"; } // Add the absolute offset to the output, formatted as HH:MM $tz_str .= gmdate( "H:i", abs($tz_seconds) ); // Return the result return $tz_str; } /****************************************************************************** * End of Function: get_Local_Timezone_Offset ******************************************************************************/ /****************************************************************************** * * Function: XMP_Check * * Description: Checks a given XMP array against a reference array, and adds any * missing blocks and tags * * NOTE: This is a recursive function * * Parameters: reference_array - The standard XMP array which contains all required tags * check_array - The XMP array to check * * Returns: output - a string containing the timezone offset * ******************************************************************************/ function XMP_Check( $reference_array, $check_array) { // Cycle through each of the elements of the reference XMP array foreach( $reference_array as $valkey => $val ) { // Search for the current reference tag within the XMP array to be checked $tagpos = find_XMP_Tag( $check_array, $val ); // Check if the tag was found if ( $tagpos === FALSE ) { // Tag not found - Add tag to array being checked $tagpos = count( $check_array ); $check_array[ $tagpos ] = $val; } // Check if the reference tag has children if ( array_key_exists( 'children', $val ) ) { // Reference tag has children - these need to be checked too // Determine if the array being checked has children for this tag if ( ! array_key_exists( 'children', $check_array[ $tagpos ] ) ) { // Array being checked has no children - add a blank children array $check_array[ $tagpos ][ 'children' ] = array( ); } // Recurse, checking the children tags against the reference children $check_array[ $tagpos ][ 'children' ] = XMP_Check( $val[ 'children' ] , $check_array[ $tagpos ][ 'children' ] ); } else { // No children - don't need to check anything else } } // Return the checked XMP array return $check_array; } /****************************************************************************** * End of Function: XMP_Check ******************************************************************************/ /****************************************************************************** * * Function: find_XMP_Tag * * Description: Searches one level of an XMP array for a specific tag, and * returns the tag position. Does not descend the XMP tree. * * Parameters: XMP_array - The XMP array which should be searched * tag - The XMP tag to search for (in same format as would be found in XMP array) * * Returns: output - a string containing the timezone offset * ******************************************************************************/ function find_XMP_Tag( $XMP_array, $tag ) { $namespacestr = ""; // Some tags have a namespace attribute which defines them (i.e. rdf:Description tags) // Check if the tag being searched for has attributs if ( array_key_exists( 'attributes', $tag ) ) { // Tag has attributes - cycle through them foreach( $tag['attributes'] as $key => $val ) { // Check if the current attribute is the namespace attribute - i.e. starts with xmlns: if ( strcasecmp( substr($key,0,6), "xmlns:" ) == 0 ) { // Found a namespace attribute - save it for later. $namespacestr = $key; } } } // Cycle through the elements of the XMP array to be searched. foreach( $XMP_array as $valkey => $val ) { // Check if the current element is a rdf:Description tag if ( strcasecmp ( $tag[ 'tag' ], 'rdf:Description' ) == 0 ) { // Current element is a rdf:Description tag // Check if the namespace attribute is the same as in the tag that is being searched for if ( array_key_exists( $namespacestr, $val['attributes'] ) ) { // Namespace is the same - this is the correct tag - return it's position return $valkey; } } // Otherwise check if the current element has the same name as the tag in question else if ( strcasecmp ( $val[ 'tag' ], $tag[ 'tag' ] ) == 0 ) { // Tags have same name - this is the correct tag - return it's position return $valkey; } } // Cycled through all tags without finding the correct one - return error value return FALSE; } /****************************************************************************** * End of Function: find_XMP_Tag ******************************************************************************/ /****************************************************************************** * * Function: create_GUID * * Description: Creates a Globally Unique IDentifier, in the format that is used * by XMP (and Windows). This value is not guaranteed to be 100% unique, * but it is ridiculously unlikely that two identical values will be produced * * Parameters: none * * Returns: output - a string containing the timezone offset * ******************************************************************************/ function create_GUID( ) { // Create a md5 sum of a random number - this is a 32 character hex string $raw_GUID = md5( uniqid( getmypid() . rand( ) . (double)microtime()*1000000, TRUE ) ); // Format the string into 8-4-4-4-12 (numbers are the number of characters in each block) 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); } /****************************************************************************** * End of Function: create_GUID ******************************************************************************/ /****************************************************************************** * * Function: add_to_field * * Description: Adds a value to a particular field in a Photoshop File Info array, * first checking whether the value is already there. If the value is * already in the array, it is not changed, otherwise the value is appended * to whatever is already in that field of the array * * Parameters: field_array - The Photoshop File Info array to receive the new value * field - The File Info field which the value is for * value - The value to be written into the File Info * separator - The string to place between values when having to append the value * * Returns: output - the Photoshop File Info array with the value added * ******************************************************************************/ function add_to_field( $field_array, $field, $value, $separator ) { // Check if the value is blank if ( $value == "" ) { // Value is blank - return File Info array unchanged return $field_array; } // Check if the value can be found anywhere within the existing value for this field if ( stristr ( $field_array[ $field ], $value ) == FALSE) { // Value could not be found // Check if the existing value for the field is blank if ( $field_array[$field] != "" ) { // Existing value for field is not blank - append a separator $field_array[$field] .= $separator; } // Append the value to the field $field_array[$field] .= $value; } // Return the File Info Array return $field_array; } /****************************************************************************** * End of Function: add_to_field ******************************************************************************/ /****************************************************************************** * * Function: find_IPTC_Resource * * Description: Searches an IPTC array for a particular record, and returns it if found * * Parameters: IPTC_array - The IPTC array to search * record_type - The IPTC record number to search for (e.g. 2:151 ) * * Returns: output - the contents of the record if found * FALSE - otherwise * ******************************************************************************/ function find_IPTC_Resource( $IPTC_array, $record_type ) { // Cycle through the ITPC records foreach ($IPTC_array as $record) { // Check the IPTC type against the required type if ( $record['IPTC_Type'] == $record_type ) { // IPTC type matches - return this record return $record; } } // No matching record found - return error code return FALSE; } /****************************************************************************** * End of Function: find_IPTC_Resource ******************************************************************************/ /****************************************************************************** * * Function: find_Photoshop_IRB_Resource * * Description: Searches a Photoshop IRB array for a particular resource, and returns it if found * * Parameters: IRB_array - The IRB array to search * resource_ID - The IRB resource number to search for (e.g. 0x03F9 ) * * Returns: output - the contents of the resource if found * FALSE - otherwise * ******************************************************************************/ function find_Photoshop_IRB_Resource( $IRB_array, $resource_ID ) { // Cycle through the IRB resources foreach( $IRB_array as $IRB_Resource ) { // Check the IRB resource ID against the required ID if ( $resource_ID == $IRB_Resource['ResID'] ) { // Resource ID matches - return this resource return $IRB_Resource; } } // No matching resource found - return error code return FALSE; } /****************************************************************************** * End of Function: find_Photoshop_IRB_Resource ******************************************************************************/ /****************************************************************************** * * Function: find_XMP_item * * Description: Searches a one level of a XMP array for a particular item by name, and returns it if found. * Does not descend through the XMP array * * Parameters: Item_Array - The XMP array to search * item_name - The name of the tag to serch for (e.g. photoshop:CaptionWriter ) * * Returns: output - the contents of the tag if found * FALSE - otherwise * ******************************************************************************/ function find_XMP_item( & $Item_Array, $item_name ) { // Cycle through the top level of the XMP array foreach( $Item_Array as $Item_Key => $Item ) { // Check this tag name against the required tag name if( $Item['tag'] == $item_name ) { // The tag names match - return the item return $Item_Array[ $Item_Key ]; } } // No matching tag found - return error code return FALSE; } /****************************************************************************** * End of Function: find_XMP_item ******************************************************************************/ /****************************************************************************** * * Function: find_XMP_block * * Description: Searches a for a particular rdf:Description block within a XMP array, and returns its children if found. * * Parameters: XMP_array - The XMP array to search as returned by read_XMP_array_from_text * block_name - The namespace of the XMP block to be found (e.g. photoshop or xapRights ) * * Returns: output - the children of the tag if found * FALSE - otherwise * ******************************************************************************/ function find_XMP_block( & $XMP_array, $block_name ) { // Check that the rdf:RDF section can be found (which contains the rdf:Description tags if ( ( $XMP_array !== FALSE ) && ( ( $XMP_array[0]['tag'] == "x:xapmeta" ) || ( $XMP_array[0]['tag'] == "x:xmpmeta" ) ) && ( $XMP_array[0]['children'][0]['tag'] == "rdf:RDF" ) ) { // Found rdf:RDF // Make it's children easily accessible $RDF_Contents = $XMP_array[0]['children'][0]['children']; // Cycle through the children (rdf:Description tags) foreach ($RDF_Contents as $RDF_Key => $RDF_Item) { // Check if this is a rdf:description tag that has children if ( ( $RDF_Item['tag'] == "rdf:Description" ) && ( array_key_exists( 'children', $RDF_Item ) ) ) { // RDF Description tag has children, // Cycle through it's attributes foreach( $RDF_Item['attributes'] as $key => $val ) { // Check if this attribute matches the namespace block name required if ( $key == "xmlns:$block_name" ) { // Namespace matches required block name - return it's children return $XMP_array[0]['children'][0]['children'][ $RDF_Key ]['children']; } } } } } // No matching rdf:Description block found return FALSE; } /****************************************************************************** * End of Function: find_XMP_block ******************************************************************************/ /****************************************************************************** * Global Variable: Blank XMP Structure * * Contents: A template XMP array which can be used to create a new XMP segment * ******************************************************************************/ // Create a GUID to be used in this template array $new_GUID = create_GUID( ); $GLOBALS[ 'Blank XMP Structure' ] = array ( 0 => array ( 'tag' => 'x:xmpmeta', 'attributes' => array ( 'xmlns:x' => 'adobe:ns:meta/', 'x:xmptk' => 'XMP toolkit 3.0-28, framework 1.6', ), 'children' => array ( 0 => array ( 'tag' => 'rdf:RDF', 'attributes' => array ( 'xmlns:rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'xmlns:iX' => 'http://ns.adobe.com/iX/1.0/', ), 'children' => array ( 1 => array ( 'tag' => 'rdf:Description', 'attributes' => array ( 'rdf:about' => "uuid:$new_GUID", 'xmlns:pdf' => 'http://ns.adobe.com/pdf/1.3/', ), ), 2 => array ( 'tag' => 'rdf:Description', 'attributes' => array ( 'rdf:about' => "uuid:$new_GUID", 'xmlns:photoshop' => 'http://ns.adobe.com/photoshop/1.0/', ), 'children' => array ( 0 => array ( 'tag' => 'photoshop:CaptionWriter', 'value' => '', ), 1 => array ( 'tag' => 'photoshop:Category', 'value' => '', ), 2 => array ( 'tag' => 'photoshop:DateCreated', 'value' => '', ), 3 => array ( 'tag' => 'photoshop:City', 'value' => '', ), 4 => array ( 'tag' => 'photoshop:State', 'value' => '', ), 5 => array ( 'tag' => 'photoshop:Country', 'value' => '', ), 6 => array ( 'tag' => 'photoshop:Credit', 'value' => '', ), 7 => array ( 'tag' => 'photoshop:Source', 'value' => '', ), 8 => array ( 'tag' => 'photoshop:Headline', 'value' => '', ), 9 => array ( 'tag' => 'photoshop:Instructions', 'value' => '', ), 10 => array ( 'tag' => 'photoshop:TransmissionReference', 'value' => '', ), 11 => array ( 'tag' => 'photoshop:Urgency', 'value' => '', ), 12 => array ( 'tag' => 'photoshop:SupplementalCategories', 'children' => array ( 0 => array ( 'tag' => 'rdf:Bag', ), ), ), 13 => array ( 'tag' => 'photoshop:AuthorsPosition', 'value' => '', ), ), ), 4 => array ( 'tag' => 'rdf:Description', 'attributes' => array ( 'rdf:about' => "uuid:$new_GUID", 'xmlns:xap' => 'http://ns.adobe.com/xap/1.0/', ), 'children' => array ( 0 => array ( 'tag' => 'xap:CreateDate', 'value' => '', ), 1 => array ( 'tag' => 'xap:ModifyDate', 'value' => '', ), 2 => array ( 'tag' => 'xap:MetadataDate', 'value' => '', ), 3 => array ( 'tag' => 'xap:CreatorTool', 'value' => '', ), ), ), 5 => array ( 'tag' => 'rdf:Description', 'attributes' => array ( 'about' => "uuid:$new_GUID", 'xmlns:stJob' => 'http://ns.adobe.com/xap/1.0/sType/Job#', 'xmlns:xapBJ' => 'http://ns.adobe.com/xap/1.0/bj/', ), 'children' => array ( 0 => array ( 'tag' => 'xapBJ:JobRef', 'children' => array ( 0 => array ( 'tag' => 'rdf:Bag', 'children' => array ( ), ), ), ), ), ), 6 => array ( 'tag' => 'rdf:Description', 'attributes' => array ( 'rdf:about' => "uuid:$new_GUID", 'xmlns:xapRights' => 'http://ns.adobe.com/xap/1.0/rights/', ), 'children' => array ( 1 => array ( 'tag' => 'xapRights:WebStatement', 'value' => '', ), ), ), 7 => array ( 'tag' => 'rdf:Description', 'attributes' => array ( 'rdf:about' => "uuid:$new_GUID", 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/', ), 'children' => array ( 0 => array ( 'tag' => 'dc:format', 'value' => 'image/jpeg', ), 1 => array ( 'tag' => 'dc:title', 'children' => array ( 0 => array ( 'tag' => 'rdf:Alt', ), ), ), 2 => array ( 'tag' => 'dc:description', 'children' => array ( 0 => array ( 'tag' => 'rdf:Alt', ), ), ), 3 => array ( 'tag' => 'dc:rights', 'children' => array ( 0 => array ( 'tag' => 'rdf:Alt', ), ), ), 4 => array ( 'tag' => 'dc:creator', 'children' => array ( 0 => array ( 'tag' => 'rdf:Seq', ), ), ), 5 => array ( 'tag' => 'dc:subject', 'children' => array ( 0 => array ( 'tag' => 'rdf:Bag', ), ), ), ), ), /* 0 => array ( 'tag' => 'rdf:Description', 'attributes' => array ( 'rdf:about' => "uuid:$new_GUID", 'xmlns:exif' => 'http://ns.adobe.com/exif/1.0/', ), 'children' => array ( //EXIF DATA GOES HERE - Not Implemented yet ), ), */ /* 2 => array ( 'tag' => 'rdf:Description', 'attributes' => array ( 'rdf:about' => "uuid:$new_GUID", 'xmlns:tiff' => 'http://ns.adobe.com/tiff/1.0/', ), 'children' => array ( // TIFF DATA GOES HERE - Not Implemented yet 0 => array ( 'tag' => 'tiff:Make', 'value' => 'NIKON CORPORATION', ), ), ), */ /* 3 => array ( 'tag' => 'rdf:Description', 'attributes' => array ( 'rdf:about' => "uuid:$new_GUID", 'xmlns:stRef' => 'http://ns.adobe.com/xap/1.0/sType/ResourceRef#', 'xmlns:xapMM' => 'http://ns.adobe.com/xap/1.0/mm/', ), 'children' => array ( // XAPMM DATA GOES HERE - Not Implemented yet 0 => array ( 'tag' => 'xapMM:DocumentID', 'value' => 'adobe:docid:photoshop:dceba4c2-e699-11d8-94b2-b6ec48319f2d', ), 1 => array ( 'tag' => 'xapMM:DerivedFrom', 'attributes' => array ( 'rdf:parseType' => 'Resource', ), 'children' => array ( 0 => array ( 'tag' => 'stRef:documentID', 'value' => 'adobe:docid:photoshop:5144475b-e698-11d8-94b2-b6ec48319f2d', ), 1 => array ( 'tag' => 'stRef:instanceID', 'value' => "uuid:$new_GUID", ), ), ), ), ), */ ), ), ), ), ); /****************************************************************************** * End of Global Variable: Blank XMP Structure ******************************************************************************/ ?>