diff options
Diffstat (limited to 'includes/jpeg_metadata_tk')
57 files changed, 24176 insertions, 0 deletions
diff --git a/includes/jpeg_metadata_tk/COPYING.txt b/includes/jpeg_metadata_tk/COPYING.txt new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/includes/jpeg_metadata_tk/COPYING.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/includes/jpeg_metadata_tk/EXIF.php b/includes/jpeg_metadata_tk/EXIF.php new file mode 100644 index 0000000..8751dcd --- /dev/null +++ b/includes/jpeg_metadata_tk/EXIF.php @@ -0,0 +1,2764 @@ +<? + +/****************************************************************************** +* +* Filename: EXIF.php +* +* Description: Provides functions for reading and writing EXIF Information +* to/from an APP1 segment of a JPEG file +* Unfortunately, because EXIF data may be distributed anywhere +* throughout an image file, rather than just being in one block, +* it is impossible to pass just a string containing only the EXIF +* information. Hence it is neccessary to be able to seek to +* any point in the file. This causes the HTTP and FTP wrappers +* not to work - i.e. the EXIF functions will only work with local +* files. +* To work on an internet file, copy it locally to start with: +* +* $newfilename = tempnam ( $dir, "tmpexif" ); +* copy ( "http://whatever.com", $newfilename ); +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* Changes: 1.00 -> 1.10 : added function get_EXIF_TIFF to allow extracting EXIF from a TIFF file +* 1.10 -> 1.11 : added functionality to allow decoding of XMP and Photoshop IRB information +* embedded within the EXIF data +* added checks for http and ftp wrappers, as these are not supported +* changed interpret_IFD to allow thumbnail links to work when +* toolkit is portable across directories +* +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 : Thoroughly test the functions for writing EXIF segments +// TODO : Figure out a way to allow EXIF to function normally with HTTP and FTP wrappers +// TODO : Implement EXIF decoding of Device Setting Description field +// TODO : Implement EXIF decoding of SpatialFrequencyResponse field +// TODO : Implement EXIF decoding of OECF field +// TODO : Implement EXIF decoding of SubjectArea field +// TODO : Add a put_EXIF_TIFF function + +/****************************************************************************** +* +* Initialisation +* +******************************************************************************/ + + +if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) ) $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE; +if ( !isset( $GLOBALS['SHOW_BINARY_DATA_HEX'] ) ) $GLOBALS['SHOW_BINARY_DATA_HEX'] = FALSE; +if ( !isset( $GLOBALS['SHOW_BINARY_DATA_TEXT'] ) ) $GLOBALS['SHOW_BINARY_DATA_TEXT'] = FALSE; + + +include_once 'EXIF_Tags.php'; +include_once 'EXIF_Makernote.php'; +include_once 'PIM.php'; +include_once 'Unicode.php'; +include_once 'JPEG.php'; +include_once 'IPTC.php'; +include_once 'Photoshop_IRB.php'; // Change: as of version 1.11 - Required for TIFF with embedded IRB +include_once 'XMP.php'; // Change: as of version 1.11 - Required for TIFF with embedded XMP +include_once 'pjmt_utils.php'; // Change: as of version 1.11 - Required for directory portability + + + + + + + + +/****************************************************************************** +* +* Function: get_EXIF_JPEG +* +* Description: Retrieves information from a Exchangeable Image File Format (EXIF) +* APP1 segment and returns it in an array. +* +* Parameters: filename - the filename of the JPEG image to process +* +* Returns: OutputArray - Array of EXIF records +* FALSE - If an error occured in decoding +* +******************************************************************************/ + +function get_EXIF_JPEG( $filename ) +{ + // Change: Added as of version 1.11 + // Check if a wrapper is being used - these are not currently supported (see notes at top of file) + if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) ) + { + // A HTTP or FTP wrapper is being used - show a warning and abort + error_log( "HTTP and FTP wrappers are currently not supported with EXIF - See EXIF functionality documentation - a local file must be specified<br> + To work on an internet file, copy it locally to start with:<br><br>\n + $newfilename = tempnam ( \$dir, \"tmpexif\" );<br>\n + copy ( \"http://whatever.com\", \$newfilename );<br><br>\n" ); + return FALSE; + } + + // get the JPEG headers + $jpeg_header_data = get_jpeg_header_data( $filename ); + + + // Flag that an EXIF segment has not been found yet + $EXIF_Location = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP1 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) + { + // And if it has the EXIF label, + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) ) // For some reason, some files have a faulty EXIF name which has a 0xFF in it + { + // Save the location of the EXIF segment + $EXIF_Location = $i; + } + } + + } + + // Check if an EXIF segment was found + if ( $EXIF_Location == -1 ) + { + // Couldn't find any EXIF block to decode + return FALSE; + } + + $filehnd = @fopen($filename, 'rb'); + + // Check if the file opened successfully + if ( ! $filehnd ) + { + // Could't open the file - exit + error_log( "Could not open file $filename" ); + return FALSE; + } + + fseek( $filehnd, $jpeg_header_data[$EXIF_Location]['SegDataStart'] + 6 ); + + // Decode the Exif segment into an array and return it + $exif_data = process_TIFF_Header( $filehnd, "TIFF" ); + + + + // Close File + fclose($filehnd); + return $exif_data; +} + +/****************************************************************************** +* End of Function: get_EXIF_JPEG +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: put_EXIF_JPEG +* +* Description: Stores information into a Exchangeable Image File Format (EXIF) +* APP1 segment from an EXIF array. +* +* WARNING: Because the EXIF standard allows pointers to data +* outside the APP1 segment, if there are any such pointers in +* a makernote, this function will DAMAGE them since it will not +* be aware that there is an external pointer. This will often +* happen with Makernotes that include an embedded thumbnail. +* This damage could be prevented where makernotes can be decoded, +* but currently this is not implemented. +* +* +* Parameters: exif_data - The array of EXIF data to insert into the JPEG header +* jpeg_header_data - The JPEG header into which the EXIF data +* should be stored, as from get_jpeg_header_data +* +* Returns: jpeg_header_data - JPEG header array with the EXIF segment inserted +* FALSE - If an error occured +* +******************************************************************************/ + +function put_EXIF_JPEG( $exif_data, $jpeg_header_data ) +{ + // pack the EXIF data into its proper format for a JPEG file + $packed_data = get_TIFF_Packed_Data( $exif_data ); + if ( $packed_data === FALSE ) + { + return $jpeg_header_data; + } + + $packed_data = "Exif\x00\x00$packed_data"; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP1 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) + { + // And if it has the EXIF label, + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) ) // For some reason, some files have a faulty EXIF name which has a 0xFF in it + { + // Found a preexisting EXIF block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = $packed_data; + return $jpeg_header_data; + } + } + } + + // No preexisting segment segment found, insert a new one at the start of the header data. + + // Determine highest position of an APP segment at or below APP3, so we can put the + // new APP3 at this position + + + $highest_APP = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // Check if we have found an APP segment at or below APP3, + if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) ) + { + // Found an APP segment at or below APP12 + $highest_APP = $i; + } + } + + // No preexisting EXIF block found, insert a new one at the start of the header data. + array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xE1, + "SegName" => "APP1", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ], + "SegData" => $packed_data ) ) ); + return $jpeg_header_data; + +} + +/****************************************************************************** +* End of Function: put_EXIF_JPEG +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_Meta_JPEG +* +* Description: Retrieves information from a Meta APP3 segment and returns it +* in an array. Uses information supplied by the +* get_jpeg_header_data function. +* The Meta segment has the same format as an EXIF segment, but +* uses different tags +* +* Parameters: filename - the filename of the JPEG image to process +* +* Returns: OutputArray - Array of Meta records +* FALSE - If an error occured in decoding +* +******************************************************************************/ + +function get_Meta_JPEG( $filename ) +{ + // Change: Added as of version 1.11 + // Check if a wrapper is being used - these are not currently supported (see notes at top of file) + if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) ) + { + // A HTTP or FTP wrapper is being used - show a warning and abort + echo "HTTP and FTP wrappers are currently not supported with Meta - See EXIF/Meta functionality documentation - a local file must be specified<br>"; + echo "To work on an internet file, copy it locally to start with:<br><br>\n"; + echo "\$newfilename = tempnam ( \$dir, \"tmpmeta\" );<br>\n"; + echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n"; + return FALSE; + } + + // get the JPEG headers + $jpeg_header_data = get_jpeg_header_data( $filename ); + + + // Flag that an Meta segment has not been found yet + $Meta_Location = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP3 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 ) + { + // And if it has the Meta label, + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) ) + { + // Save the location of the Meta segment + $Meta_Location = $i; + } + } + } + + // Check if an EXIF segment was found + if ( $Meta_Location == -1 ) + { + // Couldn't find any Meta block to decode + return FALSE; + } + + + $filehnd = @fopen($filename, 'rb'); + + // Check if the file opened successfully + if ( ! $filehnd ) + { + // Could't open the file - exit + error_log( "Could not open file $filename" ); + return FALSE; + } + + fseek( $filehnd, $jpeg_header_data[$Meta_Location]['SegDataStart'] + 6 ); + + // Decode the Meta segment into an array and return it + $meta = process_TIFF_Header( $filehnd, "Meta" ); + + // Close File + fclose($filehnd); + + return $meta; +} + +/****************************************************************************** +* End of Function: get_Meta +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: put_Meta_JPEG +* +* Description: Stores information into a Meta APP3 segment from a Meta array. +* +* +* WARNING: Because the Meta (EXIF) standard allows pointers to data +* outside the APP1 segment, if there are any such pointers in +* a makernote, this function will DAMAGE them since it will not +* be aware that there is an external pointer. This will often +* happen with Makernotes that include an embedded thumbnail. +* This damage could be prevented where makernotes can be decoded, +* but currently this is not implemented. +* +* +* Parameters: meta_data - The array of Meta data to insert into the JPEG header +* jpeg_header_data - The JPEG header into which the Meta data +* should be stored, as from get_jpeg_header_data +* +* Returns: jpeg_header_data - JPEG header array with the Meta segment inserted +* FALSE - If an error occured +* +******************************************************************************/ + +function put_Meta_JPEG( $meta_data, $jpeg_header_data ) +{ + // pack the Meta data into its proper format for a JPEG file + $packed_data = get_TIFF_Packed_Data( $meta_data ); + if ( $packed_data === FALSE ) + { + return $jpeg_header_data; + } + + $packed_data = "Meta\x00\x00$packed_data"; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP1 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 ) + { + // And if it has the Meta label, + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) ) + { + // Found a preexisting Meta block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = $packed_data; + return $jpeg_header_data; + } + } + } + // No preexisting segment segment found, insert a new one at the start of the header data. + + // Determine highest position of an APP segment at or below APP3, so we can put the + // new APP3 at this position + + + $highest_APP = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // Check if we have found an APP segment at or below APP3, + if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) ) + { + // Found an APP segment at or below APP12 + $highest_APP = $i; + } + } + + // No preexisting Meta block found, insert a new one at the start of the header data. + array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xE3, + "SegName" => "APP3", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ], + "SegData" => $packed_data ) ) ); + return $jpeg_header_data; + +} + +/****************************************************************************** +* End of Function: put_Meta_JPEG +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: get_EXIF_TIFF +* +* Description: Retrieves information from a Exchangeable Image File Format (EXIF) +* within a TIFF file and returns it in an array. +* +* Parameters: filename - the filename of the TIFF image to process +* +* Returns: OutputArray - Array of EXIF records +* FALSE - If an error occured in decoding +* +******************************************************************************/ + +function get_EXIF_TIFF( $filename ) +{ + // Change: Added as of version 1.11 + // Check if a wrapper is being used - these are not currently supported (see notes at top of file) + if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) ) + { + // A HTTP or FTP wrapper is being used - show a warning and abort + echo "HTTP and FTP wrappers are currently not supported with TIFF - See EXIF/TIFF functionality documentation - a local file must be specified<br>"; + echo "To work on an internet file, copy it locally to start with:<br><br>\n"; + echo "\$newfilename = tempnam ( \$dir, \"tmptiff\" );<br>\n"; + echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n"; + return FALSE; + } + + + $filehnd = @fopen($filename, 'rb'); + + // Check if the file opened successfully + if ( ! $filehnd ) + { + // Could't open the file - exit + error_log( "Could not open file $filename" ); + return FALSE; + } + + // Decode the Exif segment into an array and return it + $exif_data = process_TIFF_Header( $filehnd, "TIFF" ); + + // Close File + fclose($filehnd); + return $exif_data; +} + +/****************************************************************************** +* End of Function: get_EXIF_TIFF +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: Interpret_EXIF_to_HTML +* +* Description: Generates html detailing the contents an APP1 EXIF array +* which was retrieved with a get_EXIF_.... function. +* Can also be used for APP3 Meta arrays. +* +* Parameters: Exif_array - the EXIF array,as read from get_EXIF_.... +* filename - the name of the Image file being processed ( used +* by scripts which displays EXIF thumbnails) +* +* Returns: output_str - A string containing the HTML +* +******************************************************************************/ + +function Interpret_EXIF_to_HTML( $Exif_array, $filename ) +{ + // Create the string to receive the html output + $output_str = ""; + + // Check if the array to process is valid + if ( $Exif_array === FALSE ) + { + // Exif Array is not valid - abort processing + return $output_str; + } + + // Ouput the heading according to what type of tags were used in processing + if ( $Exif_array[ 'Tags Name' ] == "TIFF" ) + { + $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains Exchangeable Image File Format (EXIF) Information</h2>\n"; + } + else if ( $Exif_array[ 'Tags Name' ] == "Meta" ) + { + $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains META Information (APP3)</h2>\n"; + } + else + { + $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains " . $Exif_array[ 'Tags Name' ] . " Information</h2>\n"; + } + + + // Check that there are actually items to process in the array + if ( count( $Exif_array ) < 1 ) + { + // No items to process in array - abort processing + return $output_str; + } + + // Output secondary heading + $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Main Image Information</h2>\n"; + + // Interpret the zeroth IFD to html + $output_str .= interpret_IFD( $Exif_array[0], $filename, $Exif_array['Byte_Align'] ); + + // Check if there is a first IFD to process + if ( array_key_exists( 1, $Exif_array ) ) + { + // There is a first IFD for a thumbnail + // Add a heading for it to the output + $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Thumbnail Information</h2>\n"; + + // Interpret the IFD to html and add it to the output + $output_str .= interpret_IFD( $Exif_array[1], $filename, $Exif_array['Byte_Align'] ); + } + + // Cycle through any other IFD's + $i = 2; + while ( array_key_exists( $i, $Exif_array ) ) + { + // Add a heading for the IFD + $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Image File Directory (IFD) $i Information</h2>\n"; + + // Interpret the IFD to html and add it to the output + $output_str .= interpret_IFD( $Exif_array[$i], $filename, $Exif_array['Byte_Align'] ); + $i++; + } + + // Return the resulting HTML + return $output_str; +} + +/****************************************************************************** +* End of Function: Interpret_EXIF_to_HTML +******************************************************************************/ + + + + + + + + + + + + + + + + +/****************************************************************************** +* +* INTERNAL FUNCTIONS +* +******************************************************************************/ + + + + + + + + + + + +/****************************************************************************** +* +* Internal Function: get_TIFF_Packed_Data +* +* Description: Packs TIFF IFD data from EXIF or Meta into a form ready for +* either a JPEG EXIF/Meta segment or a TIFF file +* This function attempts to protect the contents of an EXIF makernote, +* by ensuring that it remains in the same position relative to the +* TIFF header +* +* Parameters: tiff_data - the EXIF array,as read from get_EXIF_JPEG or get_Meta_JPEG +* +* Returns: packed_data - A string containing packed segment +* +******************************************************************************/ + +function get_TIFF_Packed_Data( $tiff_data ) +{ + // Check that the segment is valid + if ( $tiff_data === FALSE ) + { + return FALSE; + } + + // Get the byte alignment + $Byte_Align = $tiff_data['Byte_Align']; + + // Add the Byte Alignment to the Packed data + $packed_data = $Byte_Align; + + // Add the TIFF ID to the Packed Data + $packed_data .= put_IFD_Data_Type( 42, 3, $Byte_Align ); + + // Create a string for the makernote + $makernote = ""; + + // Check if the makernote exists + if ( $tiff_data[ 'Makernote_Tag' ] !== FALSE ) + { + // A makernote exists - We need to ensure that it stays in the same position as it was + // Put the Makernote before any of the IFD's by padding zeros to the correct offset + $makernote .= str_repeat("\x00",( $tiff_data[ 'Makernote_Tag' ][ 'Offset' ] - 8 ) ); + $makernote .= $tiff_data[ 'Makernote_Tag' ]['Data']; + } + + // Calculage where the zeroth ifd will be + $ifd_offset = strlen( $makernote ) + 8; + + // Add the Zeroth IFD pointer to the packed data + $packed_data .= put_IFD_Data_Type( $ifd_offset, 4, $Byte_Align ); + + // Add the makernote to the packed data (if there was one) + $packed_data .= $makernote; + + //Add the IFD's to the packed data + $packed_data .= get_IFD_Array_Packed_Data( $tiff_data, $ifd_offset, $Byte_Align ); + + // Return the result + return $packed_data; +} + +/****************************************************************************** +* End of Function: get_TIFF_Packed_Data +******************************************************************************/ + + + + +/****************************************************************************** +* +* Internal Function: get_IFD_Array_Packed_Data +* +* Description: Packs a chain of IFD's from EXIF or Meta segments into a form +* ready for either a JPEG EXIF/Meta segment or a TIFF file +* +* Parameters: ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG +* Zero_IFD_offset - The offset to the first IFD from the start of the TIFF header +* Byte_Align - the Byte alignment to use - "MM" or "II" +* +* Returns: packed_data - A string containing packed IFD's +* +******************************************************************************/ + +function get_IFD_Array_Packed_Data( $ifd_data, $Zero_IFD_offset, $Byte_Align ) +{ + // Create a string to receive the packed output + $packed_data = ""; + + // Count the IFDs + $ifd_count = 0; + foreach( $ifd_data as $key => $IFD ) + { + // Make sure we only count the IFD's, not other information keys + if ( is_numeric( $key ) ) + { + $ifd_count++; + } + } + + + // Cycle through each IFD, + for ( $ifdno = 0; $ifdno < $ifd_count; $ifdno++ ) + { + // Check if this IFD is the last one + if ( $ifdno == $ifd_count - 1 ) + { + // This IFD is the last one, get it's packed data + $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, FALSE ); + } + else + { + // This IFD is NOT the last one, get it's packed data + $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, TRUE ); + } + + } + + // Return the packed output + return $packed_data; +} + +/****************************************************************************** +* End of Function: get_IFD_Array_Packed_Data +******************************************************************************/ + + + +/****************************************************************************** +* +* Internal Function: get_IFD_Packed_Data +* +* Description: Packs an IFD from EXIF or Meta segments into a form +* ready for either a JPEG EXIF/Meta segment or a TIFF file +* +* Parameters: ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG +* IFD_offset - The offset to the IFD from the start of the TIFF header +* Byte_Align - the Byte alignment to use - "MM" or "II" +* Another_IFD - boolean - false if this is the last IFD in the chain +* - true if it is not the last +* +* Returns: packed_data - A string containing packed IFD's +* +******************************************************************************/ + +function get_IFD_Packed_Data( $ifd_data, $IFD_offset, $Byte_Align, $Another_IFD ) +{ + + $ifd_body_str = ""; + $ifd_data_str = ""; + + $Tag_Definitions_Name = $ifd_data[ 'Tags Name' ]; + + + // Count the Tags in this IFD + $tag_count = 0; + foreach( $ifd_data as $key => $tag ) + { + // Make sure we only count the Tags, not other information keys + if ( is_numeric( $key ) ) + { + $tag_count++; + } + } + + // Add the Tag count to the packed data + $packed_data = put_IFD_Data_Type( $tag_count, 3, $Byte_Align ); + + // Calculate the total length of the IFD (without the offset data) + $IFD_len = 2 + $tag_count * 12 + 4; + + + // Cycle through each tag + foreach( $ifd_data as $key => $tag ) + { + // Make sure this is a tag, not another information key + if ( is_numeric( $key ) ) + { + + // Add the tag number to the packed data + $ifd_body_str .= put_IFD_Data_Type( $tag[ 'Tag Number' ], 3, $Byte_Align ); + + // Add the Data type to the packed data + $ifd_body_str .= put_IFD_Data_Type( $tag['Data Type'], 3, $Byte_Align ); + + // Check if this is a Print Image Matching entry + if ( $tag['Type'] == "PIM" ) + { + // This is a Print Image Matching entry, + // encode it + $data = Encode_PIM( $tag, $Byte_Align ); + } + // Check if this is a IPTC/NAA Record within the EXIF IFD + else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $tag[ 'Tag Number' ] == 33723 ) ) + { + // This is a IPTC/NAA Record, encode it + $data = put_IPTC( $tag['Data'] ); + } + // Change: Check for embedded XMP as of version 1.11 + // Check if this is a XMP Record within the EXIF IFD + else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $tag[ 'Tag Number' ] == 700 ) ) + { + // This is a XMP Record, encode it + $data = write_XMP_array_to_text( $tag['Data'] ); + } + // Change: Check for embedded IRB as of version 1.11 + // Check if this is a Photoshop IRB Record within the EXIF IFD + else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $tag[ 'Tag Number' ] == 34377 ) ) + { + // This is a Photoshop IRB Record, encode it + $data = pack_Photoshop_IRB_Data( $tag['Data'] ); + } + // Exif Thumbnail Offset + else if ( ( $tag[ 'Tag Number' ] == 513 ) && ( $Tag_Definitions_Name == "TIFF" ) ) + { + // The Exif Thumbnail Offset is a pointer but of type Long, not Unknown + // Hence we need to put the data into the packed string separately + // Calculate the thumbnail offset + $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); + + // Create the Offset for the IFD + $data = put_IFD_Data_Type( $data_offset, 4, $Byte_Align ); + + // Store the thumbnail + $ifd_data_str .= $tag['Data']; + } + // Exif Thumbnail Length + else if ( ( $tag[ 'Tag Number' ] == 514 ) && ( $Tag_Definitions_Name == "TIFF" ) ) + { + // Encode the Thumbnail Length + $data = put_IFD_Data_Type( strlen($ifd_data[513]['Data']), 4, $Byte_Align ); + } + // Sub-IFD + else if ( $tag['Type'] == "SubIFD" ) + { + // This is a Sub-IFD + // Calculate the offset to the start of the Sub-IFD + $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); + // Get the packed data for the IFD chain as the data for this tag + $data = get_IFD_Array_Packed_Data( $tag['Data'], $data_offset, $Byte_Align ); + } + else + { + // Not a special tag + + // Create a string to receive the data + $data = ""; + + // Check if this is a type Unknown tag + if ( $tag['Data Type'] != 7 ) + { + // NOT type Unknown + // Cycle through each data value and add it to the data string + foreach( $tag[ 'Data' ] as $data_val ) + { + $data .= put_IFD_Data_Type( $data_val, $tag['Data Type'], $Byte_Align ); + } + } + else + { + // This is a type Unknown - just add the data as is to the data string + $data .= $tag[ 'Data' ]; + } + } + + // Pad the data string out to at least 4 bytes + $data = str_pad ( $data, 4, "\x00" ); + + + // Check if the data type is an ASCII String or type Unknown + if ( ( $tag['Data Type'] == 2 ) || ( $tag['Data Type'] == 7 ) ) + { + // This is an ASCII String or type Unknown + // Add the Length of the string to the packed data as the Count + $ifd_body_str .= put_IFD_Data_Type( strlen($data), 4, $Byte_Align ); + } + else + { + // Add the array count to the packed data as the Count + $ifd_body_str .= put_IFD_Data_Type( count($tag[ 'Data' ]), 4, $Byte_Align ); + } + + + // Check if the data is over 4 bytes long + if ( strlen( $data ) > 4 ) + { + // Data is longer than 4 bytes - it needs to be offset + // Check if this entry is the Maker Note + if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $tag[ 'Tag Number' ] == 37500 ) ) + { + // This is the makernote - It will have already been stored + // at its original offset to help preserve it + // all we need to do is add the Offset to the IFD packed data + $data_offset = $tag[ 'Offset' ]; + + $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align ); + } + else + { + // This is NOT the makernote + // Calculate the data offset + $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); + + // Add the offset to the IFD packed data + $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align ); + + // Add the data to the offset packed data + $ifd_data_str .= $data; + } + } + else + { + // Data is less than or equal to 4 bytes - Add it to the packed IFD data as is + $ifd_body_str .= $data; + } + + } + } + + // Assemble the IFD body onto the packed data + $packed_data .= $ifd_body_str; + + // Check if there is another IFD after this one + if( $Another_IFD === TRUE ) + { + // There is another IFD after this + // Calculate the Next-IFD offset so that it goes immediately after this IFD + $next_ifd_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str); + } + else + { + // There is NO IFD after this - indicate with offset=0 + $next_ifd_offset = 0; + } + + // Add the Next-IFD offset to the packed data + $packed_data .= put_IFD_Data_Type( $next_ifd_offset, 4, $Byte_Align ); + + // Add the offset data to the packed data + $packed_data .= $ifd_data_str; + + // Return the resulting packed data + return $packed_data; +} + +/****************************************************************************** +* End of Function: get_IFD_Packed_Data +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Internal Function: process_TIFF_Header +* +* Description: Decodes the information stored in a TIFF header and it's +* Image File Directories (IFD's). This information is returned +* in an array +* +* Parameters: filehnd - The handle of a open image file, positioned at the +* start of the TIFF header +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: OutputArray - Array of IFD records +* FALSE - If an error occured in decoding +* +******************************************************************************/ + +function process_TIFF_Header( $filehnd, $Tag_Definitions_Name ) +{ + + + // Save the file position where the TIFF header starts, as offsets are relative to this position + $Tiff_start_pos = ftell( $filehnd ); + + + + // Read the eight bytes of the TIFF header + $DataStr = network_safe_fread( $filehnd, 8 ); + + // Check that we did get all eight bytes + if ( strlen( $DataStr ) != 8 ) + { + return FALSE; // Couldn't read the TIFF header properly + } + + $pos = 0; + // First two bytes indicate the byte alignment - should be 'II' or 'MM' + // II = Intel (LSB first, MSB last - Little Endian) + // MM = Motorola (MSB first, LSB last - Big Endian) + $Byte_Align = substr( $DataStr, $pos, 2 ); + + + + // Check the Byte Align Characters for validity + if ( ( $Byte_Align != "II" ) && ( $Byte_Align != "MM" ) ) + { + // Byte align field is invalid - we won't be able to decode file + return FALSE; + } + + // Skip over the Byte Align field which was just read + $pos += 2; + + // Next two bytes are TIFF ID - should be value 42 with the appropriate byte alignment + $TIFF_ID = substr( $DataStr, $pos, 2 ); + + if ( get_IFD_Data_Type( $TIFF_ID, 3, $Byte_Align ) != 42 ) + { + // TIFF header ID not found + return FALSE; + } + + // Skip over the TIFF ID field which was just read + $pos += 2; + + + // Next four bytes are the offset to the first IFD + $offset_str = substr( $DataStr, $pos, 4 ); + $offset = get_IFD_Data_Type( $offset_str, 4, $Byte_Align ); + + // Done reading TIFF Header + + + // Move to first IFD + + if ( fseek( $filehnd, $Tiff_start_pos + $offset ) !== 0 ) + { + // Error seeking to position of first IFD + return FALSE; + } + + + + // Flag that a makernote has not been found yet + $GLOBALS[ "Maker_Note_Tag" ] = FALSE; + + // Read the IFD chain into an array + $Output_Array = read_Multiple_IFDs( $filehnd, $Tiff_start_pos, $Byte_Align, $Tag_Definitions_Name ); + + // Check if a makernote was found + if ( $GLOBALS[ "Maker_Note_Tag" ] != FALSE ) + { + // Makernote was found - Process it + // The makernote needs to be processed after all other + // tags as it may require some of the other tags in order + // to be processed properly + $GLOBALS[ "Maker_Note_Tag" ] = Read_Makernote_Tag( $GLOBALS[ "Maker_Note_Tag" ], $Output_Array, $filehnd ); + + } + + $Output_Array[ 'Makernote_Tag' ] = $GLOBALS[ "Maker_Note_Tag" ]; + + // Save the Name of the Tags used in the output array + $Output_Array[ 'Tags Name' ] = $Tag_Definitions_Name; + + + + // Save the Byte alignment + $Output_Array['Byte_Align'] = $Byte_Align; + + + // Return the output array + return $Output_Array ; +} + +/****************************************************************************** +* End of Function: process_TIFF_Header +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Internal Function: read_Multiple_IFDs +* +* Description: Reads and interprets a chain of standard Image File Directories (IFD's), +* and returns the entries in an array. This chain is made up from IFD's +* which have a pointer to the next IFD. IFD's are read until the next +* pointer indicates there are no more +* +* Parameters: filehnd - a handle for the image file being read, positioned at the +* start of the IFD chain +* Tiff_offset - The offset of the TIFF header from the start of the file +* Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment +* Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions +* local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry +* False (normal) indicates offests are relative to start of Tiff header as per IFD standard +* read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD +* False indicates that no pointer follows the IFD +* +* +* Returns: OutputArray - Array of IFD entries +* +******************************************************************************/ + +function read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE ) +{ + // Start at the offset of the first IFD + $Next_Offset = 0; + + do + { + // Read an IFD + list($IFD_Array , $Next_Offset) = read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets, $read_next_ptr ); + + // Move to the position of the next IFD + if ( fseek( $filehnd, $Tiff_offset + $Next_Offset ) !== 0 ) + { + // Error seeking to position of next IFD + error_log( "Error: Corrupted EXIF" ); + return FALSE; + } + + $Output_Array[] = $IFD_Array; + + + } while ( $Next_Offset != 0 ); // Until the Next IFD Offset is zero + + + // return resulting array + + return $Output_Array ; +} + +/****************************************************************************** +* End of Function: read_Multiple_IFDs +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Internal Function: read_IFD_universal +* +* Description: Reads and interprets a standard or Non-standard Image File +* Directory (IFD), and returns the entries in an array +* +* Parameters: filehnd - a handle for the image file being read, positioned at the start +* of the IFD +* Tiff_offset - The offset of the TIFF header from the start of the file +* Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment +* Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions +* local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry +* False (normal) indicates offests are relative to start of Tiff header as per IFD standard +* read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD +* False indicates that no pointer follows the IFD +* +* Returns: OutputArray - Array of IFD entries +* Next_Offset - Offset to next IFD (zero = no next IFD) +* +******************************************************************************/ + +function read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE ) +{ + if ( ( $filehnd == NULL ) || ( feof( $filehnd ) ) ) + { + return array (FALSE , 0); + } + + // Record the Name of the Tag Group used for this IFD in the output array + $OutputArray[ 'Tags Name' ] = $Tag_Definitions_Name; + + // Record the offset of the TIFF header in the output array + $OutputArray[ 'Tiff Offset' ] = $Tiff_offset; + + // First 2 bytes of IFD are number of entries in the IFD + $No_Entries_str = network_safe_fread( $filehnd, 2 ); + $No_Entries = get_IFD_Data_Type( $No_Entries_str, 3, $Byte_Align ); + + + // If the data is corrupt, the number of entries may be huge, which will cause errors + // This is often caused by a lack of a Next-IFD pointer + if ( $No_Entries> 10000 ) + { + // Huge number of entries - abort + // error_log( "Error: huge number of EXIF entries - EXIF is probably Corrupted" ); + + return array ( FALSE , 0); + } + + // If the data is corrupt or just stupid, the number of entries may zero, + // Indicate this by returning false + if ( $No_Entries === 0 ) + { + // No entries - abort + return array ( FALSE , 0); + } + + // Save the file position where first IFD record starts as non-standard offsets + // need to know this to calculate an absolute offset + $IFD_first_rec_pos = ftell( $filehnd ); + + + // Read in the IFD structure + $IFD_Data = network_safe_fread( $filehnd, 12 * $No_Entries ); + + // Check if the entire IFD was able to be read + if ( strlen( $IFD_Data ) != (12 * $No_Entries) ) + { + // Couldn't read the IFD Data properly, Some Casio files have no Next IFD pointer, hence cause this error + error_log( "Error: EXIF Corrupted" ); + return array(FALSE, 0); + } + + + // Last 4 bytes of a standard IFD are the offset to the next IFD + // Some NON-Standard IFD implementations do not have this, hence causing problems if it is read + + // If the Next IFD pointer has been requested to be read, + if ( $read_next_ptr ) + { + // Read the pointer to the next IFD + + $Next_Offset_str = network_safe_fread( $filehnd, 4 ); + $Next_Offset = get_IFD_Data_Type( $Next_Offset_str, 4, $Byte_Align ); + } + else + { + // Otherwise set the pointer to zero ( no next IFD ) + $Next_Offset = 0; + } + + + + // Initialise current position to the start + $pos = 0; + + + // Loop for reading IFD entries + + for ( $i = 0; $i < $No_Entries; $i++ ) + { + // First 2 bytes of IFD entry are the tag number ( Unsigned Short ) + $Tag_No_str = substr( $IFD_Data, $pos, 2 ); + $Tag_No = get_IFD_Data_Type( $Tag_No_str, 3, $Byte_Align ); + $pos += 2; + + // Next 2 bytes of IFD entry are the data format ( Unsigned Short ) + $Data_Type_str = substr( $IFD_Data, $pos, 2 ); + $Data_Type = get_IFD_Data_Type( $Data_Type_str, 3, $Byte_Align ); + $pos += 2; + + // If Datatype is not between 1 and 12, then skip this entry, it is probably corrupted or custom + if (( $Data_Type > 12 ) || ( $Data_Type < 1 ) ) + { + $pos += 8; + continue 1; // Stop trying to process the tag any further and skip to the next one + } + + // Next 4 bytes of IFD entry are the data count ( Unsigned Long ) + $Data_Count_str = substr( $IFD_Data, $pos, 4 ); + $Data_Count = get_IFD_Data_Type( $Data_Count_str, 4, $Byte_Align ); + $pos += 4; + + if ( $Data_Count > 100000 ) + { + // error_log( "Error: huge EXIF data count - EXIF is probably Corrupted" ); + + // Some Casio files have no Next IFD pointer, hence cause errors + + return array ( FALSE , 0); + } + + // Total Data size is the Data Count multiplied by the size of the Data Type + $Total_Data_Size = $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] * $Data_Count; + + $Data_Start_pos = -1; + + // If the total data size is larger than 4 bytes, then the data part is the offset to the real data + if ( $Total_Data_Size > 4 ) + { + // Not enough room for data - offset provided instead + $Data_Offset_str = substr( $IFD_Data, $pos, 4 ); + $Data_Start_pos = get_IFD_Data_Type( $Data_Offset_str, 4, $Byte_Align ); + + + // In some NON-STANDARD makernotes, the offset is relative to the start of the current IFD entry + if ( $local_offsets ) + { + // This is a NON-Standard IFD, seek relative to the start of the current tag + fseek( $filehnd, $IFD_first_rec_pos + $pos - 8 + $Data_Start_pos ); + } + else + { + // This is a normal IFD, seek relative to the start of the TIFF header + fseek( $filehnd, $Tiff_offset + $Data_Start_pos ); + } + + // Read the data block from the offset position + $DataStr = network_safe_fread( $filehnd, $Total_Data_Size ); + } + else + { + // The data block is less than 4 bytes, and is provided in the IFD entry, so read it + $DataStr = substr( $IFD_Data, $pos, $Total_Data_Size ); + } + + // Increment the position past the data + $pos += 4; + + + // Now create the entry for output array + + $Data_Array = array( ); + + + // Read the data items from the data block + + if ( ( $Data_Type != 2 ) && ( $Data_Type != 7 ) ) + { + // The data type is Numerical, Read the data items from the data block + for ( $j = 0; $j < $Data_Count; $j++ ) + { + $Part_Data_Str = substr( $DataStr, $j * $GLOBALS['IFD_Data_Sizes'][ $Data_Type ], $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] ); + $Data_Array[] = get_IFD_Data_Type( $Part_Data_Str, $Data_Type, $Byte_Align ); + } + } + elseif ( $Data_Type == 2 ) + { + // The data type is String(s) (type 2) + + // Strip the last terminating Null + $DataStr = substr( $DataStr, 0, strlen($DataStr)-1 ); + + // Split the data block into multiple strings whereever there is a Null + $Data_Array = explode( "\x00", $DataStr ); + } + else + { + // The data type is Unknown (type 7) + // Do nothing to data + $Data_Array = $DataStr; + } + + + // If this is a Sub-IFD entry, + if ( ( array_key_exists( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name] ) ) && + ( "SubIFD" == $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'] ) ) + { + // This is a Sub-IFD entry, go and process the data forming Sub-IFD and use its output array as the new data for this entry + fseek( $filehnd, $Tiff_offset + $Data_Array[0] ); + $Data_Array = read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Tags Name'] ); + } + + $desc = ""; + $units = ""; + + // Check if this tag exists in the list of tag definitions, + + if ( array_key_exists ( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name]) ) + { + + if ( array_key_exists ( 'Description', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) ) + { + $desc = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Description']; + } + + if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) ) + { + $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Units']; + } + + // Tag exists in definitions, append details to output array + $OutputArray[ $Tag_No ] = array ( "Tag Number" => $Tag_No, + "Tag Name" => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Name'], + "Tag Description" => $desc, + "Data Type" => $Data_Type, + "Type" => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'], + "Units" => $units, + "Data" => $Data_Array ); + + } + else + { + // Tag doesnt exist in definitions, append unknown details to output array + + $OutputArray[ $Tag_No ] = array ( "Tag Number" => $Tag_No, + "Tag Name" => "Unknown Tag #" . $Tag_No, + "Tag Description" => "", + "Data Type" => $Data_Type, + "Type" => "Unknown", + "Units" => "", + "Data" => $Data_Array ); + } + + + + // Some information of type "Unknown" (type 7) might require information about + // how it's position and byte alignment in order to be decoded + if ( $Data_Type == 7 ) + { + $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos; + $OutputArray[ $Tag_No ]['Byte Align'] = $Byte_Align; + } + + + //////////////////////////////////////////////////////////////////////// + // Special Data handling + //////////////////////////////////////////////////////////////////////// + + + // Check if this is a Print Image Matching entry + if ( $OutputArray[ $Tag_No ]['Type'] == "PIM" ) + { + // This is a Print Image Matching entry, decode it. + $OutputArray[ $Tag_No ] = Decode_PIM( $OutputArray[ $Tag_No ], $Tag_Definitions_Name ); + } + + + // Interpret the entry into a text string using a custom interpreter + $text_val = get_Tag_Text_Value( $OutputArray[ $Tag_No ], $Tag_Definitions_Name ); + + // Check if a text string was generated + if ( $text_val !== FALSE ) + { + // A string was generated, append it to the output array entry + $OutputArray[ $Tag_No ]['Text Value'] = $text_val; + $OutputArray[ $Tag_No ]['Decoded'] = TRUE; + } + else + { + // A string was NOT generated, append a generic string to the output array entry + $OutputArray[ $Tag_No ]['Text Value'] = get_IFD_value_as_text( $OutputArray[ $Tag_No ] ) . " " . $units; + $OutputArray[ $Tag_No ]['Decoded'] = FALSE; + } + + + + + // Check if this entry is the Maker Note + if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $Tag_No == 37500 ) ) + { + + // Save some extra information which will allow Makernote Decoding with the output array entry + $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos; + $OutputArray[ $Tag_No ][ 'Tiff Offset' ] = $Tiff_offset; + $OutputArray[ $Tag_No ]['ByteAlign'] = $Byte_Align; + + // Save a pointer to this entry for Maker note processing later + $GLOBALS[ "Maker_Note_Tag" ] = & $OutputArray[ $Tag_No ]; + } + + + // Check if this is a IPTC/NAA Record within the EXIF IFD + if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $Tag_No == 33723 ) ) + { + // This is a IPTC/NAA Record, interpret it and put result in the data for this entry + $OutputArray[ $Tag_No ]['Data'] = get_IPTC( $DataStr ); + $OutputArray[ $Tag_No ]['Decoded'] = TRUE; + } + // Change: Check for embedded XMP as of version 1.11 + // Check if this is a XMP Record within the EXIF IFD + if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $Tag_No == 700 ) ) + { + // This is a XMP Record, interpret it and put result in the data for this entry + $OutputArray[ $Tag_No ]['Data'] = read_XMP_array_from_text( $DataStr ); + $OutputArray[ $Tag_No ]['Decoded'] = TRUE; + } + + // Change: Check for embedded IRB as of version 1.11 + // Check if this is a Photoshop IRB Record within the EXIF IFD + if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) && + ( $Tag_No == 34377 ) ) + { + // This is a Photoshop IRB Record, interpret it and put result in the data for this entry + $OutputArray[ $Tag_No ]['Data'] = unpack_Photoshop_IRB_Data( $DataStr ); + $OutputArray[ $Tag_No ]['Decoded'] = TRUE; + } + + // Exif Thumbnail + // Check that both the thumbnail length and offset entries have been processed, + // and that this is one of them + if ( ( ( ( $Tag_No == 513 ) && ( array_key_exists( 514, $OutputArray ) ) ) || + ( ( $Tag_No == 514 ) && ( array_key_exists( 513, $OutputArray ) ) ) ) && + ( $Tag_Definitions_Name == "TIFF" ) ) + { + // Seek to the start of the thumbnail using the offset entry + fseek( $filehnd, $Tiff_offset + $OutputArray[513]['Data'][0] ); + + // Read the thumbnail data, and replace the offset data with the thumbnail + $OutputArray[513]['Data'] = network_safe_fread( $filehnd, $OutputArray[514]['Data'][0] ); + } + + + // Casio Thumbnail + // Check that both the thumbnail length and offset entries have been processed, + // and that this is one of them + if ( ( ( ( $Tag_No == 0x0004 ) && ( array_key_exists( 0x0003, $OutputArray ) ) ) || + ( ( $Tag_No == 0x0003 ) && ( array_key_exists( 0x0004, $OutputArray ) ) ) ) && + ( $Tag_Definitions_Name == "Casio Type 2" ) ) + { + // Seek to the start of the thumbnail using the offset entry + fseek( $filehnd, $Tiff_offset + $OutputArray[0x0004]['Data'][0] ); + + // Read the thumbnail data, and replace the offset data with the thumbnail + $OutputArray[0x0004]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0003]['Data'][0] ); + } + + // Minolta Thumbnail + // Check that both the thumbnail length and offset entries have been processed, + // and that this is one of them + if ( ( ( ( $Tag_No == 0x0088 ) && ( array_key_exists( 0x0089, $OutputArray ) ) ) || + ( ( $Tag_No == 0x0089 ) && ( array_key_exists( 0x0088, $OutputArray ) ) ) ) && + ( $Tag_Definitions_Name == "Olympus" ) ) + { + + // Seek to the start of the thumbnail using the offset entry + fseek( $filehnd, $Tiff_offset + $OutputArray[0x0088]['Data'][0] ); + + // Read the thumbnail data, and replace the offset data with the thumbnail + $OutputArray[0x0088]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0089]['Data'][0] ); + + // Sometimes the minolta thumbnail data is empty (or the offset is corrupt, which results in the same thing) + + // Check if the thumbnail data exists + if ( $OutputArray[0x0088]['Data'] != "" ) + { + // Thumbnail exists + + // Minolta Thumbnails are missing their first 0xFF for some reason, + // which is replaced with some weird character, so fix this + $OutputArray[0x0088]['Data']{0} = "\xFF"; + } + else + { + // Thumbnail doesnt exist - make it obvious + $OutputArray[0x0088]['Data'] = FALSE; + } + } + + } + + + + + + + + // Return the array of IFD entries and the offset to the next IFD + + return array ($OutputArray , $Next_Offset); +} + + + +/****************************************************************************** +* End of Function: read_IFD_universal +******************************************************************************/ + + + + + + + + + + + + +/****************************************************************************** +* +* Internal Function: get_Tag_Text_Value +* +* Description: Attempts to interpret an IFD entry into a text string using the +* information in the IFD_Tag_Definitions global array. +* +* Parameters: Tag - The IFD entry to process +* Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array +* +* Returns: String - if the tag was successfully decoded into a text string +* FALSE - if the tag could not be decoded using the information +* in the IFD_Tag_Definitions global array +* +******************************************************************************/ + +function get_Tag_Text_Value( $Tag, $Tag_Definitions_Name ) +{ + // Check what format the entry is specified as + + if ( $Tag['Type'] == "String" ) + { + // Format is Text String + + // If "Unknown" (type 7) data type, + if ( $Tag['Data Type'] == 7 ) + { + // Return data as is. + return $Tag['Data']; + } + else + { + // Otherwise return the default string value of the datatype + return get_IFD_value_as_text( $Tag ); + } + } + else if ( $Tag['Type'] == "Character Coded String" ) + { + // Format is Character Coded String (First 8 characters indicate coding scheme) + + // Convert Data to a string + if ( $Tag['Data Type'] == 7 ) + { + // If it is type "Unknown" (type 7) use data as is + $data = $Tag['Data']; + } + else + { + // Otherwise use the default string value of the datatype + $data = get_IFD_value_as_text( $Tag ); + } + + // Some implementations allow completely data with no Coding Scheme Name, + // so we need to handle this to avoid errors + if ( trim( $data ) == "" ) + { + return ""; + } + + // Extract the Coding Scheme Name from the first 8 characters + $char_code = substr( $data, 0, 8 ); + + // Extract the Data part from after the first 8 characters + $characters = substr( $data, 8 ); + + // Check coding scheme and interpret as neccessary + + if ( $char_code === "ASCII\x00\x00\x00" ) + { + // ASCII coding - return data as is. + return $characters; + } + elseif ( ( $char_code === "UNICODE\x00" ) || + ( $char_code === "Unicode\x00" ) ) // Note lowercase is non standard + { + // Unicode coding - interpret and return result. + return xml_UTF16_clean( $characters, TRUE ); + } + else + { + // Unknown coding - return string indicating this + return "Unsupported character coding : \"$char_code\"\n\"" . trim($characters) . "\""; + } + break; + } + else if ( $Tag['Type'] == "Numeric" ) + { + // Format is numeric - return default text value with any required units text appended + if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) ) + { + $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ]['Units']; + } + else + { + $units = ""; + } + return get_IFD_value_as_text( $Tag ) . " " . $units; + } + else if ( $Tag['Type'] == "Lookup" ) + { + // Format is a Lookup Table + + // Get a numeric value to use in lookup + + if ( is_array( $Tag['Data'] ) ) + { + // If data is an array, use first element + $first_val = $Tag['Data'][0]; + } + else if ( is_string( $Tag['Data'] ) ) + { + // If data is a string, use the first character + $first_val = ord($Tag['Data']{0}); + } + else + { + // Otherwise use the data as is + $first_val = $Tag['Data']; + } + + // Check if the data value exists in the lookup table for this IFD entry + if ( array_key_exists( $first_val, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) ) + { + // Data value exists in lookup table - return the matching string + return $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ][ $first_val ]; + } + else + { + // Data value doesnt exist in lookup table - return explanation string + return "Unknown Reserved value $first_val "; + } + } + else if ( $Tag['Type'] == "Special" ) + { + // Format is special - interpret to text with special handlers + return get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name ); + } + else if ( $Tag['Type'] == "PIM" ) + { + // Format is Print Image Matching info - interpret with custom handler + return get_PIM_Text_Value( $Tag, $Tag_Definitions_Name ); + } + else if ( $Tag['Type'] == "SubIFD" ) + { + // Format is a Sub-IFD - this has no text value + return ""; + } + else + { + // Unknown Format - Couldn't interpret using the IFD_Tag_Definitions global array information + return FALSE; + } +} + +/****************************************************************************** +* End of Function: get_Tag_Text_Value +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Internal Function: get_Special_Tag_Text_Value +* +* Description: Interprets an IFD entry marked as "Special" in the IFD_Tag_Definitions +* global array into a text string using custom handlers +* +* Parameters: Tag - The IFD entry to process +* Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array +* +* Returns: String - if the tag was successfully decoded into a text string +* FALSE - if the tag could not be decoded +* +******************************************************************************/ + +function get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name ) +{ + // Check what type of IFD is being decoded + + if ( $Tag_Definitions_Name == "TIFF" ) + { + // This is a TIFF IFD (bottom level) + + // Check what tag number the IFD entry has. + switch ( $Tag['Tag Number'] ) + { + case 530: // YCbCr Sub Sampling Entry + + // Data contains two numerical values + + if ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 1 ) ) + { + // Values are 2,1 - hence YCbCr 4:2:2 + return "YCbCr 4:2:2 ratio of chrominance components to the luminance components"; + } + elseif ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 2 ) ) + { + // Values are 2,2 - hence YCbCr 4:2:0 + return "YCbCr 4:2:0 ratio of chrominance components to the luminance components"; + } + else + { + // Other values are unknown + return "Unknown Reserved value (" . $Tag['Data'][0] . ")"; + } + break; + + default: + return FALSE; + } + } + else if ( $Tag_Definitions_Name == "EXIF" ) + { + // This is an EXIF IFD + + // Check what tag number the IFD entry has. + switch ( $Tag['Tag Number'] ) + { + + case 37121: // Components configuration + + // Data contains 4 numerical values indicating component type + + $output_str = ""; + + // Cycle through each component + for ( $Num = 0; $Num < 4; $Num++ ) + { + // Construct first part of text string + $output_str .= "Component " . ( $Num + 1 ) . ": "; + + // Construct second part of text string via + // lookup using numerical value + + $value = ord( $Tag['Data']{$Num} ); + switch( $value ) + { + case 0: + $output_str .= "Does not exist\n"; + break; + case 1: + $output_str .= "Y (Luminance)\n"; + break; + case 2: + $output_str .= "Cb (Chroma minus Blue)\n"; + break; + case 3: + $output_str .= "Cr (Chroma minus Red)\n"; + break; + case 4: + $output_str .= "Red\n"; + break; + case 5: + $output_str .= "Green\n"; + break; + case 6: + $output_str .= "Blue\n"; + break; + default: + $output_str .= "Unknown value $value\n"; + }; + } + + // Return the completed string + + return $output_str; + break; + + + + case 41730: // Colour Filter Array Pattern + + // The first two characters are a SHORT for Horizontal repeat pixel unit - + $n_max = get_IFD_Data_Type( substr( $Tag['Data'], 0, 2 ), 3, $Tag['Byte Align'] ); + + // The next two characters are a SHORT for Vertical repeat pixel unit - + $m_max = get_IFD_Data_Type( substr( $Tag['Data'], 2, 2 ), 3, $Tag['Byte Align'] ); + + + // At least one camera type appears to have byte reversed values for N_Max and M_Max + // Check if they need reversing + if ( $n_max > 256 ) + { + $n_max = $n_max/256 + 256*($n_max%256); + } + + if ( $m_max > 256 ) + { + $m_max = $m_max/256 + 256*($m_max%256); + } + + + $output_str = ""; + + + // Cycle through all the elements in the resulting 2 dimensional array, + for( $m = 1; $m <= $m_max; $m++ ) + { + for( $n = 1; $n <= $n_max; $n++ ) + { + + // Append text from a lookup table according to + // the value read for this element + if( isset( $Tag['Data']{($n_max*($m-1)+$n+3)} ) ) { + switch ( ord($Tag['Data']{($n_max*($m-1)+$n+3)}) ) + { + case 0: + $output_str .= "RED "; + break; + case 1: + $output_str .= "GREEN "; + break; + case 2: + $output_str .= "BLUE "; + break; + case 3: + $output_str .= "CYAN "; + break; + case 4: + $output_str .= "MAGENTA "; + break; + case 5: + $output_str .= "YELLOW "; + break; + case 6: + $output_str .= "WHITE "; + break; + default: + $output_str .= "Unknown "; + break; + }; + } + }; + $output_str .= "\n"; + }; + + // Return the resulting string + return $output_str; + break; + + default: + return FALSE; + } + } + else + { + // Unknown IFD type, see if it is part of a makernote + return get_Makernote_Text_Value( $Tag, $Tag_Definitions_Name ); + } + + +} + +/****************************************************************************** +* End of Function: get_Tag_Text_Value +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: interpret_IFD +* +* Description: Generates html detailing the contents a single IFD. +* +* Parameters: IFD_array - the array containing an IFD +* filename - the name of the Image file being processed ( used +* by scripts which displays EXIF thumbnails) +* +* Returns: output_str - A string containing the HTML +* +******************************************************************************/ + +function interpret_IFD( $IFD_array, $filename ) +{ + // Create the output string with the table tag + $output_str = "<table class=\"EXIF_Table\" border=1>\n"; + + // Create an extra output string to receive any supplementary html + // which cannot go inside the table + $extra_IFD_str = ""; + + // Check that the IFD array is valid + if ( ( $IFD_array === FALSE ) || ( $IFD_array === NULL ) ) + { + // the IFD array is NOT valid - exit + return ""; + } + + // Check if this is an EXIF IFD and if there is a makernote present + if ( ( $IFD_array['Tags Name'] === "EXIF" ) && + ( ! array_key_exists( 37500, $IFD_array ) ) ) + { + + // This is an EXIF IFD but NO makernote is present - Add a message to the output + $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">No Makernote Present</h3>"; + } + + // Cycle through each tag in the IFD + + foreach( $IFD_array as $Tag_ID => $Exif_Tag ) + { + + // Ignore the non numeric elements - they aren't tags + if ( ! is_numeric ( $Tag_ID ) ) + { + // Skip Tags Name + } + // Check if the Tag has been decoded successfully + else if ( $Exif_Tag['Decoded'] == TRUE ) + { + // This tag has been successfully decoded + + // Table cells won't get drawn with nothing in them - + // Ensure that at least a non breaking space exists in them + + if ( trim($Exif_Tag['Text Value']) == "" ) + { + $Exif_Tag['Text Value'] = " "; + } + + // Check if the tag is a sub-IFD + if ( $Exif_Tag['Type'] == "SubIFD" ) + { + // This is a sub-IFD tag + // Add a sub-heading for the sub-IFD + $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">" . $Exif_Tag['Tag Name'] . " contents</h3>"; + + // Cycle through each sub-IFD in the chain + foreach ( $Exif_Tag['Data'] as $subIFD ) + { + // Interpret this sub-IFD and add the html to the secondary output + $extra_IFD_str .= interpret_IFD( $subIFD, $filename ); + } + } + // Check if the tag is a makernote + else if ( $Exif_Tag['Type'] == "Maker Note" ) + { + // This is a Makernote Tag + // Add a sub-heading for the Makernote + $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Maker Note Contents</h3>"; + + // Interpret the Makernote and add the html to the secondary output + $extra_IFD_str .= Interpret_Makernote_to_HTML( $Exif_Tag, $filename ); + } + // Check if this is a IPTC/NAA Record within the EXIF IFD + else if ( $Exif_Tag['Type'] == "IPTC" ) + { + // This is a IPTC/NAA Record, interpret it and output to the secondary html + $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains IPTC/NAA Embedded in EXIF</h3>"; + $extra_IFD_str .=Interpret_IPTC_to_HTML( $Exif_Tag['Data'] ); + } + // Change: Check for embedded XMP as of version 1.11 + // Check if this is a XMP Record within the EXIF IFD + else if ( $Exif_Tag['Type'] == "XMP" ) + { + // This is a XMP Record, interpret it and output to the secondary html + $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains XMP Embedded in EXIF</h3>"; + $extra_IFD_str .= Interpret_XMP_to_HTML( $Exif_Tag['Data'] ); + } + // Change: Check for embedded IRB as of version 1.11 + // Check if this is a Photoshop IRB Record within the EXIF IFD + else if ( $Exif_Tag['Type'] == "IRB" ) + { + // This is a Photoshop IRB Record, interpret it and output to the secondary html + $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains Photoshop IRB Embedded in EXIF</h3>"; + $extra_IFD_str .= Interpret_IRB_to_HTML( $Exif_Tag['Data'], $filename ); + } + // Check if the tag is Numeric + else if ( $Exif_Tag['Type'] == "Numeric" ) + { + // Numeric Tag - Output text value as is. + $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\">" . $Exif_Tag['Text Value'] . "</td></tr>\n"; + } + else + { + // Other tag - Output text as preformatted + $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\"><pre>" . trim( $Exif_Tag['Text Value']) . "</pre></td></tr>\n"; + } + + } + else + { + // Tag has NOT been decoded successfully + // Hence it is either an unknown tag, or one which + // requires processing at the time of html construction + + // Table cells won't get drawn with nothing in them - + // Ensure that at least a non breaking space exists in them + + if ( trim($Exif_Tag['Text Value']) == "" ) + { + $Exif_Tag['Text Value'] = " "; + } + + // Check if this tag is the first IFD Thumbnail + if ( ( $IFD_array['Tags Name'] == "TIFF" ) && + ( $Tag_ID == 513 ) ) + { + // This is the first IFD thumbnail - Add html to the output + + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/get_exif_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ); + + // Add thumbnail link to html + $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\"><a class=\"EXIF_First_IFD_Thumb_Link\" href=\"$link_str\"><img class=\"EXIF_First_IFD_Thumb\" src=\"$link_str\"></a></td></tr>\n"; + } + // Check if this is the Makernote + else if ( $Exif_Tag['Type'] == "Maker Note" ) + { + // This is the makernote, but has not been decoded + // Add a message to the secondary output + $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Makernote Coding Unknown</h3>\n"; + } + else + { + // This is an Unknown Tag + + // Check if the user wants to hide unknown tags + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] === FALSE ) + { + // User wants to display unknown tags + + // Check if the Data is an ascii string + if ( $Exif_Tag['Data Type'] == 2 ) + { + // This is a Ascii String field - add it preformatted to the output + $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\"><pre>" . trim( $Exif_Tag['Text Value'] ) . "</pre></td></tr>\n"; + } + else + { + // Not an ASCII string - add it as is to the output + $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\">" . trim( $Exif_Tag['Text Value'] ) . "</td></tr>\n"; + } + } + } + } + } + + // Close the table in the output + $output_str .= "</table>\n"; + + // Add the secondary output at the end of the main output + $output_str .= "$extra_IFD_str\n"; + + // Return the resulting html + return $output_str; +} + +/****************************************************************************** +* End of Function: interpret_IFD +******************************************************************************/ + + + + + + + + + + + + + + + +/****************************************************************************** +* +* Function: get_IFD_Data_Type +* +* Description: Decodes an IFD field value from a binary data string, using +* information supplied about the data type and byte alignment of +* the stored data. +* This function should be used for all datatypes except ASCII strings +* +* Parameters: input_data - a binary data string containing the IFD value, +* must be exact length of the value +* data_type - a number representing the IFD datatype as per the +* TIFF 6.0 specification: +* 1 = Unsigned 8-bit Byte +* 2 = ASCII String +* 3 = Unsigned 16-bit Short +* 4 = Unsigned 32-bit Long +* 5 = Unsigned 2x32-bit Rational +* 6 = Signed 8-bit Byte +* 7 = Undefined +* 8 = Signed 16-bit Short +* 9 = Signed 32-bit Long +* 10 = Signed 2x32-bit Rational +* 11 = 32-bit Float +* 12 = 64-bit Double +* Byte_Align - Indicates the byte alignment of the data. +* MM = Motorola, MSB first, Big Endian +* II = Intel, LSB first, Little Endian +* +* Returns: output - the value of the data (string or numeric) +* +******************************************************************************/ + +function get_IFD_Data_Type( $input_data, $data_type, $Byte_Align ) +{ + $value = NULL; + if( !empty( $input_data ) ) { + // Check if this is a Unsigned Byte, Unsigned Short or Unsigned Long + if (( $data_type == 1 ) || ( $data_type == 3 ) || ( $data_type == 4 )) + { + // This is a Unsigned Byte, Unsigned Short or Unsigned Long + + // Check the byte alignment to see if the bytes need tp be reversed + if ( $Byte_Align == "II" ) + { + // This is in Intel format, reverse it + $input_data = strrev ( $input_data ); + } + + // Convert the binary string to a number and return it + return hexdec( bin2hex( $input_data ) ); + } + // Check if this is a ASCII string type + elseif ( $data_type == 2 ) + { + // Null terminated ASCII string(s) + // The input data may represent multiple strings, as the + // 'count' field represents the total bytes, not the number of strings + // Hence this should not be processed here, as it would have + // to return multiple values instead of a single value + + error_log( "Error - ASCII Strings should not be processed in get_IFD_Data_Type" ); + return "Error Should never get here"; //explode( "\x00", $input_data ); + } + // Check if this is a Unsigned rational type + elseif ( $data_type == 5 ) + { + // This is a Unsigned rational type + + // Check the byte alignment to see if the bytes need to be reversed + if ( $Byte_Align == "MM" ) + { + // Motorola MSB first byte aligment + // Unpack the Numerator and denominator and return them + return @unpack( 'NNumerator/NDenominator', $input_data ); + } + else + { + // Intel LSB first byte aligment + // Unpack the Numerator and denominator and return them + return @unpack( 'VNumerator/VDenominator', $input_data ); + } + } + // Check if this is a Signed Byte, Signed Short or Signed Long + elseif ( ( $data_type == 6 ) || ( $data_type == 8 ) || ( $data_type == 9 ) ) + { + // This is a Signed Byte, Signed Short or Signed Long + + // Check the byte alignment to see if the bytes need to be reversed + if ( $Byte_Align == "II" ) + { + //Intel format, reverse the bytes + $input_data = strrev ( $input_data ); + } + + // Convert the binary string to an Unsigned number + $value = hexdec( bin2hex( $input_data ) ); + + // Convert to signed number + + // Check if it is a Byte above 128 (i.e. a negative number) + if ( ( $data_type == 6 ) && ( $value > 128 ) ) + { + // number should be negative - make it negative + return $value - 256; + } + + // Check if it is a Short above 32767 (i.e. a negative number) + if ( ( $data_type == 8 ) && ( $value > 32767 ) ) + { + // number should be negative - make it negative + return $value - 65536; + } + + // Check if it is a Long above 2147483648 (i.e. a negative number) + if ( ( $data_type == 9 ) && ( $value > 2147483648 ) ) + { + // number should be negative - make it negative + return $value - 4294967296; + } + + // Return the signed number + return $value; + } + // Check if this is Undefined type + elseif ( $data_type == 7 ) + { + // Custom Data - Do nothing + return $input_data; + } + // Check if this is a Signed Rational type + elseif ( $data_type == 10 ) + { + // This is a Signed Rational type + + // Signed Long not available with endian in unpack , use unsigned and convert + + // Check the byte alignment to see if the bytes need to be reversed + if ( $Byte_Align == "MM" ) + { + // Motorola MSB first byte aligment + // Unpack the Numerator and denominator + $value = @unpack( 'NNumerator/NDenominator', $input_data ); + } + else + { + // Intel LSB first byte aligment + // Unpack the Numerator and denominator + $value = @unpack( 'VNumerator/VDenominator', $input_data ); + } + + // Convert the numerator to a signed number + // Check if it is above 2147483648 (i.e. a negative number) + if ( $value['Numerator'] > 2147483648 ) + { + // number is negative + $value['Numerator'] -= 4294967296; + } + + // Convert the denominator to a signed number + // Check if it is above 2147483648 (i.e. a negative number) + if ( $value['Denominator'] > 2147483648 ) + { + // number is negative + $value['Denominator'] -= 4294967296; + } + + // Return the Signed Rational value + return $value; + } + // Check if this is a Float type + elseif ( $data_type == 11 ) + { + // IEEE 754 Float + // TODO - EXIF - IFD datatype Float not implemented yet + return "FLOAT NOT IMPLEMENTED YET"; + } + // Check if this is a Double type + elseif ( $data_type == 12 ) + { + // IEEE 754 Double + // TODO - EXIF - IFD datatype Double not implemented yet + return "DOUBLE NOT IMPLEMENTED YET"; + } + else + { + // Error - Invalid Datatype + return "Invalid Datatype $data_type"; + + } + } else { + return "Invalid Datatype $data_type"; + } + +} + +/****************************************************************************** +* End of Function: get_IFD_Data_Type +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: put_IFD_Data_Type +* +* Description: Encodes an IFD field from a value to a binary data string, using +* information supplied about the data type and byte alignment of +* the stored data. +* +* Parameters: input_data - an IFD data value, numeric or string +* data_type - a number representing the IFD datatype as per the +* TIFF 6.0 specification: +* 1 = Unsigned 8-bit Byte +* 2 = ASCII String +* 3 = Unsigned 16-bit Short +* 4 = Unsigned 32-bit Long +* 5 = Unsigned 2x32-bit Rational +* 6 = Signed 8-bit Byte +* 7 = Undefined +* 8 = Signed 16-bit Short +* 9 = Signed 32-bit Long +* 10 = Signed 2x32-bit Rational +* 11 = 32-bit Float +* 12 = 64-bit Double +* Byte_Align - Indicates the byte alignment of the data. +* MM = Motorola, MSB first, Big Endian +* II = Intel, LSB first, Little Endian +* +* Returns: output - the packed binary string of the data +* +******************************************************************************/ + +function put_IFD_Data_Type( $input_data, $data_type, $Byte_Align ) +{ + // Process according to the datatype + switch ( $data_type ) + { + case 1: // Unsigned Byte - return character as is + return chr($input_data); + break; + + case 2: // ASCII String + // Return the string with terminating null + return $input_data . "\x00"; + break; + + case 3: // Unsigned Short + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the short and return + return pack( "v", $input_data ); + } + else + { + // Motorola/Big Endian - pack the short and return + return pack( "n", $input_data ); + } + break; + + case 4: // Unsigned Long + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the long and return + return pack( "V", $input_data ); + } + else + { + // Motorola/Big Endian - pack the long and return + return pack( "N", $input_data ); + } + break; + + case 5: // Unsigned Rational + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the two longs and return + return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] ); + } + else + { + // Motorola/Big Endian - pack the two longs and return + return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] ); + } + break; + + case 6: // Signed Byte + // Check if number is negative + if ( $input_data < 0 ) + { + // Number is negative - return signed character + return chr( $input_data + 256 ); + } + else + { + // Number is positive - return character + return chr( $input_data ); + } + break; + + case 7: // Unknown - return as is + return $input_data; + break; + + case 8: // Signed Short + // Check if number is negative + if ( $input_data < 0 ) + { + // Number is negative - make signed value + $input_data = $input_data + 65536; + } + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the short and return + return pack( "v", $input_data ); + } + else + { + // Motorola/Big Endian - pack the short and return + return pack( "n", $input_data ); + } + break; + + case 9: // Signed Long + // Check if number is negative + if ( $input_data < 0 ) + { + // Number is negative - make signed value + $input_data = $input_data + 4294967296; + } + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the long and return + return pack( "v", $input_data ); + } + else + { + // Motorola/Big Endian - pack the long and return + return pack( "n", $input_data ); + } + break; + + case 10: // Signed Rational + // Check if numerator is negative + if ( $input_data['Numerator'] < 0 ) + { + // Number is numerator - make signed value + $input_data['Numerator'] = $input_data['Numerator'] + 4294967296; + } + // Check if denominator is negative + if ( $input_data['Denominator'] < 0 ) + { + // Number is denominator - make signed value + $input_data['Denominator'] = $input_data['Denominator'] + 4294967296; + } + // Check byte alignment + if ( $Byte_Align == "II" ) + { + // Intel/Little Endian - pack the two longs and return + return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] ); + } + else + { + // Motorola/Big Endian - pack the two longs and return + return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] ); + } + break; + + case 11: // Float + // IEEE 754 Float + // TODO - EXIF - IFD datatype Float not implemented yet + return "FLOAT NOT IMPLEMENTED YET"; + break; + + case 12: // Double + // IEEE 754 Double + // TODO - EXIF - IFD datatype Double not implemented yet + return "DOUBLE NOT IMPLEMENTED YET"; + break; + + default: + // Error - Invalid Datatype + return "Invalid Datatype $data_type"; + break; + + } + + // Shouldn't get here + return FALSE; +} + +/****************************************************************************** +* End of Function: put_IFD_Data_Type +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: get_IFD_value_as_text +* +* Description: Decodes an IFD field value from a binary data string, using +* information supplied about the data type and byte alignment of +* the stored data. +* This function should be used for all datatypes except ASCII strings +* +* Parameters: input_data - a binary data string containing the IFD value, +* must be exact length of the value +* data_type - a number representing the IFD datatype as per the +* TIFF 6.0 specification: +* 1 = Unsigned 8-bit Byte +* 2 = ASCII String +* 3 = Unsigned 16-bit Short +* 4 = Unsigned 32-bit Long +* 5 = Unsigned 2x32-bit Rational +* 6 = Signed 8-bit Byte +* 7 = Undefined +* 8 = Signed 16-bit Short +* 9 = Signed 32-bit Long +* 10 = Signed 2x32-bit Rational +* 11 = 32-bit Float +* 12 = 64-bit Double +* Byte_Align - Indicates the byte alignment of the data. +* MM = Motorola, MSB first, Big Endian +* II = Intel, LSB first, Little Endian +* +* Returns: output - the value of the data (string or numeric) +* +******************************************************************************/ + +function get_IFD_value_as_text( $Exif_Tag ) +{ + // Create a string to receive the output text + $output_str = ""; + +if( !empty( $Exif_Tag['Data Type'] ) ) { + // Select Processing method according to the datatype + switch ($Exif_Tag['Data Type']) + { + case 1 : // Unsigned Byte + case 3 : // Unsigned Short + case 4 : // Unsigned Long + case 6 : // Signed Byte + case 8 : // Signed Short + case 9 : // Signed Long + + // Cycle through each of the values for this tag + foreach ( $Exif_Tag['Data'] as $val ) + { + // Check that this isn't the first value, + if ( $output_str != "" ) + { + // This isn't the first value, Add a Comma and Newline to the output + $output_str .= ",\n"; + } + // Add the Value to the output + $output_str .= $val; + } + break; + + case 2 : // ASCII + // Append all the strings together, separated by Newlines + $output_str .= implode ( "\n", $Exif_Tag['Data']); + break; + + case 5 : // Unsigned Rational + case 10: // Signed Rational + + // Cycle through each of the values for this tag + foreach ( $Exif_Tag['Data'] as $val ) + { + // Check that this isn't the first value, + if ( $output_str != "" ) + { + // This isn't the first value, Add a Comma and Newline to the output + $output_str .= ",\n"; + } + + // Add the Full Value to the output + $output_str .= $val['Numerator'] ."/" . $val['Denominator']; + + // Check if division by zero might be a problem + if ( $val['Denominator'] != 0 ) + { + // Denominator is not zero, Add the Decimal Value to the output text + $output_str .= " (" . ($val['Numerator'] / $val['Denominator']) . ")"; + } + } + break; + + case 11: // Float + case 12: // Double + // TODO - EXIF - IFD datatype Double and Float not implemented yet + $output_str .= "Float and Double not implemented yet"; + break; + + case 7 : // Undefined + // Unless the User has asked to see the raw binary data, this + // type should not be displayed + + // Check if the user has requested to see the binary data in hex + if ( $GLOBALS['SHOW_BINARY_DATA_HEX'] == TRUE) + { + // User has requested to see the binary data in hex + // Add the value in hex + $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . bin2hex( $Exif_Tag['Data'] ) ; + } + // Check if the user has requested to see the binary data as is + else if ( $GLOBALS['SHOW_BINARY_DATA_TEXT'] == TRUE) + { + // User has requested to see the binary data as is + // Add the value as is + $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . $Exif_Tag['Data'] ; + } + else + { + // User has NOT requested to see binary data, + // Add a message indicating the number of bytes to the output + $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ) " ; + } + break; + + default : + // Error - Unknown IFD datatype + $output_str .= "Error - Exif tag data type (" . $Exif_Tag['Data Type'] .") is invalid"; + break; + } +} + + // Return the resulting text string + return $output_str; +} + +/****************************************************************************** +* End of Function: get_IFD_value_as_text +******************************************************************************/ + + + + +/****************************************************************************** +* Global Variable: IFD_Data_Sizes +* +* Contents: The sizes (in bytes) of each EXIF IFD Datatype, indexed by +* their datatype number +* +******************************************************************************/ + +$GLOBALS['IFD_Data_Sizes'] = array( 1 => 1, // Unsigned Byte + 2 => 1, // ASCII String + 3 => 2, // Unsigned Short + 4 => 4, // Unsigned Long + 5 => 8, // Unsigned Rational + 6 => 1, // Signed Byte + 7 => 1, // Undefined + 8 => 2, // Signed Short + 9 => 4, // Signed Long + 10 => 8, // Signed Rational + 11 => 4, // Float + 12 => 8 ); // Double + +/****************************************************************************** +* End of Global Variable: IFD_Data_Sizes +******************************************************************************/ + + + + + + +?> diff --git a/includes/jpeg_metadata_tk/EXIF_Makernote.php b/includes/jpeg_metadata_tk/EXIF_Makernote.php new file mode 100644 index 0000000..ceb82f4 --- /dev/null +++ b/includes/jpeg_metadata_tk/EXIF_Makernote.php @@ -0,0 +1,334 @@ +<?php + +/****************************************************************************** +* +* Filename: EXIF_Makernote.php +* +* Description: Provides functions for reading EXIF Makernote Information +* The actual functions for reading each manufacturers makernote +* are provided in the Makernotes directory. +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* Changes: 1.00 -> 1.11 : changed makernotes directory definition to allow +* the toolkit to be portable across directories +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + + +// Create the Makernote Parser and Interpreter Function Array + +$GLOBALS['Makernote_Function_Array'] = array( "Read_Makernote_Tag" => array( ), + "get_Makernote_Text_Value" => array( ), + "Interpret_Makernote_to_HTML" => array( ) ); + + +// Include the Main TIFF and EXIF Tags array + +include_once 'EXIF.php'; + + + +/****************************************************************************** +* +* Include the Makernote Scripts +* +******************************************************************************/ + +// Set the Makernotes Directory + +$dir = dirname(__FILE__) . "/Makernotes/"; // Change: as of version 1.11 - to allow directory portability + +// Open the directory +$dir_hnd = @opendir ( $dir ); + +// Cycle through each of the files in the Makernotes directory + +while ( ( $file = readdir( $dir_hnd ) ) !== false ) +{ + // Check if the current item is a file + if ( is_file ( $dir . $file ) ) + { + // Item is a file, break it into it's parts + $path_parts = pathinfo( $dir . $file ); + + // Check if the extension is php + if ( $path_parts["extension"] == "php" ) + { + // This is a php script - include it + include_once ($dir . $file) ; + } + } +} +// close the directory +closedir( $dir_hnd ); + + + + + + + + + + +/****************************************************************************** +* +* Function: Read_Makernote_Tag +* +* Description: Attempts to decodes the Makernote tag supplied, returning the +* new tag with the decoded information attached. +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* +******************************************************************************/ + +function Read_Makernote_Tag( $Makernote_Tag, $EXIF_Array, $filehnd ) +{ + + // Check if the Makernote is present but empty - this sometimes happens + if ( ( strlen( $Makernote_Tag['Data'] ) === 0 ) || + ( is_string( $Makernote_Tag['Data'] ) && $Makernote_Tag['Data'] === str_repeat ( "\x00", strlen( $Makernote_Tag['Data'] )) ) ) + { + // Modify the makernote to display that it is empty + $Makernote_Tag['Decoded Data'] = "Empty"; + $Makernote_Tag['Makernote Type'] = "Empty"; + $Makernote_Tag['Makernote Tags'] = "Empty"; + $Makernote_Tag['Decoded'] = TRUE; + + // Return the new makernote + return $Makernote_Tag; + } + + // Check if the Make Field exists in the TIFF IFD + if ( array_key_exists ( 271, $EXIF_Array[0] ) ) + { + // A Make tag exists in IFD0, collapse multiple strings (if any), and save result + $Make_Field = implode ( "\n", $EXIF_Array[0][271]['Data']); + } + else + { + // No Make field found + $Make_Field = ""; + } + + // Cycle through each of the "Read_Makernote_Tag" functions + + foreach( $GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'] as $func ) + { + // Run the current function, and save the result + $New_Makernote_Tag = $func( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ); + + // Check if a valid result was returned + if ( $New_Makernote_Tag !== FALSE ) + { + // A valid result was returned - stop cycling + break; + } + } + + // Check if a valid result was returned + if ( $New_Makernote_Tag === false ) + { + // A valid result was NOT returned - construct a makernote tag representing this + $New_Makernote_Tag = $Makernote_Tag; + $New_Makernote_Tag['Decoded'] = FALSE; + $New_Makernote_Tag['Makernote Type'] = "Unknown Makernote"; + } + + // Return the new makernote tag + return $New_Makernote_Tag; + +} + +/****************************************************************************** +* End of Function: Read_Makernote_Tag +******************************************************************************/ + + + + + + + + + +/****************************************************************************** +* +* Function: get_Makernote_Text_Value +* +* Description: Attempts to provide a text value for any makernote tag marked +* as type special. Returns false no handler could be found to +* process the tag +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from Read_Makernote_Tag +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If no handler could be found to process this tag, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Makernote_Text_Value( $Tag, $Tag_Definitions_Name ) +{ + + // Cycle through each of the "get_Makernote_Text_Value" functions + + foreach( $GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'] as $func ) + { + // Run the current function, and save the result + $Text_Val = $func( $Tag, $Tag_Definitions_Name ); + + // Check if a valid result was returned + if ( $Text_Val !== FALSE ) + { + // valid result - return it + return $Text_Val; + } + } + + // No Special tag handler found for this tag - return false + return FALSE; + +} + + +/****************************************************************************** +* End of Function: get_Makernote_Text_Value +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: Interpret_Makernote_to_HTML +* +* Description: Attempts to interpret a makernote into html. +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* +******************************************************************************/ + +function Interpret_Makernote_to_HTML( $Makernote_tag, $filename ) +{ + + // Create a string to receive the HTML + $output_str = ""; + + // Check if the makernote tag is valid + if ( $Makernote_tag === FALSE ) + { + // No makernote info - return + return $output_str; + } + + + // Check if the makernote has been marked as unknown + if ( $Makernote_tag['Makernote Type'] == "Unknown Makernote" ) + { + // Makernote is unknown - return message + $output_str .= "<h4 class=\"EXIF_Makernote_Small_Heading\">Unknown Makernote Coding</h4>\n"; + return $output_str; + } + else + { + // Makernote is known - add a heading to the output + $output_str .= "<p class=\"EXIF_Makernote_Text\">Makernote Coding: " . $Makernote_tag['Makernote Type'] . "</p>\n"; + } + + // Check if this is an empty makernote + if ( $Makernote_tag['Makernote Type'] == "Empty" ) + { + // It is empty - don't try to interpret + return $output_str; + } + + // Cycle through each of the "Interpret_Makernote_to_HTML" functions + + foreach( $GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'] as $func ) + { + // Run the current function, and save the result + $html_text = $func( $Makernote_tag, $filename ); + + // Check if a valid result was returned + if ( $html_text !== FALSE ) + { + // valid result - return it + return $output_str . $html_text; + } + } + + // No Interpreter function handled the makernote - return a message + + $output_str .= "<h4 class=\"EXIF_Makernote_Small_Heading\">Could not Decode Makernote, it may be corrupted or empty</h4>\n"; + + return $output_str; + + +} + +/****************************************************************************** +* End of Function: Interpret_Makernote_to_HTML +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/EXIF_Tags.php b/includes/jpeg_metadata_tk/EXIF_Tags.php new file mode 100644 index 0000000..cb36f18 --- /dev/null +++ b/includes/jpeg_metadata_tk/EXIF_Tags.php @@ -0,0 +1,915 @@ +<?php + +/****************************************************************************** +* +* Filename: EXIF_Tags.php +* +* Description: Provides definitions of the tags for TIFF, EXIF, Interoperability, +* GPS, Meta, Kodak Special Effects and Kodak Borders IFD's. +* +* Author: Evan Hunter +* +* Date: 1/8/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* Changes: 1.00 -> 1.11 : Added TIFF compression types ZIP, LZW and JPEG +* Added embedded XMP tag +* Added embedded Photoshop IRB tag +* Fixed GPS tags after testing +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions +* +* Contents: This array defines the fields for the TIFF, EXIF, Interoperability, +* GPS, Meta, Kodak Special Effects and Kodak Borders IFD's. +* It is indexed by the IFD Type, then the Tag number +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ] = array( + + +/*****************************************************************************/ +/* */ +/* TIFF Tags */ +/* */ +/*****************************************************************************/ + + +"TIFF" => array( + + +256 => array( 'Name' => "Image Width", + 'Description' => "Width of image in pixels (number of columns)", + 'Type' => "Numeric", + 'Units' => "pixels" ), + +257 => array( 'Name' => "Image Length", + 'Description' => "Height of image in pixels (number of rows)", + 'Type' => "Numeric", + 'Units' => "pixels" ), + +258 => array( 'Name' => "Bits Per Sample", + 'Description' => "Number of bits recorded per sample (a sample is usually one colour (Red, Green or Blue) of one pixel)", + 'Type' => "Numeric", + 'Units' => "bits ( for each colour component )" ), + + +259 => array( 'Name' => "Compression", + 'Description' => "Specifies what type of compression is used 1 = uncompressed, 6 = JPEG compression (thumbnails only), Other = reserved", + 'Type' => "Lookup", + 1 => "Uncompressed", + 5 => "LZW Compression", + 6 => "Thumbnail compressed with JPEG compression", + 7 => "JPEG Compression", + 8 => "ZIP Compression" ), // Change: Added TIFF compression types as of version 1.11 + +262 => array( 'Name' => "Photometric Interpretation", + 'Description' => "Specifies Pixel Composition - 0 or 1 = monochrome, 2 = RGB, 3 = Palatte Colour, 4 = Transparency Mask, 6 = YCbCr", + 'Type' => "Lookup", + 2 => "RGB (Red Green Blue)", + 6 => "YCbCr (Luminance, Chroma minus Blue, and Chroma minus Red)" ), + +274 => array( 'Name' => "Orientation", + 'Description' => "Specifies the orientation of the image.\n +1 = Row 0 top, column 0 left\n +2 = Row 0 top, column 0 right\n +3 = Row 0 bottom, column 0 right\n +4 = Row 0 bottom, column 0 left\n +5 = Row 0 left, column 0 top\n +6 = Row 0 right, column 0 top\n +7 = Row 0 right, column 0 bottom\n +8 = Row 0 left, column 0 bottom", + 'Type' => "Lookup", + 1 => "No Rotation, No Flip \n(Row 0 is at the visual top of the image,\n and column 0 is the visual left-hand side)", + 2 => "No Rotation, Flipped Horizontally \n(Row 0 is at the visual top of the image,\n and column 0 is the visual right-hand side)", + 3 => "Rotated 180 degrees, No Flip \n(Row 0 is at the visual bottom of the image,\n and column 0 is the visual right-hand side)", + 4 => "No Rotation, Flipped Vertically \n(Row 0 is at the visual bottom of the image,\n and column 0 is the visual left-hand side)", + 5 => "Flipped Horizontally, Rotated 90 degrees counter clockwise \n(Row 0 is at the visual left-hand side of of the image,\n and column 0 is the visual top)", + 6 => "No Flip, Rotated 90 degrees clockwise \n(Row 0 is at the visual right-hand side of of the image,\n and column 0 is the visual top)", + 7 => "Flipped Horizontally, Rotated 90 degrees clockwise \n(Row 0 is at the visual right-hand side of of the image,\n and column 0 is the visual bottom)", + 8 => "No Flip, Rotated 90 degrees counter clockwise \n(Row 0 is at the visual left-hand side of of the image,\n and column 0 is the visual bottom)" ), +277 => array( 'Name' => "Samples Per Pixel", + 'Description' => "Number of recorded samples (colours) per pixel - usually 1 for B&W, grayscale, and palette-colour, usually 3 for RGB and YCbCr", + 'Type' => "Numeric", + 'Units' => "Components (colours)" ), + +284 => array( 'Name' => "Planar Configuration", + 'Description' => "Specifies whether pixel components are recorded in chunky or planar format - 1 = Chunky, 2 = Planar", + 'Type' => "Lookup", + 1 => "Chunky Format", + 2 => "Planar Format" ), + +530 => array( 'Name' => "YCbCr Sub-Sampling", + 'Description' => "Specifies ratio of chrominance to luminance components - [2, 1] = YCbCr4:2:2, [2, 2] = YCbCr4:2:0", + 'Type' => "Special" ), + + +531 => array( 'Name' => "YCbCr Positioning", + 'Description' => "Specifies location of chrominance and luminance components - 1 = centered, 2 = co-sited", + 'Type' => "Lookup", + 1 => "Chrominance components Centred in relation to luminance components", + 2 => "Chrominance and luminance components Co-Sited" ), + + +282 => array( 'Name' => "X Resolution", + 'Description' => "Number of columns (pixels) per \'ResolutionUnit\'", + 'Type' => "Numeric", + 'Units'=> "pixels per 'Resolution Unit' " ), + +283 => array( 'Name' => "Y Resolution", + 'Description' => "Number of rows (pixels) per \'ResolutionUnit\'", + 'Type' => "Numeric", + 'Units'=> "pixels per 'Resolution Unit' " ), + +296 => array( 'Name' => "Resolution Unit", + 'Description' => "Units for measuring XResolution and YResolution - 1 = No units, 2 = Inches, 3 = Centimetres", + 'Type' => "Lookup", + 2 => "Inches", + 3 => "Centimetres" ), + +273 => array( 'Name' => "Strip Offsets", + 'Type' => "Numeric", + 'Units'=> "bytes offset" ), + +278 => array( 'Name' => "Rows Per Strip", + 'Type' => "Numeric", + 'Units'=> "rows" ), + +279 => array( 'Name' => "Strip Byte Counts", + 'Type' => "Numeric", + 'Units'=> "bytes" ), + +513 => array( 'Name' => "Exif Thumbnail (JPEG Interchange Format)", + 'Type' => "Special" ), + +514 => array( 'Name' => "Exif Thumbnail Length (JPEG Interchange Format Length)", + 'Type' => "Numeric", + 'Units'=> "bytes" ), + +301 => array( 'Name' => "Transfer Function", + 'Type' => "Numeric", + 'Units'=> "" ), + +318 => array( 'Name' => "White Point Chromaticity", + 'Type' => "Numeric", + 'Units'=> "(x,y coordinates on a 1931 CIE xy chromaticity diagram)" ), + +319 => array( 'Name' => "Primary Chromaticities", + 'Type' => "Numeric", + 'Units'=> "(Red x,y, Green x,y, Blue x,y coordinates on a 1931 CIE xy chromaticity diagram)" ), + +529 => array( 'Name' => "YCbCr Coefficients", + 'Description' => "Transform Coefficients for transformation from RGB to YCbCr", + 'Type' => "Numeric", + 'Units'=> "(LumaRed, LumaGreen, LumaBlue [proportions of red, green, and blue in luminance])" ), + +532 => array( 'Name' => "Reference Black point and White point", + 'Type' => "Numeric", + 'Units'=> "(R or Y White Headroom, R or Y Black Footroom, G or Cb White Headroom, G or Cb Black Footroom, B or Cr White Headroom, B or Cr Black Footroom)" ), + +306 => array( 'Name' => "Date and Time", + 'Type' => "Numeric", + 'Units'=> " (Format: YYYY:MM:DD HH:mm:SS)" ), + +270 => array( 'Name' => "Image Description", + 'Type' => "String" ), + +271 => array( 'Name' => "Make (Manufacturer)", + 'Type' => "String" ), + +272 => array( 'Name' => "Model", + 'Type' => "String" ), + +305 => array( 'Name' => "Software or Firmware", + 'Type' => "String" ), + +315 => array( 'Name' => "Artist Name", + 'Type' => "String" ), + +700 => array( 'Name' => "Embedded XMP Block", // Change: Added embedded XMP as of version 1.11 + 'Type' => "XMP" ), + +33432 => array( 'Name' => "Copyright Information", + 'Type' => "String" ), + +34665 => array( 'Name' => "EXIF Image File Directory (IFD)", + 'Type' => "SubIFD", + 'Tags Name' => "EXIF" ), + +33723 => array( 'Name' => "IPTC Records", + 'Type' => "IPTC" ), + +34377 => array( 'Name' => "Embedded Photoshop IRB", // Change: Added embedded IRB as of version 1.11 + 'Type' => "IRB" ), + +34853 => array( 'Name' => "GPS Info Image File Directory (IFD)", // Change: Moved GPS IFD tag to correct location as of version 1.11 + 'Type' => "SubIFD", + 'Tags Name' => "GPS" ), + +50341 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +), + + +/*****************************************************************************/ +/* */ +/* EXIF Tags */ +/* */ +/*****************************************************************************/ + + +'EXIF' => array ( + +// Exif IFD +36864 => array( 'Name' => "Exif Version", + 'Type' => "String" ), + +40965 => array( 'Name' => "Interoperability Image File Directory (IFD)", + 'Type' => "SubIFD", + 'Tags Name' => "Interoperability" ), + +// Change: removed GPS IFD tag from here as it was incorrect location - as of version 1.11 + +40960 => array( 'Name' => "FlashPix Version", + 'Type' => "String" ), + +40961 => array( 'Name' => "Colour Space", + 'Type' => "Lookup", + 1 => "sRGB", + 0xFFFF => "Uncalibrated" ), + +40962 => array( 'Name' => "Pixel X Dimension", + 'Type' => "Numeric", + 'Units'=> "pixels" ), + +40963 => array( 'Name' => "Pixel Y Dimension", + 'Type' => "Numeric", + 'Units' => "pixels" ), + +37121 => array( 'Name' => "Components Configuration", + 'Type' => "Special" ), + +37122 => array( 'Name' => "Compressed Bits Per Pixel", + 'Type' => "Numeric", + 'Units' => "bits" ), + +37500 => array( 'Name' => "Maker Note", + 'Type' => "Maker Note" ), + +37510 => array( 'Name' => "User Comment", + 'Type' => "Character Coded String" ), + +40964 => array( 'Name' => "Related Sound File", + 'Type' => "String" ), + +36867 => array( 'Name' => "Date and Time of Original", + 'Type' => "String", + 'Units' => " (Format: YYYY:MM:DD HH:mm:SS)" ), + +36868 => array( 'Name' => "Date and Time when Digitized", + 'Type' => "String", + 'Units' => " (Format: YYYY:MM:DD HH:mm:SS)" ), + +37520 => array( 'Name' => "Sub Second Time", + 'Type' => "String" ), + +37521 => array( 'Name' => "Sub Second Time of Original", + 'Type' => "String" ), + +37522 => array( 'Name' => "Sub Second Time when Digitized", + 'Type' => "String" ), + +33434 => array( 'Name' => "Exposure Time", + 'Type' => "Numeric", + 'Units' => "seconds" ), + +37377 => array( 'Name' => "APEX Shutter Speed Value (Tv)", + 'Type' => "Numeric" ), + +37378 => array( 'Name' => "APEX Aperture Value (Av)", + 'Type' => "Numeric" ), + +37379 => array( 'Name' => "APEX Brightness Value (Bv)", + 'Type' => "Numeric" ), + +37380 => array( 'Name' => "APEX Exposure Bias Value (Exposure Compensation)", + 'Type' => "Numeric", + 'Units' => "EV" ), + +42240 => array( 'Name' => "Gamma Compensation for Playback", + 'Type' => "Numeric" ), + + +37381 => array( 'Name' => "APEX Maximum Aperture Value", + 'Type' => "Numeric" ), + +37382 => array( 'Name' => "Subject Distance", + 'Type' => "Numeric", + 'Units' => "metres" ), + +37383 => array( 'Name' => "Metering Mode", + 'Type' => "Lookup", + 0 => "Unknown", + 1 => "Average", + 2 => "Center Weighted Average", + 3 => "Spot", + 4 => "Multi Spot", + 5 => "Pattern", + 6 => "Partial", + 255 => "Other" ), + +37384 => array( 'Name' => "Light Source", + 'Type' => "Lookup", + 0 => "Unknown", + 1 => "Daylight", + 2 => "Fluorescent", + 3 => "Tungsten (incandescent light)", + 4 => "Flash", + 9 => "Fine weather", + 10 => "Cloudy weather", + 11 => "Shade", + 12 => "Daylight fluorescent (D 5700 – 7100K)", + 13 => "Day white fluorescent (N 4600 – 5400K)", + 14 => "Cool white fluorescent (W 3900 – 4500K)", + 15 => "White fluorescent (WW 3200 – 3700K)", + 17 => "Standard light A", + 18 => "Standard light B", + 19 => "Standard light C", + 20 => "D55", + 21 => "D65", + 22 => "D75", + 23 => "D50", + 24 => "ISO studio tungsten", + 255 => "Other" ), + +37385 => array( 'Name' => "Flash", + 'Type' => "Lookup", + 0 => "Flash did not fire", + 1 => "Flash fired", + 5 => "Strobe return light not detected", + 7 => "Strobe return light detected", + 9 => "Flash fired, compulsory flash mode", + 13 => "Flash fired, compulsory flash mode, return light not detected", + 15 => "Flash fired, compulsory flash mode, return light detected", + 16 => "Flash did not fire, compulsory flash suppression mode", + 24 => "Flash did not fire, auto mode", + 25 => "Flash fired, auto mode", + 29 => "Flash fired, auto mode, return light not detected", + 31 => "Flash fired, auto mode, return light detected", + 32 => "No flash function", + 65 => "Flash fired, red-eye reduction mode", + 69 => "Flash fired, red-eye reduction mode, return light not detected", + 71 => "Flash fired, red-eye reduction mode, return light detected", + 73 => "Flash fired, compulsory flash mode, red-eye reduction mode", + 77 => "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", + 79 => "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", + 89 => "Flash fired, auto mode, red-eye reduction mode", + 93 => "Flash fired, auto mode, return light not detected, red-eye reduction mode", + 95 => "Flash fired, auto mode, return light detected, red-eye reduction mode" ), + +37386 => array( 'Name' => "FocalLength", + 'Type' => "Numeric", + 'Units' => "mm" ), + +37396 => array( 'Name' => "Subject Area", + 'Type' => "Numeric", + 'Units' => "( Two Values: x,y coordinates, Three Values: x,y coordinates, diameter, Four Values: center x,y coordinates, width, height)" ), + +33437 => array( 'Name' => "Aperture F Number", + 'Type' => "Numeric" ), + +34850 => array( 'Name' => "Exposure Program", + 'Type' => "Lookup", + 0 => "Not defined", + 1 => "Manual", + 2 => "Normal program", + 3 => "Aperture priority", + 4 => "Shutter priority", + 5 => "Creative program (biased toward depth of field)", + 6 => "Action program (biased toward fast shutter speed)", + 7 => "Portrait mode (for closeup photos with the background out of focus)", + 8 => "Landscape mode (for landscape photos with the background in focus)" ), + +34852 => array( 'Name' => "Spectral Sensitivity", + 'Type' => "String" ), + +34855 => array( 'Name' => "ISO Speed Ratings", + 'Type' => "Numeric" ), + +34856 => array( 'Name' => "Opto-Electronic Conversion Function", + 'Type' => "Unknown" ), + +41483 => array( 'Name' => "Flash Energy", + 'Type' => "Numeric", + 'Units' => "Beam Candle Power Seconds (BCPS)" ), + +41484 => array( 'Name' => "Spatial Frequency Response", + 'Type' => "Unknown" ), + +41486 => array( 'Name' => "Focal Plane X Resolution", + 'Type' => "Numeric", + 'Units' => "pixels per 'Focal Plane Resolution Unit'" ), + +41487 => array( 'Name' => "Focal Plane Y Resolution", + 'Type' => "Numeric", + 'Units' => "pixels per 'Focal Plane Resolution Unit'" ), + +41488 => array( 'Name' => "Focal Plane Resolution Unit", + 'Type' => "Lookup", + 2 => "Inches", + 3 => "Centimetres" ), + +41492 => array( 'Name' => "Subject Location", + 'Type' => "Numeric", + 'Units' => "(x,y pixel coordinates of subject)" ), + +41493 => array( 'Name' => "Exposure Index", + 'Type' => "Numeric" ), + +41495 => array( 'Name' => "Sensing Method", + 'Type' => "Lookup", + 1 => "Not defined", + 2 => "One-chip colour area sensor", + 3 => "Two-chip colour area sensor", + 4 => "Three-chip colour area sensor", + 5 => "Colour sequential area sensor", + 7 => "Trilinear sensor", + 8 => "Colour sequential linear sensor" ), + +41728 => array( 'Name' => "File Source", + 'Type' => "Lookup", + 3 => "Digital Still Camera" ), + +41729 => array( 'Name' => "Scene Type", + 'Type' => "Lookup", + 1 => "A directly photographed image" ), + +41730 => array( 'Name' => "Colour Filter Array Pattern", + 'Type' => "Special" ), + +41985 => array( 'Name' => "Special Processing (Custom Rendered)", + 'Type' => "Lookup", + 0 => "Normal process", + 1 => "Custom process" ), + +41986 => array( 'Name' => "Exposure Mode", + 'Type' => "Lookup", + 0 => "Auto exposure", + 1 => "Manual exposure", + 2 => "Auto bracket" ), + +41987 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 0 => "Auto white balance", + 1 => "Manual white balance" ), + +41988 => array( 'Name' => "Digital Zoom Ratio", + 'Type' => "Numeric", + 'Units' => " ( Zero = Digital Zoom Not Used )" ), + +41989 => array( 'Name' => "Equivalent Focal Length In 35mm Film", + 'Type' => "Numeric", + 'Units' => "mm" ), + +41990 => array( 'Name' => "Scene Capture Type", + 'Type' => "Lookup", + 0 => "Standard", + 1 => "Landscape", + 2 => "Portrait", + 3 => "Night scene" ), + +41991 => array( 'Name' => "Gain Control", + 'Type' => "Lookup", + 0 => "None", + 1 => "Low gain up", + 2 => "High gain up", + 3 => "Low gain down", + 4 => "High gain down" ), + +41992 => array( 'Name' => "Contrast", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Soft", + 2 => "Hard" ), + +41993 => array( 'Name' => "Saturation", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Low saturation", + 2 => "High saturation" ), + +41994 => array( 'Name' => "Sharpness", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Soft", + 2 => "Hard" ), + +41995 => array( 'Name' => "Device Setting Description", + 'Type' => "Unknown" ), + +41996 => array( 'Name' => "Subject Distance Range", + 'Type' => "Lookup", + 0 => "Unknown", + 1 => "Macro", + 2 => "Close view", + 3 => "Distant view" ), + +42016 => array( 'Name' => "Image Unique ID", + 'Type' => "String" ), + + + +// 11 => "ACDComment", +// 255 => "NewSubfileType" + + +), + + + + +/*****************************************************************************/ +/* */ +/* Interoperability Tags */ +/* */ +/*****************************************************************************/ + +"Interoperability" => array( + +1 => array( 'Name' => "Interoperability Index", + 'Type' => "String" ), + +2 => array( 'Name' => "Interoperability Version", + 'Type' => "String" ), + +4096 => array( 'Name' => "Related Image File Format", + 'Type' => "String" ), + +4097 => array( 'Name' => "Related Image File Width", + 'Type' => "Numeric", + 'Units' => "pixels" ), + +4098 => array( 'Name' => "Related Image File Length", + 'Type' => "Numeric", + 'Units' => "pixels " ) + +), + + +/*****************************************************************************/ +/* */ +/* GPS Tags */ +/* */ +/*****************************************************************************/ + +"GPS" => array( + +0 => array( 'Name' => "GPS Tag Version", + 'Type' => "Numeric", + 'Units' => "(e.g.: 2.2.0.0 = Version 2.2 )" ), + +1 => array( 'Name' => "North or South Latitude", + 'Type' => "String" ), + +2 => array( 'Name' => "Latitude", + 'Type' => "Numeric", + 'Units' => "(Degrees Minutes Seconds North or South)" ), + +3 => array( 'Name' => "East or West Longitude", + 'Type' => "String" ), + +4 => array( 'Name' => "Longitude", + 'Type' => "Numeric", + 'Units' => "(Degrees Minutes Seconds East or West)" ), + +5 => array( 'Name' => "Altitude Reference", + 'Type' => "Lookup", + 0 => "Sea Level", + 1 => "Sea level reference (negative value)" ), + +6 => array( 'Name' => "Altitude", + 'Type' => "Numeric", + 'Units' => "Metres with respect to Altitude Reference" ), + +7 => array( 'Name' => "GPS Time (atomic clock)", + 'Type' => "Numeric", + 'Units' => "(Hours Minutes Seconds)" ), + +8 => array( 'Name' => "GPS Satellites used for Measurement", + 'Type' => "String" ), + +9 => array( 'Name' => "GPS Receiver Status", + 'Type' => "Lookup", + 'A' => "Measurement in progress", // Change: Fixed tag values as of version 1.11 + 'V' => "Measurement Interoperability" ), + +10 => array( 'Name' => "GPS Measurement Mode", + 'Type' => "Lookup", + 2 => "2-dimensional measurement", // Change: Fixed tag values as of version 1.11 + 3 => "3-dimensional measurement" ), + +11 => array( 'Name' => "Measurement Precision", + 'Type' => "Numeric", + 'Units' => "(Data Degree of Precision, Horizontal for 2D, Position for 3D)" ), + +12 => array( 'Name' => "Speed Unit", + 'Type' => "Lookup", + 'K' => "Kilometers per Hour", // Change: Fixed tag values as of version 1.11 + 'M' => "Miles per Hour", + 'N' => "Knots" ), + +13 => array( 'Name' => "Speed of GPS receiver", + 'Type' => "Numeric", + 'Units' => "Speed Units" ), + +14 => array( 'Name' => "Reference for direction of Movement", + 'Type' => "Lookup", // Change: Fixed tag values as of version 1.11 + 'T' => "True North", + 'M' => "Magnetic North" ), + +15 => array( 'Name' => "Direction of Movement", + 'Type' => "Numeric", + 'Units' => "Degrees relative to Movement Direction Reference" ), + +16 => array( 'Name' => "Reference for Direction of Image", + 'Type' => "Lookup", + 'T' => "True North", // Change: Fixed tag values as of version 1.11 + 'M' => "Magnetic North" ), + +17 => array( 'Name' => "Direction of Image", + 'Type' => "Numeric", + 'Units' => "Degrees relative to Image Direction Reference" ), + +18 => array( 'Name' => "Geodetic Survey Datum Used", + 'Type' => "String" ), + +19 => array( 'Name' => "Destination - North or South Latitude", + 'Type' => "String" ), + +20 => array( 'Name' => "Latitude of Destination", + 'Type' => "Numeric", + 'Units' => "(Degrees Minutes Seconds North or South)" ), + +21 => array( 'Name' => "Destination - East or West Longitude", + 'Type' => "String" ), + +22 => array( 'Name' => "Longitude of Destination", + 'Type' => "Numeric", + 'Units' => "(Degrees Minutes Seconds East or West)" ), + +23 => array( 'Name' => "Reference for Bearing of Destination", + 'Type' => "Lookup", + 'T' => "True North", // Change: Fixed tag values as of version 1.11 + 'M' => "Magnetic North" ), + +24 => array( 'Name' => "Bearing of Destination", + 'Type' => "Numeric", + 'Units' => "Degrees relative to Destination Bearing Reference" ), + +25 => array( 'Name' => "Units for Distance to Destination", + 'Type' => "Lookup", + 'K' => "Kilometres", // Change: Fixed tag values as of version 1.11 + 'M' => "Miles", + 'N' => "Nautical Miles" ), + +26 => array( 'Name' => "Distance to Destination", + 'Type' => "Numeric", + 'Units' => "Destination Distance Units" ), + +27 => array( 'Name' => "Name of GPS Processing Method", + 'Type' => "Character Coded String" ), + +28 => array( 'Name' => "Name of GPS Area", + 'Type' => "Character Coded String" ), + +29 => array( 'Name' => "GPS Date", + 'Type' => "Numeric", + 'Units'=> " (Format: YYYY:MM:DD HH:mm:SS)" ), + +30 => array( 'Name' => "GPS Differential Correction", + 'Type' => "Lookup", + 0 => "Measurement without differential correction", + 1 => "Differential correction applied" ), + +), + + + + + + + + + +/*****************************************************************************/ +/* */ +/* META (App3) Tags */ +/* */ +/*****************************************************************************/ + +"Meta" => array( + + +50000 => array( 'Name' => "CaptureDevice.FilmProductCode", + 'Type' => "Unknown" ), + +50001 => array( 'Name' => "DigitalProcess.ImageSourceEK", + 'Type' => "Unknown" ), + +50002 => array( 'Name' => "CaptureConditions.PAR", + 'Type' => "Unknown" ), + +50003 => array( 'Name' => "CaptureDevice.CameraOwner.EK", + 'Type' => "Character Coded String" ), + +50004 => array( 'Name' => "CaptureDevice.SerialNumber.Camera", + 'Type' => "Unknown" ), + +50005 => array( 'Name' => "SceneContent.GroupCaption.UserSelectGroupTitle", + 'Type' => "Unknown" ), + +50006 => array( 'Name' => "OutputOrder.Information.DealerIDNumber", + 'Type' => "Unknown" ), + +50007 => array( 'Name' => "CaptureDevice.FID", + 'Type' => "Unknown" ), + +50008 => array( 'Name' => "OutputOrder.Information.EnvelopeNumber", + 'Type' => "Unknown" ), + +50009 => array( 'Name' => "OutputOrder.SimpleRenderInst.FrameNumber", + 'Type' => "Unknown" ), + +50010 => array( 'Name' => "CaptureDevice.FilmCategory", + 'Type' => "Unknown" ), + +50011 => array( 'Name' => "CaptureDevice.FilmGencode", + 'Type' => "Unknown" ), + +50012 => array( 'Name' => "CaptureDevice.Scanner.ModelAndVersion", + 'Type' => "Unknown" ), + +50013 => array( 'Name' => "CaptureDevice.FilmSize", + 'Type' => "Unknown" ), + +50014 => array( 'Name' => "DigitalProcess.History.SBARGBShifts", + 'Type' => "Unknown" ), + +50015 => array( 'Name' => "DigitalProcess.History.SBAInputImageColourspace", + 'Type' => "Unknown" ), + +50016 => array( 'Name' => "DigitalProcess.History.SBAInputImageBitDepth", + 'Type' => "Unknown" ), + +50017 => array( 'Name' => "DigitalProcess.History.SBAExposureRecord", + 'Type' => "Unknown" ), + +50018 => array( 'Name' => "DigitalProcess.History.UserAdjSBARGBShifts", + 'Type' => "Unknown" ), + +50019 => array( 'Name' => "DigitalProcess.ImageRotationStatus", + 'Type' => "Unknown" ), + +50020 => array( 'Name' => "DigitalProcess.RollGuid.Elements", + 'Type' => "Unknown" ), + +50021 => array( 'Name' => "ImageContainer.MetadataNumber", + 'Type' => "String" ), + +50022 => array( 'Name' => "DigitalProcess.History.EditTagArray", + 'Type' => "Unknown" ), + +50023 => array( 'Name' => "CaptureConditions.Magnification", + 'Type' => "Unknown" ), + +50028 => array( 'Name' => "CaptureDevice.NativePhysicalXResolution", + 'Type' => "Unknown" ), + +50029 => array( 'Name' => "CaptureDevice.NativePhysicalYResolution", + 'Type' => "Unknown" ), + +50030 => array( 'Name' => "Kodak Special Effects IFD", + 'Type' => "SubIFD", + 'Tags Name' => "KodakSpecialEffects" ), + +50031 => array( 'Name' => "Kodak Borders IFD", + 'Type' => "SubIFD", + 'Tags Name' => "KodakBorders" ), + +50042 => array( 'Name' => "CaptureDevice.NativePhysicalResolutionUnit", + 'Type' => "Unknown" ), + +50200 => array( 'Name' => "ImageContainer.SourceImageDirectory", + 'Type' => "Unknown" ), + +50201 => array( 'Name' => "ImageContainer.SourceImageFileName", + 'Type' => "Unknown" ), + +50202 => array( 'Name' => "ImageContainer.SourceImageVolumeName", + 'Type' => "Unknown" ), + +50284 => array( 'Name' => "CaptureConditions.PrintQuantity", + 'Type' => "Unknown" ), + +50286 => array( 'Name' => "DigitalProcess.ImagePrintStatus", + 'Type' => "Unknown" ) + +), + + + +/*****************************************************************************/ +/* */ +/* Kodak Special Effects IFD Tags */ +/* */ +/*****************************************************************************/ + +"KodakSpecialEffects" => array( + +0 => array( 'Name' => "Digital Effects Version", + 'Type' => "Numeric" ), + +1 => array( 'Name' => "Digital Effects Name", + 'Type' => "Character Coded String" ), + +2 => array( 'Name' => "Digital Effects Type", + 'Type' => "Lookup", + 0 => "None Applied" ) + +), + +/*****************************************************************************/ +/* */ +/* Kodak Borders IFD Tags */ +/* */ +/*****************************************************************************/ + +"KodakBorders" => array( + +0 => array( 'Name' => "Borders Version", + 'Type' => "Numeric" ), + +1 => array( 'Name' => "Border Name", + 'Type' => "Character Coded String" ), + +2 => array( 'Name' => "Border ID", + 'Type' => "Numeric" ), + +3 => array( 'Name' => "Border Location", + 'Type' => "Lookup" ), + +4 => array( 'Name' => "Border Type", + 'Type' => "Lookup", + 0 => "None" ), + +8 => array( 'Name' => "Watermark Type", + 'Type' => "Lookup", + 0 => "None" ) + +), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions +******************************************************************************/ + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Edit_File_Info.php b/includes/jpeg_metadata_tk/Edit_File_Info.php new file mode 100644 index 0000000..7b19e70 --- /dev/null +++ b/includes/jpeg_metadata_tk/Edit_File_Info.php @@ -0,0 +1,575 @@ +<?php + + +/**************************************************************************** +* +* Filename: Edit_File_Info.php +* +* Description: Allows the user to edit the metadata of an image over the internet +* in the same way that Photoshop edits 'File Info' data +* This file provides only the html for a form containing the file info +* input fields. The rest of the html file must be provided by the calling script. +* $outputfilename must always be defined - it is ne name of the file which +* have the metadata changed after the form has been submitted +* +* This file has several modes of operation: +* +* 1) If $new_ps_file_info_array is defined then it's data will be used +* to fill the fields. +* 2) If $new_ps_file_info_array is not defined but $filename is defined, +* then the file info fields will be filled from the metadata in the file specified +* 3) If $new_ps_file_info_array is not defined but $filename and $default_ps_file_info_array +* are defined, then the file info fields will be filled from the metadata +* in the file specified, but where fields are blank, they will be filled from $default_ps_file_info_array +* 4) Otherwise the fields will be blank +* +* See Edit_File_Info_Example.php for an example of usage +* +* Author: Evan Hunter +* +* Date: 17/11/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* Changes: 1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +***************************************************************************/ + + + + require_once 'Toolkit_Version.php'; // Change: added as of version 1.11 + + // Check for operation modes 2 or 3 + // i.e. $filename is defined, and $new_ps_file_info_array is not + if ( ( ! isset( $new_ps_file_info_array ) ) && + ( isset( $filename ) ) && + ( is_string( $filename ) ) ) + { + // Hide any unknown EXIF tags + $GLOBALS['HIDE_UNKNOWN_TAGS'] = TRUE; + + // Accessing the existing file info for the specified file requires these includes + require_once 'JPEG.php'; + require_once 'XMP.php'; + require_once 'Photoshop_IRB.php'; + require_once 'EXIF.php'; + require_once 'Photoshop_File_Info.php'; + + // Retrieve the header information from the JPEG file + $jpeg_header_data = get_jpeg_header_data( $filename ); + + // Retrieve EXIF information from the JPEG file + $Exif_array = get_EXIF_JPEG( $filename ); + + // Retrieve XMP information from the JPEG file + $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) ); + + // Retrieve Photoshop IRB information from the JPEG file + $IRB_array = get_Photoshop_IRB( $jpeg_header_data ); + + // Retrieve Photoshop File Info from the three previous arrays + $new_ps_file_info_array = get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array ); + + + + // Check if there is an array of defaults available + if ( ( isset( $default_ps_file_info_array) ) && + ( is_array( $default_ps_file_info_array) ) ) + { + // There are defaults defined + + // Check if there is a default for the date defined + if ( ( ! array_key_exists( 'date', $default_ps_file_info_array ) ) || + ( ( array_key_exists( 'date', $default_ps_file_info_array ) ) && + ( $default_ps_file_info_array['date'] == '' ) ) ) + { + // No default for the date defined + // figure out a default from the file + + // Check if there is a EXIF Tag 36867 "Date and Time of Original" + if ( ( $Exif_array != FALSE ) && + ( array_key_exists( 0, $Exif_array ) ) && + ( array_key_exists( 34665, $Exif_array[0] ) ) && + ( array_key_exists( 0, $Exif_array[0][34665] ) ) && + ( array_key_exists( 36867, $Exif_array[0][34665][0] ) ) ) + { + // Tag "Date and Time of Original" found - use it for the default date + $default_ps_file_info_array['date'] = $Exif_array[0][34665][0][36867]['Data'][0]; + $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] ); + } + // Check if there is a EXIF Tag 36868 "Date and Time when Digitized" + else if ( ( $Exif_array != FALSE ) && + ( array_key_exists( 0, $Exif_array ) ) && + ( array_key_exists( 34665, $Exif_array[0] ) ) && + ( array_key_exists( 0, $Exif_array[0][34665] ) ) && + ( array_key_exists( 36868, $Exif_array[0][34665][0] ) ) ) + { + // Tag "Date and Time when Digitized" found - use it for the default date + $default_ps_file_info_array['date'] = $Exif_array[0][34665][0][36868]['Data'][0]; + $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] ); + } + // Check if there is a EXIF Tag 306 "Date and Time" + else if ( ( $Exif_array != FALSE ) && + ( array_key_exists( 0, $Exif_array ) ) && + ( array_key_exists( 306, $Exif_array[0] ) ) ) + { + // Tag "Date and Time" found - use it for the default date + $default_ps_file_info_array['date'] = $Exif_array[0][306]['Data'][0]; + $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] ); + } + else + { + // Couldn't find an EXIF date in the image + // Set default date as creation date of file + $default_ps_file_info_array['date'] = date ("Y-m-d", filectime( $filename )); + } + } + + // Cycle through all the elements of the default values array + foreach( $default_ps_file_info_array as $def_key =>$default_item ) + { + // Check if the current element is Keywords or + // Supplemental Categories as these are arrays + // and need to be treated differently + if ( ( strcasecmp( $def_key, "keywords" ) == 0 ) || + ( strcasecmp( $def_key, "supplementalcategories" ) == 0 ) ) + { + // Keywords or Supplemental Categories found + // Check if the File Info from the file is empty for this element + // and if there are default values in this array element + if ( ( count( $new_ps_file_info_array[ $def_key ] ) == 0 ) && + ( is_array( $default_item ) ) && + ( count( $default_item ) >= 0 ) ) + { + // The existing file info is empty, and there are + // defaults - add them + $new_ps_file_info_array[ $def_key ] = $default_item; + } + } + // Otherwise, this is not an array element, just check if it is blank in the existing file info + else if ( trim( $new_ps_file_info_array[ $def_key ] ) == "" ) + { + // The existing file info is blank, add the default value + $new_ps_file_info_array[ $def_key ] = $default_item; + } + + } + } + } + // Check for operation mode 4 - $new_ps_file_info_array and $filename are not defined, + else if ( ( ( !isset($new_ps_file_info_array) ) || ( ! is_array($new_ps_file_info_array) ) ) && + ( ( !isset($filename) ) || ( ! is_string( $filename ) ) ) ) + { + // No filename or new_ps_file_info_array defined, create a blank file info array to display + $new_ps_file_info_array = 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" => "" ); + } + + + +/*************************************************************************** +* +* Now output the actual HTML form +* +***************************************************************************/ + +?> + + + + + <form name="EditJPEG" action="Write_File_Info.php" method="post"> + + + <?php echo "<input name=\"filename\" type=\"hidden\" value=\"$outputfilename\">"; ?> + + <table> + + <tr> + <td> + Title + </td> + <td> + <?php + echo "<input size=49 name=\"title\" type=\"text\" value=\"". $new_ps_file_info_array[ 'title' ] ."\">"; + ?> + </td> + </tr> + + <tr> + <td> + Author + </td> + <td> + <?php + echo "<input size=49 name=\"author\" type=\"text\" value=\"". $new_ps_file_info_array[ 'author' ] ."\">"; + ?> + </td> + </tr> + + <tr> + <td> + Authors Position + </td> + <td> + <?php + echo "<input size=49 name=\"authorsposition\" type=\"text\" value=\"". $new_ps_file_info_array[ 'authorsposition' ] ."\"> - Note: not used in Photoshop 7 or higher"; + ?> + </td> + </tr> + + <tr> + <td> + Description + </td> + <td> + <textarea name="caption" rows=3 cols=37 wrap="off"><?php echo $new_ps_file_info_array[ 'caption' ]; ?></textarea> + </td> + </tr> + + <tr> + <td> + Description Writer + </td> + <td> + <?php + echo "<input size=49 name=\"captionwriter\" type=\"text\" value=\"". $new_ps_file_info_array[ 'captionwriter' ] ."\">"; + ?> + </td> + </tr> + + + <tr> + <td> + Keywords + </td> + <td> + <textarea name="keywords" rows=3 cols=37 wrap="off"><?php + foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword ) + { + echo "$keyword
"; + } + ?></textarea> + </td> + </tr> + + + <tr> + <td> + Copyright Status + </td> + <td> + <select size=1 name="copyrightstatus"> + <?php + $copystatus = $new_ps_file_info_array[ 'copyrightstatus' ]; + if ( $copystatus == "Unknown" ) + { + echo "<option value=\"Unknown\" SELECTED >Unknown</option>\n"; + } + else + { + echo "<option value=\"Unknown\">Unknown</option>\n"; + } + + if ( $copystatus == "Copyrighted Work" ) + { + echo "<option value=\"Copyrighted Work\" SELECTED >Copyrighted Work</option>\n"; + } + else + { + echo "<option value=\"Copyrighted Work\">Copyrighted Work</option>\n"; + } + + if ( $copystatus == "Public Domain" ) + { + echo "<option value=\"Public Domain\" SELECTED >Public Domain</option>\n"; + } + else + { + echo "<option value=\"Public Domain\">Public Domain</option>\n"; + } + ?> + </select> + </td> + </tr> + + + <tr> + <td> + Copyright Notice + </td> + <td> + <textarea name="copyrightnotice" rows=3 cols=37 wrap="off"><?php echo $new_ps_file_info_array[ 'copyrightnotice' ]; ?></textarea> + </td> + </tr> + + + <tr> + <td> + Copyright Info URL + </td> + <td> + <?php + echo "<input size=49 name=\"ownerurl\" type=\"text\" value=\"". $new_ps_file_info_array[ 'ownerurl' ] ."\">\n"; + if ($new_ps_file_info_array[ 'ownerurl' ] != "" ) + { + echo "<a href=\"". $new_ps_file_info_array[ 'ownerurl' ] ."\" > (". $new_ps_file_info_array[ 'ownerurl' ] .")</a>\n"; + } + ?> + + </td> + </tr> + + + <tr> + <td> + Category + </td> + <td> + <?php + echo "<input size=49 name=\"category\" type=\"text\" value=\"". $new_ps_file_info_array[ 'category' ] ."\">\n"; + ?> + + </td> + </tr> + + <tr> + <td> + Supplemental Categories + </td> + <td> + <textarea name="supplementalcategories" rows=3 cols=37 wrap="off"><?php + foreach( $new_ps_file_info_array[ 'supplementalcategories' ] as $supcat ) + { + echo "$supcat
"; + } + ?> + </textarea> + </td> + </tr> + + + + <tr> + <td> + Date Created + </td> + <td> + <?php + echo "<input size=49 name=\"date\" type=\"text\" value=\"". $new_ps_file_info_array[ 'date' ] ."\">"; + ?> + (Note date must be YYYY-MM-DD format) + </td> + </tr> + + <tr> + <td> + City + </td> + <td> + <?php + echo "<input size=49 name=\"city\" type=\"text\" value=\"". $new_ps_file_info_array[ 'city' ] ."\">"; + ?> + </td> + </tr> + + + <tr> + <td> + State + </td> + <td> + <?php + echo "<input size=49 name=\"state\" type=\"text\" value=\"". $new_ps_file_info_array[ 'state' ] ."\">"; + ?> + </td> + </tr> + + + <tr> + <td> + Country + </td> + <td> + <?php + echo "<input size=49 name=\"country\" type=\"text\" value=\"". $new_ps_file_info_array[ 'country' ] ."\">"; + ?> + </td> + </tr> + + + + <tr> + <td> + Credit + </td> + <td> + <?php + echo "<input size=49 name=\"credit\" type=\"text\" value=\"". $new_ps_file_info_array[ 'credit' ] ."\">"; + ?> + </td> + </tr> + + + <tr> + <td> + Source + </td> + <td> + <?php + echo "<input size=49 name=\"source\" type=\"text\" value=\"". $new_ps_file_info_array[ 'source' ] ."\">"; + ?> + </td> + </tr> + + + + <tr> + <td> + Headline + </td> + <td> + <textarea name="headline" rows=3 cols=37 wrap="off"><?php echo $new_ps_file_info_array[ 'headline' ]; ?></textarea> + </td> + </tr> + + + + <tr> + <td> + Instructions + </td> + <td> + <textarea name="instructions" rows=3 cols=37 wrap="off"><?php echo $new_ps_file_info_array[ 'instructions' ]; ?></textarea> + </td> + </tr> + + + <tr> + <td> + Transmission Reference + </td> + <td> + <textarea name="transmissionreference" rows=3 cols=37 wrap="off"><?php echo $new_ps_file_info_array[ 'transmissionreference' ]; ?></textarea> + </td> + </tr> + + <tr> + <td> + Job Name + </td> + <td> + <?php + echo "<input size=49 name=\"jobname\" type=\"text\" value=\"". $new_ps_file_info_array[ 'jobname' ] ."\"> - Note: not used in Photoshop CS"; + ?> + </td> + </tr> + + <tr> + <td> + Urgency + </td> + <td> + <select size="1" name="urgency"> + <?php + for( $i = 1; $i <= 8; $i++ ) + { + echo "<option value=\"$i\""; + if ( $new_ps_file_info_array[ 'urgency' ] == $i ) + { + echo " SELECTED "; + } + echo ">"; + if ( $i == 1 ) + { + echo "High"; + } + else if ( $i == 5 ) + { + echo "Normal"; + } + else if ( $i == 8 ) + { + echo "Low"; + } + else + { + echo "$i"; + } + echo "</option>\n"; + } + if ( $new_ps_file_info_array[ 'urgency' ] == "none" ) + { + echo "<option value=\"none\" SELECTED >None</option>"; + } + else + { + echo "<option value=\"none\" >None</option>"; + } + ?> + + </select> + </td> + </tr> + + </table> + <br> + <input type="submit" value="Update!"> + + + </form> + + <br> + <br> + <p>Powered by: <a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p> <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 --> + <br> + <br> diff --git a/includes/jpeg_metadata_tk/IPTC.php b/includes/jpeg_metadata_tk/IPTC.php new file mode 100644 index 0000000..4499c67 --- /dev/null +++ b/includes/jpeg_metadata_tk/IPTC.php @@ -0,0 +1,691 @@ +<?php + +/****************************************************************************** +* +* Filename: IPTC.php +* +* Description: Provides functions for reading and writing IPTC-NAA Information +* Interchange Model metadata +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.10 +* +* Changes: 1.00 -> 1.01 : changed get_IPTC to return partial data when error occurs +* 1.01 -> 1.10 : changed put_IPTC to check if the incoming IPTC block is valid +* changed Interpret_IPTC_to_HTML, adding nl2br functions for each text field, +* so that multiline text displays properly +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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: Not all of the IPTC fields have been tested properly + +/****************************************************************************** +* +* Function: get_IPTC +* +* Description: Extracts IPTC-NAA IIM data from the string provided, and returns +* the information as an array +* +* Parameters: Data_Str - the string containing the IPTC-NAA IIM records. Must +* be exact length of the IPTC-NAA IIM data. +* +* Returns: OutputArray - Array of IPTC-NAA IIM records +* FALSE - If an error occured in decoding +* +******************************************************************************/ + +function get_IPTC( $Data_Str ) +{ + + // Initialise the start position + $pos = 0; + // Create the array to receive the data + $OutputArray = array( ); + + // Cycle through the IPTC records, decoding and storing them + while( $pos < strlen($Data_Str) ) + { + // TODO - Extended Dataset record not supported + + // Check if there is sufficient data for reading the record + if ( strlen( substr($Data_Str,$pos) ) < 5 ) + { + // Not enough data left for a record - Probably corrupt data - ERROR + // Change: changed to return partial data as of revision 1.01 + return $OutputArray; + } + + // Unpack data from IPTC record: + // First byte - IPTC Tag Marker - always 28 + // Second byte - IPTC Record Number + // Third byte - IPTC Dataset Number + // Fourth and fifth bytes - two byte size value + $iptc_raw = unpack( "CIPTC_Tag_Marker/CIPTC_Record_No/CIPTC_Dataset_No/nIPTC_Size", substr($Data_Str,$pos) ); + + // Skip position over the unpacked data + $pos += 5; + + // Construct the IPTC type string eg 2:105 + $iptctype = sprintf( "%01d:%02d", $iptc_raw['IPTC_Record_No'], $iptc_raw['IPTC_Dataset_No']); + + // Check if there is sufficient data for reading the record contents + if ( strlen( substr( $Data_Str, $pos, $iptc_raw['IPTC_Size'] ) ) !== $iptc_raw['IPTC_Size'] ) + { + // Not enough data left for the record content - Probably corrupt data - ERROR + // Change: changed to return partial data as of revision 1.01 + return $OutputArray; + } + + // Add the IPTC record to the output array + $OutputArray[] = array( "IPTC_Type" => $iptctype , + "RecName" => (isset( $GLOBALS["IPTC_Entry_Names"][$iptctype] ) ? $GLOBALS["IPTC_Entry_Names"][$iptctype] : ''), + "RecDesc" => (isset( $GLOBALS["IPTC_Entry_Descriptions"][$iptctype] ) ? $GLOBALS[ "IPTC_Entry_Descriptions" ][ $iptctype ] : ''), + "RecData" => substr( $Data_Str, $pos, $iptc_raw['IPTC_Size'] ) ); + + // Skip over the IPTC record data + $pos += $iptc_raw['IPTC_Size']; + } + return $OutputArray; + +} + + +/****************************************************************************** +* End of Function: get_IPTC +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: put_IPTC +* +* Description: Encodes an array of IPTC-NAA records into a string encoded +* as IPTC-NAA IIM. (The reverse of get_IPTC) +* +* Parameters: new_IPTC_block - the IPTC-NAA array to be encoded. Should be +* the same format as that received from get_IPTC +* +* Returns: iptc_packed_data - IPTC-NAA IIM encoded string +* +******************************************************************************/ + + +function put_IPTC( $new_IPTC_block ) +{ + // Check if the incoming IPTC block is valid + if ( $new_IPTC_block == FALSE ) + { + // Invalid IPTC block - abort + return FALSE; + } + // Initialise the packed output data string + $iptc_packed_data = ""; + + // Cycle through each record in the new IPTC block + foreach ($new_IPTC_block as $record) + { + // Extract the Record Number and Dataset Number from the IPTC_Type field + list($IPTC_Record, $IPTC_Dataset) = sscanf( $record['IPTC_Type'], "%d:%d"); + + // Write the IPTC-NAA IIM Tag Marker, Record Number, Dataset Number and Data Size to the packed output data string + $iptc_packed_data .= pack( "CCCn", 28, $IPTC_Record, $IPTC_Dataset, strlen($record['RecData']) ); + + // Write the IPTC-NAA IIM Data to the packed output data string + $iptc_packed_data .= $record['RecData']; + } + + // Return the IPTC-NAA IIM data + return $iptc_packed_data; +} + +/****************************************************************************** +* End of Function: put_IPTC +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: Interpret_IPTC_to_HTML +* +* Description: Generates html detailing the contents a IPTC-NAA IIM array +* which was retrieved with the get_IPTC function +* +* Parameters: IPTC_info - the IPTC-NAA IIM array,as read from get_IPTC +* +* Returns: OutputStr - A string containing the HTML +* +******************************************************************************/ + +function Interpret_IPTC_to_HTML( $IPTC_info ) +{ + // Create a string to receive the HTML + $output_str =""; + + // Check if the IPTC + if ( $IPTC_info !== FALSE ) + { + + + // Add Heading to HTML + $output_str .= "<h3 class=\"IPTC_Main_Heading\">IPTC-NAA Record</h3>\n"; + + // Add Table to HTML + $output_str .= "\n<table class=\"IPTC_Table\" border=1>\n"; + + // Cycle through each of the IPTC-NAA IIM records + foreach( $IPTC_info as $IPTC_Record ) + { + // Check if the record is a known IPTC field + $Record_Name = $IPTC_Record['RecName']; + if ( $Record_Name == "" ) + { + // Record is an unknown field - add message to HTML + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">Unknown IPTC field '". htmlentities( $IPTC_Record['IPTC_Type'] ). "' :</td><td class=\"IPTC_Value_Cell\">" . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n"; + } + else + { + // Record is a recognised IPTC field - Process it accordingly + + switch ( $IPTC_Record['IPTC_Type'] ) + { + case "1:00": // Envelope Record:Model Version + case "1:22": // Envelope Record:File Format Version + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">" . hexdec( bin2hex( $IPTC_Record['RecData'] ) ) ."</td></tr>\n"; + break; + + case "1:90": // Envelope Record:Coded Character Set + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Decoding not yet implemented<br>\n (Hex Data: " . bin2hex( $IPTC_Record['RecData'] ) .")</td></tr>\n"; + break; + // TODO: Implement decoding of IPTC record 1:90 + + case "1:20": // Envelope Record:File Format + + $formatno = hexdec( bin2hex( $IPTC_Record['RecData'] ) ); + + // Lookup file format from lookup-table + if ( array_key_exists( $formatno, $GLOBALS[ "IPTC_File Formats" ] ) ) + { + // Entry was found in lookup table - add it to HTML + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">File Format</td><td class=\"IPTC_Value_Cell\">". $GLOBALS[ "IPTC_File Formats" ][$formatno] . "</td></tr>\n"; + } + else + { + // No matching entry was found in lookup table - add message to html + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">File Format</td><td class=\"IPTC_Value_Cell\">Unknown File Format ($formatno)</td></tr>\n"; + } + break; + + + case "2:00": // Application Record:Record Version + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">IPTC Version</td><td class=\"IPTC_Value_Cell\">" . hexdec( bin2hex( $IPTC_Record['RecData'] ) ) ."</td></tr>\n"; + break; + + case "2:42": // Application Record: Action Advised + + // Looup Action + if ( $IPTC_Record['RecData'] == "01" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Kill</td></tr>\n"; + } + elseif ( $IPTC_Record['RecData'] == "02" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Replace</td></tr>\n"; + } + elseif ( $IPTC_Record['RecData'] == "03" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Append</td></tr>\n"; + } + elseif ( $IPTC_Record['RecData'] == "04" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Reference</td></tr>\n"; + } + else + { + // Unknown Action + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n"; + } + break; + + case "2:08": // Application Record:Editorial Update + if ( $IPTC_Record['RecData'] == "01" ) + { + // Additional Language + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Additional language</td></tr>\n"; + } + else + { + // Unknown Value + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n"; + } + break; + + case "2:30": // Application Record:Release Date + case "2:37": // Application Record:Expiration Date + case "2:47": // Application Record:Reference Date + case "2:55": // Application Record:Date Created + case "2:62": // Application Record:Digital Creation Date + case "1:70": // Envelope Record:Date Sent + $date_array = unpack( "a4Year/a2Month/A2Day", $IPTC_Record['RecData'] ); + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">" . nl2br( HTML_UTF8_Escape( $date_array['Day'] . "/" . $date_array['Month'] . "/" . $date_array['Year'] ) ) ."</td></tr>\n"; + break; + + case "2:35": // Application Record:Release Time + case "2:38": // Application Record:Expiration Time + case "2:60": // Application Record:Time Created + case "2:63": // Application Record:Digital Creation Time + case "1:80": // Envelope Record:Time Sent + $time_array = unpack( "a2Hour/a2Minute/A2Second/APlusMinus/A4Timezone", $IPTC_Record['RecData'] ); + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">" . nl2br( HTML_UTF8_Escape( $time_array['Hour'] . ":" . $time_array['Minute'] . ":" . $time_array['Second'] . " ". $time_array['PlusMinus'] . $time_array['Timezone'] ) ) ."</td></tr>\n"; + break; + + case "2:75": // Application Record:Object Cycle + // Lookup Value + if ( $IPTC_Record['RecData'] == "a" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Morning</td></tr>\n"; + } + elseif ( $IPTC_Record['RecData'] == "p" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Evening</td></tr>\n"; + } + elseif ( $IPTC_Record['RecData'] == "b" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Both Morning and Evening</td></tr>\n"; + } + else + { + // Unknown Value + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n"; + } + break; + + case "2:125": // Application Record:Rasterised Caption + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">460x128 pixel black and white caption image</td></tr>\n"; + break; + // TODO: Display Rasterised Caption for IPTC record 2:125 + + case "2:130": // Application Record:Image Type + // Lookup Number of Components + if ( $IPTC_Record['RecData']{0} == "0" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">No Objectdata"; + } + elseif ( $IPTC_Record['RecData']{0} == "9" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Supplemental objects related to other objectdata"; + } + else + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Number of Colour Components : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData']{0} ) ); + } + + // Lookup current objectdata colour + if ( $GLOBALS['ImageType_Names'][ $IPTC_Record['RecData']{1} ] == "" ) + { + $output_str .= ", Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData']{1} ) ); + } + else + { + $output_str .= ", " . nl2br( HTML_UTF8_Escape( $GLOBALS['ImageType_Names'][ $IPTC_Record['RecData']{1} ] ) ); + } + $output_str .= "</td></tr>\n"; + break; + + case "2:131": // Application Record:Image Orientation + // Lookup value + if ( $IPTC_Record['RecData'] == "L" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Landscape</td></tr>\n"; + } + elseif ( $IPTC_Record['RecData'] == "P" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Portrait</td></tr>\n"; + } + elseif ( $IPTC_Record['RecData'] == "S" ) + { + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Square</td></tr>\n"; + } + else + { + // Unknown Orientation Value + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n"; + } + break; + + default: // All other records + $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">" .nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n"; + break; + } + } + } + + // Add Table End to HTML + $output_str .= "</table><br>\n"; + } + + // Return HTML + return $output_str; +} + + +/****************************************************************************** +* End of Function: Interpret_IPTC_to_HTML +******************************************************************************/ + + + +/****************************************************************************** +* Global Variable: IPTC_Entry_Names +* +* Contents: The names of the IPTC-NAA IIM fields +* +******************************************************************************/ + +$GLOBALS[ "IPTC_Entry_Names" ] = array( +// Envelope Record +"1:00" => "Model Version", +"1:05" => "Destination", +"1:20" => "File Format", +"1:22" => "File Format Version", +"1:30" => "Service Identifier", +"1:40" => "Envelope Number", +"1:50" => "Product ID", +"1:60" => "Envelope Priority", +"1:70" => "Date Sent", +"1:80" => "Time Sent", +"1:90" => "Coded Character Set", +"1:100" => "UNO (Unique Name of Object)", +"1:120" => "ARM Identifier", +"1:122" => "ARM Version", + +// Application Record +"2:00" => "Record Version", +"2:03" => "Object Type Reference", +"2:05" => "Object Name (Title)", +"2:07" => "Edit Status", +"2:08" => "Editorial Update", +"2:10" => "Urgency", +"2:12" => "Subject Reference", +"2:15" => "Category", +"2:20" => "Supplemental Category", +"2:22" => "Fixture Identifier", +"2:25" => "Keywords", +"2:26" => "Content Location Code", +"2:27" => "Content Location Name", +"2:30" => "Release Date", +"2:35" => "Release Time", +"2:37" => "Expiration Date", +"2:35" => "Expiration Time", +"2:40" => "Special Instructions", +"2:42" => "Action Advised", +"2:45" => "Reference Service", +"2:47" => "Reference Date", +"2:50" => "Reference Number", +"2:55" => "Date Created", +"2:60" => "Time Created", +"2:62" => "Digital Creation Date", +"2:63" => "Digital Creation Time", +"2:65" => "Originating Program", +"2:70" => "Program Version", +"2:75" => "Object Cycle", +"2:80" => "By-Line (Author)", +"2:85" => "By-Line Title (Author Position) [Not used in Photoshop 7]", +"2:90" => "City", +"2:92" => "Sub-Location", +"2:95" => "Province/State", +"2:100" => "Country/Primary Location Code", +"2:101" => "Country/Primary Location Name", +"2:103" => "Original Transmission Reference", +"2:105" => "Headline", +"2:110" => "Credit", +"2:115" => "Source", +"2:116" => "Copyright Notice", +"2:118" => "Contact", +"2:120" => "Caption/Abstract", +"2:122" => "Caption Writer/Editor", +"2:125" => "Rasterized Caption", +"2:130" => "Image Type", +"2:131" => "Image Orientation", +"2:135" => "Language Identifier", +"2:150" => "Audio Type", +"2:151" => "Audio Sampling Rate", +"2:152" => "Audio Sampling Resolution", +"2:153" => "Audio Duration", +"2:154" => "Audio Outcue", +"2:200" => "ObjectData Preview File Format", +"2:201" => "ObjectData Preview File Format Version", +"2:202" => "ObjectData Preview Data", + +// Pre-ObjectData Descriptor Record +"7:10" => "Size Mode", +"7:20" => "Max Subfile Size", +"7:90" => "ObjectData Size Announced", +"7:95" => "Maximum ObjectData Size", + +// ObjectData Record +"8:10" => "Subfile", + +// Post ObjectData Descriptor Record +"9:10" => "Confirmed ObjectData Size" + +); + +/****************************************************************************** +* End of Global Variable: IPTC_Entry_Names +******************************************************************************/ + + + + + +/****************************************************************************** +* Global Variable: IPTC_Entry_Descriptions +* +* Contents: The Descriptions of the IPTC-NAA IIM fields +* +******************************************************************************/ + +$GLOBALS[ "IPTC_Entry_Descriptions" ] = array( +// Envelope Record +"1:00" => "2 byte binary version number", +"1:05" => "Max 1024 characters of Destination", +"1:20" => "2 byte binary file format number, see IPTC-NAA V4 Appendix A", +"1:22" => "Binary version number of file format", +"1:30" => "Max 10 characters of Service Identifier", +"1:40" => "8 Character Envelope Number", +"1:50" => "Product ID - Max 32 characters", +"1:60" => "Envelope Priority - 1 numeric characters", +"1:70" => "Date Sent - 8 numeric characters CCYYMMDD", +"1:80" => "Time Sent - 11 characters HHMMSS±HHMM", +"1:90" => "Coded Character Set - Max 32 characters", +"1:100" => "UNO (Unique Name of Object) - 14 to 80 characters", +"1:120" => "ARM Identifier - 2 byte binary number", +"1:122" => "ARM Version - 2 byte binary number", + +// Application Record +"2:00" => "Record Version - 2 byte binary number", +"2:03" => "Object Type Reference - 3 plus 0 to 64 Characters", +"2:05" => "Object Name (Title) - Max 64 characters", +"2:07" => "Edit Status - Max 64 characters", +"2:08" => "Editorial Update - 2 numeric characters", +"2:10" => "Urgency - 1 numeric character", +"2:12" => "Subject Reference - 13 to 236 characters", +"2:15" => "Category - Max 3 characters", +"2:20" => "Supplemental Category - Max 32 characters", +"2:22" => "Fixture Identifier - Max 32 characters", +"2:25" => "Keywords - Max 64 characters", +"2:26" => "Content Location Code - 3 characters", +"2:27" => "Content Location Name - Max 64 characters", +"2:30" => "Release Date - 8 numeric characters CCYYMMDD", +"2:35" => "Release Time - 11 characters HHMMSS±HHMM", +"2:37" => "Expiration Date - 8 numeric characters CCYYMMDD", +"2:35" => "Expiration Time - 11 characters HHMMSS±HHMM", +"2:40" => "Special Instructions - Max 256 Characters", +"2:42" => "Action Advised - 2 numeric characters", +"2:45" => "Reference Service - Max 10 characters", +"2:47" => "Reference Date - 8 numeric characters CCYYMMDD", +"2:50" => "Reference Number - 8 characters", +"2:55" => "Date Created - 8 numeric characters CCYYMMDD", +"2:60" => "Time Created - 11 characters HHMMSS±HHMM", +"2:62" => "Digital Creation Date - 8 numeric characters CCYYMMDD", +"2:63" => "Digital Creation Time - 11 characters HHMMSS±HHMM", +"2:65" => "Originating Program - Max 32 characters", +"2:70" => "Program Version - Max 10 characters", +"2:75" => "Object Cycle - 1 character", +"2:80" => "By-Line (Author) - Max 32 Characters", +"2:85" => "By-Line Title (Author Position) - Max 32 characters", +"2:90" => "City - Max 32 Characters", +"2:92" => "Sub-Location - Max 32 characters", +"2:95" => "Province/State - Max 32 Characters", +"2:100" => "Country/Primary Location Code - 3 alphabetic characters", +"2:101" => "Country/Primary Location Name - Max 64 characters", +"2:103" => "Original Transmission Reference - Max 32 characters", +"2:105" => "Headline - Max 256 Characters", +"2:110" => "Credit - Max 32 Characters", +"2:115" => "Source - Max 32 Characters", +"2:116" => "Copyright Notice - Max 128 Characters", +"2:118" => "Contact - Max 128 characters", +"2:120" => "Caption/Abstract - Max 2000 Characters", +"2:122" => "Caption Writer/Editor - Max 32 Characters", +"2:125" => "Rasterized Caption - 7360 bytes, 1 bit per pixel, 460x128pixel image", +"2:130" => "Image Type - 2 characters", +"2:131" => "Image Orientation - 1 alphabetic character", +"2:135" => "Language Identifier - 2 or 3 aphabetic characters", +"2:150" => "Audio Type - 2 characters", +"2:151" => "Audio Sampling Rate - 6 numeric characters", +"2:152" => "Audio Sampling Resolution - 2 numeric characters", +"2:153" => "Audio Duration - 6 numeric characters", +"2:154" => "Audio Outcue - Max 64 characters", +"2:200" => "ObjectData Preview File Format - 2 byte binary number", +"2:201" => "ObjectData Preview File Format Version - 2 byte binary number", +"2:202" => "ObjectData Preview Data - Max 256000 binary bytes", + +// Pre-ObjectData Descriptor Record +"7:10" => "Size Mode - 1 numeric character", +"7:20" => "Max Subfile Size", +"7:90" => "ObjectData Size Announced", +"7:95" => "Maximum ObjectData Size", + +// ObjectData Record +"8:10" => "Subfile", + +// Post ObjectData Descriptor Record +"9:10" => "Confirmed ObjectData Size" + +); + +/****************************************************************************** +* End of Global Variable: IPTC_Entry_Descriptions +******************************************************************************/ + + + + +/****************************************************************************** +* Global Variable: IPTC_File Formats +* +* Contents: The names of the IPTC-NAA IIM File Formats for field 1:20 +* +******************************************************************************/ + +$GLOBALS[ "IPTC_File Formats" ] = array( +00 => "No ObjectData", +01 => "IPTC-NAA Digital Newsphoto Parameter Record", +02 => "IPTC7901 Recommended Message Format", +03 => "Tagged Image File Format (Adobe/Aldus Image data)", +04 => "Illustrator (Adobe Graphics data)", +05 => "AppleSingle (Apple Computer Inc)", +06 => "NAA 89-3 (ANPA 1312)", +07 => "MacBinary II", +08 => "IPTC Unstructured Character Oriented File Format (UCOFF)", +09 => "United Press International ANPA 1312 variant", +10 => "United Press International Down-Load Message", +11 => "JPEG File Interchange (JFIF)", +12 => "Photo-CD Image-Pac (Eastman Kodak)", +13 => "Microsoft Bit Mapped Graphics File [*.BMP]", +14 => "Digital Audio File [*.WAV] (Microsoft & Creative Labs)", +15 => "Audio plus Moving Video [*.AVI] (Microsoft)", +16 => "PC DOS/Windows Executable Files [*.COM][*.EXE]", +17 => "Compressed Binary File [*.ZIP] (PKWare Inc)", +18 => "Audio Interchange File Format AIFF (Apple Computer Inc)", +19 => "RIFF Wave (Microsoft Corporation)", +20 => "Freehand (Macromedia/Aldus)", +21 => "Hypertext Markup Language - HTML (The Internet Society)", +22 => "MPEG 2 Audio Layer 2 (Musicom), ISO/IEC", +23 => "MPEG 2 Audio Layer 3, ISO/IEC", +24 => "Portable Document File (*.PDF) Adobe", +25 => "News Industry Text Format (NITF)", +26 => "Tape Archive (*.TAR)", +27 => "Tidningarnas Telegrambyrå NITF version (TTNITF DTD)", +28 => "Ritzaus Bureau NITF version (RBNITF DTD)", +29 => "Corel Draw [*.CDR]" +); + + +/****************************************************************************** +* End of Global Variable: IPTC_File Formats +******************************************************************************/ + +/****************************************************************************** +* Global Variable: ImageType_Names +* +* Contents: The names of the colour components for IPTC-NAA IIM field 2:130 +* +******************************************************************************/ + +$GLOBALS['ImageType_Names'] = array( "M" => "Monochrome", + "Y" => "Yellow Component", + "M" => "Magenta Component", + "C" => "Cyan Component", + "K" => "Black Component", + "R" => "Red Component", + "G" => "Green Component", + "B" => "Blue Component", + "T" => "Text Only", + "F" => "Full colour composite, frame sequential", + "L" => "Full colour composite, line sequential", + "P" => "Full colour composite, pixel sequential", + "S" => "Full colour composite, special interleaving" ); + + + +/****************************************************************************** +* End of Global Variable: ImageType_Names +******************************************************************************/ + +?> diff --git a/includes/jpeg_metadata_tk/JFIF.php b/includes/jpeg_metadata_tk/JFIF.php new file mode 100644 index 0000000..90f314f --- /dev/null +++ b/includes/jpeg_metadata_tk/JFIF.php @@ -0,0 +1,438 @@ +<?php + +/****************************************************************************** +* +* Filename: JFIF.php +* +* Description: Provides functions for reading and writing information to/from +* JPEG File Interchange Format (JFIF) segments and +* JFIF Extension (JFXX) segments within a JPEG file. +* +* Author: Evan Hunter +* +* Date: 24/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* Changes: 1.00 -> 1.11 : changed Interpret_JFXX_to_HTML to allow thumbnail links to work when +* toolkit is portable across directories +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + +include_once 'pjmt_utils.php'; // Change: as of version 1.11 - added to allow directory portability + +/****************************************************************************** +* +* Function: get_JFIF +* +* Description: Retrieves information from a JPEG File Interchange Format (JFIF) +* segment and returns it in an array. Uses information supplied by +* the get_jpeg_header_data function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: JFIF_data - an array of JFIF data +* FALSE - if a JFIF segment could not be found +* +******************************************************************************/ + +function get_JFIF( $jpeg_header_data ) +{ + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP0 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) + { + // And if it has the JFIF label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 ) + { + // Found a JPEG File Interchange Format (JFIF) Block + + // unpack the JFIF data from the incoming string + // First is the JFIF label string + // Then a two byte version number + // Then a byte, units identifier, ( 0 = aspect ration, 1 = dpi, 2 = dpcm) + // Then a two byte int X-Axis pixel Density (resolution) + // Then a two byte int Y-Axis pixel Density (resolution) + // Then a byte X-Axis JFIF thumbnail size + // Then a byte Y-Axis JFIF thumbnail size + // Then the uncompressed RGB JFIF thumbnail data + + $JFIF_data = unpack( 'a5JFIF/C2Version/CUnits/nXDensity/nYDensity/CThumbX/CThumbY/a*ThumbData', $jpeg_header_data[$i]['SegData'] ); + + return $JFIF_data; + } + } + } + return FALSE; +} + +/****************************************************************************** +* End of Function: get_JFIF +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: put_JFIF +* +* Description: Creates a new JFIF segment from an array of JFIF data in the +* same format as would be retrieved from get_JFIF, and inserts +* this segment into the supplied JPEG header array +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data, into which the +* new JFIF segment will be put +* new_JFIF_array - a JFIF information array in the same format as +* from get_JFIF, to create the new segment +* +* Returns: jpeg_header_data - the JPEG header data array with the new +* JFIF segment added +* +******************************************************************************/ + +function put_JFIF( $jpeg_header_data, $new_JFIF_array ) +{ + // pack the JFIF data into its proper format for a JPEG file + $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", $new_JFIF_array['Version1'], $new_JFIF_array['Version2'], $new_JFIF_array['Units'], $new_JFIF_array['XDensity'], $new_JFIF_array['YDensity'], $new_JFIF_array['ThumbX'], $new_JFIF_array['ThumbY'], $new_JFIF_array['ThumbData'] ); + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP0 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) + { + // And if it has the JFIF label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 ) + { + // Found a preexisting JFIF block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = $packed_data; + return $jpeg_header_data; + } + } + } + + // No preexisting JFIF block found, insert a new one at the start of the header data. + array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, + "SegName" => "APP0", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], + "SegData" => $packed_data ) ) ); + return $jpeg_header_data; +} + +/****************************************************************************** +* End of Function: put_JFIF +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: Interpret_JFIF_to_HTML +* +* Description: Generates html showing the JFIF information contained in +* a JFIF data array, as retrieved with get_JFIF +* +* Parameters: JFIF_array - a JFIF data array, as from get_JFIF +* filename - the name of the JPEG file being processed ( used +* by the script which displays the JFIF thumbnail) +* +* +* Returns: output - the HTML string +* +******************************************************************************/ + +function Interpret_JFIF_to_HTML( $JFIF_array, $filename ) +{ + $output = ""; + if ( $JFIF_array !== FALSE ) + { + $output .= "<H2 class=\"JFIF_Main_Heading\">Contains JPEG File Interchange Format (JFIF) Information</H2>\n"; + $output .= "\n<table class=\"JFIF_Table\" border=1>\n"; + $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">JFIF version: </td><td class=\"JFIF_Value_Cell\">". sprintf( "%d.%02d", $JFIF_array['Version1'], $JFIF_array['Version2'] ) . "</td></tr>\n"; + if ( $JFIF_array['Units'] == 0 ) + { + $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Pixel Aspect Ratio: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . "</td></tr>\n"; + } + elseif ( $JFIF_array['Units'] == 1 ) + { + $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Resolution: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per inch</td></tr>\n"; + } + elseif ( $JFIF_array['Units'] == 2 ) + { + $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Resolution: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per cm</td></tr>\n"; + } + + $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">JFIF (uncompressed) thumbnail: </td><td class=\"JFIF_Value_Cell\">"; + if ( ( $JFIF_array['ThumbX'] != 0 ) && ( $JFIF_array['ThumbY'] != 0 ) ) + { + $output .= $JFIF_array['ThumbX'] ." x " . $JFIF_array['ThumbY'] . " pixels, Thumbnail Display Not Yet Implemented</td></tr>\n"; + // TODO Implement JFIF Thumbnail display + } + else + { + $output .= "None</td></tr>\n"; + } + + $output .= "</table><br>\n"; + } + + return $output; + +} + + +/****************************************************************************** +* End of Function: Interpret_JFIF_to_HTML +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* +* Function: get_JFXX +* +* Description: Retrieves information from a JPEG File Interchange Format Extension (JFXX) +* segment and returns it in an array. Uses information supplied by +* the get_jpeg_header_data function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: JFXX_data - an array of JFXX data +* FALSE - if a JFXX segment could not be found +* +******************************************************************************/ + +function get_JFXX( $jpeg_header_data ) +{ + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP0 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) + { + // And if it has the JFIF label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 ) + { + // Found a JPEG File Interchange Format Extension (JFXX) Block + + // unpack the JFXX data from the incoming string + // First is the 5 byte JFXX label string + // Then a 1 byte Extension code, indicating Thumbnail Format + // Then the thumbnail data + + $JFXX_data = unpack( 'a5JFXX/CExtension_Code/a*ThumbData', $jpeg_header_data[$i]['SegData'] ); + return $JFXX_data; + } + } + } + return FALSE; +} + +/****************************************************************************** +* End of Function: get_JFXX +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: put_JFXX +* +* Description: Creates a new JFXX segment from an array of JFXX data in the +* same format as would be retrieved from get_JFXX, and inserts +* this segment into the supplied JPEG header array +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data, into which the +* new JFXX segment will be put +* new_JFXX_array - a JFXX information array in the same format as +* from get_JFXX, to create the new segment +* +* Returns: jpeg_header_data - the JPEG header data array with the new +* JFXX segment added +* +******************************************************************************/ + +function put_JFXX( $jpeg_header_data, $new_JFXX_array ) +{ + // pack the JFXX data into its proper format for a JPEG file + $packed_data = pack( 'a5Ca*',"JFXX\x00", $new_JFXX_array['Extension_Code'], $new_JFXX_array['ThumbData'] ); + + $JFIF_pos = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP0 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 ) + { + // And if it has the JFXX label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 ) + { + // Found a preexisting JFXX block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = $packed_data; + return $jpeg_header_data; + } + + // if it has the JFIF label, + if( strncmp ( $jpeg_header_data[$i][SegData], "JFIF\x00", 5) == 0 ) + { + // Found a preexisting JFIF block - Mark it in case we need to insert the JFXX after it + $JFIF_pos = $i; + } + } + } + + + // No preexisting JFXX block found + + // Check if a JFIF segment was found, + if ( $JFIF_pos !== -1 ) + { + // A pre-existing JFIF segment was found, + // insert the new JFXX segment after it. + array_splice($jpeg_header_data, $JFIF_pos +1 , 0, array ( array( "SegType" => 0xE0, + "SegName" => "APP0", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], + "SegData" => $packed_data ) ) ); + + } + else + { + // No pre-existing JFIF segment was found, + // insert a new JFIF and the new JFXX segment at the start of the array. + + // Insert new JFXX segment + array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, + "SegName" => "APP0", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], + "SegData" => $packed_data ) ) ); + + // Create a new JFIF to be inserted at the start of + // the array, with generic values + $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", 1, 2, 1, 72, 72, 0, 0, "" ); + + array_splice($jpeg_header_data, 0 , 0, array( array( "SegType" => 0xE0, + "SegName" => "APP0", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ], + "SegData" => $packed_data ) ) ); + } + + + return $jpeg_header_data; +} + +/****************************************************************************** +* End of Function: put_JFIF +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: Interpret_JFXX_to_HTML +* +* Description: Generates html showing the JFXX thumbnail contained in +* a JFXX data array, as retrieved with get_JFXX +* +* Parameters: JFXX_array - a JFXX information array in the same format as +* from get_JFXX, to create the new segment +* filename - the name of the JPEG file being processed ( used +* by the script which displays the JFXX thumbnail) +* +* Returns: output - the Html string +* +******************************************************************************/ + +function Interpret_JFXX_to_HTML( $JFXX_array, $filename ) +{ + $output = ""; + if ( $JFXX_array !== FALSE ) + { + $output .= "<H2 class=\"JFXX_Main_Heading\">Contains JPEG File Interchange Extension Format (JFXX) Thumbnail</H2>\n"; + switch ( $JFXX_array['Extension_Code'] ) + { + case 0x10 : $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is JPEG Encoded</p>\n"; + + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/get_JFXX_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ); + + // Add thumbnail link to html + $output .= "<a class=\"JFXX_Thumbnail_Link\" href=\"$link_str\"><img class=\"JFXX_Thumbnail\" src=\"$link_str\"></a>\n"; + break; + case 0x11 : $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded 1 byte/pixel</p>\n"; + $output .= "<p class=\"JFXX_Text\">Thumbnail Display Not Implemented Yet</p>\n"; + break; + case 0x13 : $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded 3 bytes/pixel</p>\n"; + $output .= "<p class=\"JFXX_Text\">Thumbnail Display Not Implemented Yet</p>\n"; + break; + default : $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded with Unknown format</p>\n"; + break; + + // TODO: Implement JFXX one and three bytes per pixel thumbnail decoding + } + + } + + return $output; + +} + +/****************************************************************************** +* End of Function: Interpret_JFXX_to_HTML +******************************************************************************/ + + + + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/JPEG.php b/includes/jpeg_metadata_tk/JPEG.php new file mode 100644 index 0000000..2004192 --- /dev/null +++ b/includes/jpeg_metadata_tk/JPEG.php @@ -0,0 +1,973 @@ +<?php + +/****************************************************************************** +* +* Filename: JPEG.php +* +* Description: Provides functions for reading and writing information to/from +* JPEG format files +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.10 +* +* Changes: 1.00 -> 1.10 : changed put_jpeg_header_data to check if the data +* being written exists +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_jpeg_header_data +* +* Description: Reads all the JPEG header segments from an JPEG image file into an +* array +* +* Parameters: filename - the filename of the file to JPEG file to read +* +* Returns: headerdata - Array of JPEG header segments +* FALSE - if headers could not be read +* +******************************************************************************/ + +function get_jpeg_header_data( $filename ) +{ + + // prevent refresh from aborting file operations and hosing file + ignore_user_abort(true); + + + // Attempt to open the jpeg file - the at symbol supresses the error message about + // not being able to open files. The file_exists would have been used, but it + // does not work with files fetched over http or ftp. + $filehnd = @fopen($filename, 'rb'); + + // Check if the file opened successfully + if ( ! $filehnd ) + { + // Could't open the file - exit + error_log( "Could not open file $filename" ); + return FALSE; + } + + + // Read the first two characters + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the first two characters are 0xFF 0xDA (SOI - Start of image) + if ( $data != "\xFF\xD8" ) + { + // No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return; + error_log( "This probably is not a JPEG file" ); + fclose($filehnd); + return FALSE; + } + + + // Read the third character + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the third character is 0xFF (Start of first segment header) + if ( $data{0} != "\xFF" ) + { + // NO FF found - close file and return - JPEG is probably corrupted + fclose($filehnd); + return FALSE; + } + + // Flag that we havent yet hit the compressed image data + $hit_compressed_image_data = FALSE; + + + // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit, + // 2) we have hit the compressed image data (no more headers are allowed after data) + // 3) or end of file is hit + + while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) )) + { + // Found a segment to look at. + // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them + if ( ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) ) + { + // Segment isn't a Restart marker + // Read the next two bytes (size) + $sizestr = network_safe_fread( $filehnd, 2 ); + + // convert the size bytes to an integer + $decodedsize = unpack ("nsize", $sizestr); + + // Save the start position of the data + $segdatastart = ftell( $filehnd ); + + // Read the segment data with length indicated by the previously read size + $segdata = network_safe_fread( $filehnd, $decodedsize['size'] - 2 ); + + + // Store the segment information in the output array + $headerdata[] = array( "SegType" => ord($data{1}), + "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ ord($data{1}) ], + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ ord($data{1}) ], + "SegDataStart" => $segdatastart, + "SegData" => $segdata ); + } + + // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows + if ( $data{1} == "\xDA" ) + { + // Flag that we have hit the compressed image data - exit loop as no more headers available. + $hit_compressed_image_data = TRUE; + } + else + { + // Not an SOS - Read the next two bytes - should be the segment marker for the next segment + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the first byte of the two is 0xFF as it should be for a marker + if ( $data{0} != "\xFF" ) + { + // NO FF found - close file and return - JPEG is probably corrupted + fclose($filehnd); + return FALSE; + } + } + } + + // Close File + fclose($filehnd); + // Alow the user to abort from now on + ignore_user_abort(false); + + // Return the header data retrieved + return $headerdata; +} + + +/****************************************************************************** +* End of Function: get_jpeg_header_data +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: put_jpeg_header_data +* +* Description: Writes JPEG header data into a JPEG file. Takes an array in the +* same format as from get_jpeg_header_data, and combines it with +* the image data of an existing JPEG file, to create a new JPEG file +* WARNING: As this function will replace all JPEG headers, +* including SOF etc, it is best to read the jpeg headers +* from a file, alter them, then put them back on the same +* file. If a SOF segment wer to be transfered from one +* file to another, the image could become unreadable unless +* the images were idenical size and configuration +* +* +* Parameters: old_filename - the JPEG file from which the image data will be retrieved +* new_filename - the name of the new JPEG to create (can be same as old_filename) +* jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: TRUE - on Success +* FALSE - on Failure +* +******************************************************************************/ + +function put_jpeg_header_data( $old_filename, $new_filename, $jpeg_header_data ) +{ + + // Change: added check to ensure data exists, as of revision 1.10 + // Check if the data to be written exists + if ( $jpeg_header_data == FALSE ) + { + // Data to be written not valid - abort + return FALSE; + } + + // extract the compressed image data from the old file + $compressed_image_data = get_jpeg_image_data( $old_filename ); + + // Check if the extraction worked + if ( ( $compressed_image_data === FALSE ) || ( $compressed_image_data === NULL ) ) + { + // Couldn't get image data from old file + return FALSE; + } + + + // Cycle through new headers + foreach ($jpeg_header_data as $segno => $segment) + { + // Check that this header is smaller than the maximum size + if ( strlen($segment['SegData']) > 0xfffd ) + { + // Could't open the file - exit + error_log( "A Header is too large to fit in JPEG segment" ); + return FALSE; + } + } + + ignore_user_abort(true); ## prevent refresh from aborting file operations and hosing file + + + // Attempt to open the new jpeg file + $newfilehnd = @fopen($new_filename, 'wb'); + // Check if the file opened successfully + if ( ! $newfilehnd ) + { + // Could't open the file - exit + error_log( "Could not open file $new_filename" ); + return FALSE; + } + + // Write SOI + fwrite( $newfilehnd, "\xFF\xD8" ); + + // Cycle through new headers, writing them to the new file + foreach ($jpeg_header_data as $segno => $segment) + { + + // Write segment marker + fwrite( $newfilehnd, sprintf( "\xFF%c", $segment['SegType'] ) ); + + // Write segment size + fwrite( $newfilehnd, pack( "n", strlen($segment['SegData']) + 2 ) ); + + // Write segment data + fwrite( $newfilehnd, $segment['SegData'] ); + } + + // Write the compressed image data + fwrite( $newfilehnd, $compressed_image_data ); + + // Write EOI + fwrite( $newfilehnd, "\xFF\xD9" ); + + // Close File + fclose($newfilehnd); + + // Alow the user to abort from now on + ignore_user_abort(false); + + + return TRUE; + +} + +/****************************************************************************** +* End of Function: put_jpeg_header_data +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: get_jpeg_Comment +* +* Description: Retreives the contents of the JPEG Comment (COM = 0xFFFE) segment if one +* exists +* +* Parameters: jpeg_header_data - the JPEG header data, as retrieved +* from the get_jpeg_header_data function +* +* Returns: string - Contents of the Comment segement +* FALSE - if the comment segment couldnt be found +* +******************************************************************************/ + +function get_jpeg_Comment( $jpeg_header_data ) +{ + //Cycle through the header segments until COM is found or we run out of segments + $i = 0; + while ( ( $i < count( $jpeg_header_data) ) && ( $jpeg_header_data[$i]['SegName'] != "COM" ) ) + { + $i++; + } + + // Check if a COM segment has been found + if ( $i < count( $jpeg_header_data) ) + { + // A COM segment was found, return it's contents + return $jpeg_header_data[$i]['SegData']; + } + else + { + // No COM segment found + return FALSE; + } +} + +/****************************************************************************** +* End of Function: get_jpeg_Comment +******************************************************************************/ + + +/****************************************************************************** +* +* Function: put_jpeg_Comment +* +* Description: Creates a new JPEG Comment segment from a string, and inserts +* this segment into the supplied JPEG header array +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data, into which the +* new Comment segment will be put +* $new_Comment - a string containing the new Comment +* +* Returns: jpeg_header_data - the JPEG header data array with the new +* JPEG Comment segment added +* +******************************************************************************/ + +function put_jpeg_Comment( $jpeg_header_data, $new_Comment ) +{ + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an COM header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "COM" ) == 0 ) + { + // Found a preexisting Comment block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = $new_Comment; + return $jpeg_header_data; + } + } + + + + // No preexisting Comment block found, find where to put it by searching for the highest app segment + $i = 0; + while ( ( $i < count( $jpeg_header_data ) ) && ( $jpeg_header_data[$i]["SegType"] >= 0xE0 ) ) + { + $i++; + } + + + // insert a Comment segment new at the position found of the header data. + array_splice($jpeg_header_data, $i , 0, array( array( "SegType" => 0xFE, + "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ 0xFE ], + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xFE ], + "SegData" => $new_Comment ) ) ); + return $jpeg_header_data; +} + +/****************************************************************************** +* End of Function: put_jpeg_Comment +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: Interpret_Comment_to_HTML +* +* Description: Generates html showing the contents of any JPEG Comment segment +* +* Parameters: jpeg_header_data - the JPEG header data, as retrieved +* from the get_jpeg_header_data function +* +* Returns: output - the HTML +* +******************************************************************************/ + +function Interpret_Comment_to_HTML( $jpeg_header_data ) +{ + // Create a string to receive the output + $output = ""; + + // read the comment segment + $comment = get_jpeg_Comment( $jpeg_header_data ); + + // Check if the comment segment was valid + if ( $comment !== FALSE ) + { + // Comment exists - add it to the output + $output .= "<h2 class=\"JPEG_Comment_Main_Heading\">JPEG Comment</h2>\n"; + $output .= "<p class=\"JPEG_Comment_Text\">$comment</p>\n"; + } + + // Return the result + return $output; +} + +/****************************************************************************** +* End of Function: Interpret_Comment_to_HTML +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_jpeg_intrinsic_values +* +* Description: Retreives information about the intrinsic characteristics of the +* jpeg image, such as Bits per Component, Height and Width. +* +* Parameters: jpeg_header_data - the JPEG header data, as retrieved +* from the get_jpeg_header_data function +* +* Returns: array - An array containing the intrinsic JPEG values +* FALSE - if the comment segment couldnt be found +* +******************************************************************************/ + +function get_jpeg_intrinsic_values( $jpeg_header_data ) +{ + // Create a blank array for the output + $Outputarray = array( ); + + //Cycle through the header segments until Start Of Frame (SOF) is found or we run out of segments + $i = 0; + while ( ( $i < count( $jpeg_header_data) ) && ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) != "SOF" ) ) + { + $i++; + } + + // Check if a SOF segment has been found + if ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) == "SOF" ) + { + // SOF segment was found, extract the information + + $data = $jpeg_header_data[$i]['SegData']; + + // First byte is Bits per component + $Outputarray['Bits per Component'] = ord( $data{0} ); + + // Second and third bytes are Image Height + $Outputarray['Image Height'] = ord( $data{ 1 } ) * 256 + ord( $data{ 2 } ); + + // Forth and fifth bytes are Image Width + $Outputarray['Image Width'] = ord( $data{ 3 } ) * 256 + ord( $data{ 4 } ); + + // Sixth byte is number of components + $numcomponents = ord( $data{ 5 } ); + + // Following this is a table containing information about the components + for( $i = 0; $i < $numcomponents; $i++ ) + { + $Outputarray['Components'][] = array ( 'Component Identifier' => ord( $data{ 6 + $i * 3 } ), + 'Horizontal Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0xF0 ) / 16, + 'Vertical Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0x0F ), + 'Quantization table destination selector' => ord( $data{ 8 + $i * 3 } ) ); + } + } + else + { + // Couldn't find Start Of Frame segment, hence can't retrieve info + return FALSE; + } + + return $Outputarray; +} + + +/****************************************************************************** +* End of Function: get_jpeg_intrinsic_values +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: Interpret_intrinsic_values_to_HTML +* +* Description: Generates html showing some of the intrinsic JPEG values which +* were retrieved with the get_jpeg_intrinsic_values function +* +* Parameters: values - the JPEG intrinsic values, as read from get_jpeg_intrinsic_values +* +* Returns: OutputStr - A string containing the HTML +* +******************************************************************************/ + +function Interpret_intrinsic_values_to_HTML( $values ) +{ + // Check values are valid + if ( $values != FALSE ) + { + // Write Heading + $OutputStr = "<h2 class=\"JPEG_Intrinsic_Main_Heading\">Intrinsic JPEG Information</h2>\n"; + + // Create Table + $OutputStr .= "<table class=\"JPEG_Intrinsic_Table\" border=1>\n"; + + // Put image height and width into table + $OutputStr .= "<tr class=\"JPEG_Intrinsic_Table_Row\"><td class=\"JPEG_Intrinsic_Caption_Cell\">Image Height</td><td class=\"JPEG_Intrinsic_Value_Cell\">" . $values['Image Height'] . " pixels</td></tr>\n"; + $OutputStr .= "<tr class=\"JPEG_Intrinsic_Table_Row\"><td class=\"JPEG_Intrinsic_Caption_Cell\">Image Width</td><td class=\"JPEG_Intrinsic_Value_Cell\">" . $values['Image Width'] . " pixels</td></tr>\n"; + + // Put colour depth into table + if ( count( $values['Components'] ) == 1 ) + { + $OutputStr .= "<tr class=\"JPEG_Intrinsic_Table_Row\"><td class=\"JPEG_Intrinsic_Caption_Cell\">Colour Depth</td><td class=\"JPEG_Intrinsic_Value_Cell\">" . $values['Bits per Component'] . " bit Monochrome</td></tr>\n"; + } + else + { + $OutputStr .= "<tr class=\"JPEG_Intrinsic_Table_Row\"><td class=\"JPEG_Intrinsic_Caption_Cell\">Colour Depth</td><td class=\"JPEG_Intrinsic_Value_Cell\">" . ($values['Bits per Component'] * count( $values['Components'] ) ) . " bit</td></tr>\n"; + } + + // Close Table + $OutputStr .= "</table>\n"; + + // Return html + return $OutputStr; + } +} + +/****************************************************************************** +* End of Function: Interpret_intrinsic_values_to_HTML +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: get_jpeg_image_data +* +* Description: Retrieves the compressed image data part of the JPEG file +* +* Parameters: filename - the filename of the JPEG file to read +* +* Returns: compressed_data - A string containing the compressed data +* FALSE - if retrieval failed +* +******************************************************************************/ + +function get_jpeg_image_data( $filename ) +{ + + // prevent refresh from aborting file operations and hosing file + ignore_user_abort(true); + + // Attempt to open the jpeg file + $filehnd = @fopen($filename, 'rb'); + + // Check if the file opened successfully + if ( ! $filehnd ) + { + // Could't open the file - exit + return FALSE; + } + + + // Read the first two characters + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the first two characters are 0xFF 0xDA (SOI - Start of image) + if ( $data != "\xFF\xD8" ) + { + // No SOI (FF D8) at start of file - close file and return; + fclose($filehnd); + return FALSE; + } + + + + // Read the third character + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the third character is 0xFF (Start of first segment header) + if ( $data{0} != "\xFF" ) + { + // NO FF found - close file and return + fclose($filehnd); + return; + } + + // Flag that we havent yet hit the compressed image data + $hit_compressed_image_data = FALSE; + + + // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit, + // 2) we have hit the compressed image data (no more headers are allowed after data) + // 3) or end of file is hit + + while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) )) + { + // Found a segment to look at. + // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them + if ( ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) ) + { + // Segment isn't a Restart marker + // Read the next two bytes (size) + $sizestr = network_safe_fread( $filehnd, 2 ); + + // convert the size bytes to an integer + $decodedsize = unpack ("nsize", $sizestr); + + // Read the segment data with length indicated by the previously read size + $segdata = network_safe_fread( $filehnd, $decodedsize['size'] - 2 ); + } + + // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows + if ( $data{1} == "\xDA" ) + { + // Flag that we have hit the compressed image data - exit loop after reading the data + $hit_compressed_image_data = TRUE; + + // read the rest of the file in + // Can't use the filesize function to work out + // how much to read, as it won't work for files being read by http or ftp + // So instead read 1Mb at a time till EOF + + $compressed_data = ""; + do + { + $compressed_data .= network_safe_fread( $filehnd, 1048576 ); + } while( ! feof( $filehnd ) ); + + // Strip off EOI and anything after + $EOI_pos = strpos( $compressed_data, "\xFF\xD9" ); + $compressed_data = substr( $compressed_data, 0, $EOI_pos ); + } + else + { + // Not an SOS - Read the next two bytes - should be the segment marker for the next segment + $data = network_safe_fread( $filehnd, 2 ); + + // Check that the first byte of the two is 0xFF as it should be for a marker + if ( $data{0} != "\xFF" ) + { + // Problem - NO FF foundclose file and return"; + fclose($filehnd); + return; + } + } + } + + // Close File + fclose($filehnd); + + // Alow the user to abort from now on + ignore_user_abort(false); + + + // Return the compressed data if it was found + if ( $hit_compressed_image_data ) + { + return $compressed_data; + } + else + { + return FALSE; + } +} + + +/****************************************************************************** +* End of Function: get_jpeg_image_data +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: Generate_JPEG_APP_Segment_HTML +* +* Description: Generates html showing information about the Application (APP) +* segments which are present in the JPEG file +* +* Parameters: jpeg_header_data - the JPEG header data, as retrieved +* from the get_jpeg_header_data function +* +* Returns: output - A string containing the HTML +* +******************************************************************************/ + +function Generate_JPEG_APP_Segment_HTML( $jpeg_header_data ) +{ + if ( $jpeg_header_data == FALSE ) + { + return ""; + } + + + // Write Heading + $output = "<h2 class=\"JPEG_APP_Segments_Main_Heading\">Application Metadata Segments</h2>\n"; + + // Create table + $output .= "<table class=\"JPEG_APP_Segments_Table\" border=1>\n"; + + + // Cycle through each segment in the array + + foreach( $jpeg_header_data as $jpeg_header ) + { + + // Check if the segment is a APP segment + + if ( ( $jpeg_header['SegType'] >= 0xE0 ) && ( $jpeg_header['SegType'] <= 0xEF ) ) + { + // This is an APP segment + + // Read APP Segment Name - a Null terminated string at the start of the segment + $seg_name = strtok($jpeg_header['SegData'], "\x00"); + + // Some Segment names are either too long or not meaningfull, so + // we should clean them up + + if ( $seg_name == "http://ns.adobe.com/xap/1.0/" ) + { + $seg_name = "XAP/RDF (\"http://ns.adobe.com/xap/1.0/\")"; + } + elseif ( $seg_name == "Photoshop 3.0" ) + { + $seg_name = "Photoshop IRB (\"Photoshop 3.0\")"; + } + elseif ( ( strncmp ( $seg_name, "[picture info]", 14) == 0 ) || + ( strncmp ( $seg_name, "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) ) + { + $seg_name = "[picture info]"; + } + elseif ( strncmp ( $seg_name, "Type=", 5) == 0 ) + { + $seg_name = "Epson Info"; + } + elseif ( ( strncmp ( $seg_name, "HHHHHHHHHHHHHHH", 15) == 0 ) || + ( strncmp ( $seg_name, "@s33", 5) == 0 ) ) + { + $seg_name = "HP segment full of \"HHHHH\""; + } + + + // Clean the segment name so it doesn't cause problems with HTML + $seg_name = htmlentities( $seg_name ); + + // Output a Table row containing this APP segment + $output .= "<tr class=\"JPEG_APP_Segments_Table_Row\"><td class=\"JPEG_APP_Segments_Caption_Cell\">$seg_name</td><td class=\"JPEG_APP_Segments_Type_Cell\">" . $jpeg_header['SegName'] . "</td><td class=\"JPEG_APP_Segments_Size_Cell\" align=\"right\">" . strlen( $jpeg_header['SegData']). " bytes</td></tr>\n"; + } + } + + // Close the table + $output .= "</table>\n"; + + // Return the HTML + return $output; +} + + +/****************************************************************************** +* End of Function: Generate_JPEG_APP_Segment_HTML +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: network_safe_fread +* +* Description: Retrieves data from a file. This function is required since +* the fread function will not always return the requested number +* of characters when reading from a network stream or pipe +* +* Parameters: file_handle - the handle of a file to read from +* length - the number of bytes requested +* +* Returns: data - the data read from the file. may be less than the number +* requested if EOF was hit +* +******************************************************************************/ + +function network_safe_fread( $file_handle, $length ) +{ + // Create blank string to receive data + $data = ""; + + // Keep reading data from the file until either EOF occurs or we have + // retrieved the requested number of bytes + + while ( ( !feof( $file_handle ) ) && ( strlen($data) < $length ) ) + { + $data .= fread( $file_handle, $length-strlen($data) ); + } + + // return the data read + return $data; +} + +/****************************************************************************** +* End of Function: network_safe_fread +******************************************************************************/ + + + + +/****************************************************************************** +* Global Variable: JPEG_Segment_Names +* +* Contents: The names of the JPEG segment markers, indexed by their marker number +* +******************************************************************************/ + +$GLOBALS[ "JPEG_Segment_Names" ] = array( + +0xC0 => "SOF0", 0xC1 => "SOF1", 0xC2 => "SOF2", 0xC3 => "SOF4", +0xC5 => "SOF5", 0xC6 => "SOF6", 0xC7 => "SOF7", 0xC8 => "JPG", +0xC9 => "SOF9", 0xCA => "SOF10", 0xCB => "SOF11", 0xCD => "SOF13", +0xCE => "SOF14", 0xCF => "SOF15", +0xC4 => "DHT", 0xCC => "DAC", + +0xD0 => "RST0", 0xD1 => "RST1", 0xD2 => "RST2", 0xD3 => "RST3", +0xD4 => "RST4", 0xD5 => "RST5", 0xD6 => "RST6", 0xD7 => "RST7", + +0xD8 => "SOI", 0xD9 => "EOI", 0xDA => "SOS", 0xDB => "DQT", +0xDC => "DNL", 0xDD => "DRI", 0xDE => "DHP", 0xDF => "EXP", + +0xE0 => "APP0", 0xE1 => "APP1", 0xE2 => "APP2", 0xE3 => "APP3", +0xE4 => "APP4", 0xE5 => "APP5", 0xE6 => "APP6", 0xE7 => "APP7", +0xE8 => "APP8", 0xE9 => "APP9", 0xEA => "APP10", 0xEB => "APP11", +0xEC => "APP12", 0xED => "APP13", 0xEE => "APP14", 0xEF => "APP15", + + +0xF0 => "JPG0", 0xF1 => "JPG1", 0xF2 => "JPG2", 0xF3 => "JPG3", +0xF4 => "JPG4", 0xF5 => "JPG5", 0xF6 => "JPG6", 0xF7 => "JPG7", +0xF8 => "JPG8", 0xF9 => "JPG9", 0xFA => "JPG10", 0xFB => "JPG11", +0xFC => "JPG12", 0xFD => "JPG13", + +0xFE => "COM", 0x01 => "TEM", 0x02 => "RES", + +); + +/****************************************************************************** +* End of Global Variable: JPEG_Segment_Names +******************************************************************************/ + + +/****************************************************************************** +* Global Variable: JPEG_Segment_Descriptions +* +* Contents: The descriptions of the JPEG segment markers, indexed by their marker number +* +******************************************************************************/ + +$GLOBALS[ "JPEG_Segment_Descriptions" ] = array( + +/* JIF Marker byte pairs in JPEG Interchange Format sequence */ +0xC0 => "Start Of Frame (SOF) Huffman - Baseline DCT", +0xC1 => "Start Of Frame (SOF) Huffman - Extended sequential DCT", +0xC2 => "Start Of Frame Huffman - Progressive DCT (SOF2)", +0xC3 => "Start Of Frame Huffman - Spatial (sequential) lossless (SOF3)", +0xC5 => "Start Of Frame Huffman - Differential sequential DCT (SOF5)", +0xC6 => "Start Of Frame Huffman - Differential progressive DCT (SOF6)", +0xC7 => "Start Of Frame Huffman - Differential spatial (SOF7)", +0xC8 => "Start Of Frame Arithmetic - Reserved for JPEG extensions (JPG)", +0xC9 => "Start Of Frame Arithmetic - Extended sequential DCT (SOF9)", +0xCA => "Start Of Frame Arithmetic - Progressive DCT (SOF10)", +0xCB => "Start Of Frame Arithmetic - Spatial (sequential) lossless (SOF11)", +0xCD => "Start Of Frame Arithmetic - Differential sequential DCT (SOF13)", +0xCE => "Start Of Frame Arithmetic - Differential progressive DCT (SOF14)", +0xCF => "Start Of Frame Arithmetic - Differential spatial (SOF15)", +0xC4 => "Define Huffman Table(s) (DHT)", +0xCC => "Define Arithmetic coding conditioning(s) (DAC)", + +0xD0 => "Restart with modulo 8 count 0 (RST0)", +0xD1 => "Restart with modulo 8 count 1 (RST1)", +0xD2 => "Restart with modulo 8 count 2 (RST2)", +0xD3 => "Restart with modulo 8 count 3 (RST3)", +0xD4 => "Restart with modulo 8 count 4 (RST4)", +0xD5 => "Restart with modulo 8 count 5 (RST5)", +0xD6 => "Restart with modulo 8 count 6 (RST6)", +0xD7 => "Restart with modulo 8 count 7 (RST7)", + +0xD8 => "Start of Image (SOI)", +0xD9 => "End of Image (EOI)", +0xDA => "Start of Scan (SOS)", +0xDB => "Define quantization Table(s) (DQT)", +0xDC => "Define Number of Lines (DNL)", +0xDD => "Define Restart Interval (DRI)", +0xDE => "Define Hierarchical progression (DHP)", +0xDF => "Expand Reference Component(s) (EXP)", + +0xE0 => "Application Field 0 (APP0) - usually JFIF or JFXX", +0xE1 => "Application Field 1 (APP1) - usually EXIF or XMP/RDF", +0xE2 => "Application Field 2 (APP2) - usually Flashpix", +0xE3 => "Application Field 3 (APP3)", +0xE4 => "Application Field 4 (APP4)", +0xE5 => "Application Field 5 (APP5)", +0xE6 => "Application Field 6 (APP6)", +0xE7 => "Application Field 7 (APP7)", + +0xE8 => "Application Field 8 (APP8)", +0xE9 => "Application Field 9 (APP9)", +0xEA => "Application Field 10 (APP10)", +0xEB => "Application Field 11 (APP11)", +0xEC => "Application Field 12 (APP12) - usually [picture info]", +0xED => "Application Field 13 (APP13) - usually photoshop IRB / IPTC", +0xEE => "Application Field 14 (APP14)", +0xEF => "Application Field 15 (APP15)", + + +0xF0 => "Reserved for JPEG extensions (JPG0)", +0xF1 => "Reserved for JPEG extensions (JPG1)", +0xF2 => "Reserved for JPEG extensions (JPG2)", +0xF3 => "Reserved for JPEG extensions (JPG3)", +0xF4 => "Reserved for JPEG extensions (JPG4)", +0xF5 => "Reserved for JPEG extensions (JPG5)", +0xF6 => "Reserved for JPEG extensions (JPG6)", +0xF7 => "Reserved for JPEG extensions (JPG7)", +0xF8 => "Reserved for JPEG extensions (JPG8)", +0xF9 => "Reserved for JPEG extensions (JPG9)", +0xFA => "Reserved for JPEG extensions (JPG10)", +0xFB => "Reserved for JPEG extensions (JPG11)", +0xFC => "Reserved for JPEG extensions (JPG12)", +0xFD => "Reserved for JPEG extensions (JPG13)", + +0xFE => "Comment (COM)", +0x01 => "For temp private use arith code (TEM)", +0x02 => "Reserved (RES)", + +); + +/****************************************************************************** +* End of Global Variable: JPEG_Segment_Descriptions +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/Pentax.php b/includes/jpeg_metadata_tk/Makernotes/Pentax.php new file mode 100644 index 0000000..10194a6 --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/Pentax.php @@ -0,0 +1,353 @@ +<?php + +/****************************************************************************** +* +* Filename: pentax.php +* +* Description: Pentax (Asahi) Makernote Parser +* Provides functions to decode an Pentax (Asahi) EXIF makernote and to interpret +* the resulting array into html. +* +* Pentax Makernote Format: +* +* Type 1 +* +* Field Size Description +* ---------------------------------------------------------------- +* IFD Data Variable NON-Standard IFD Data using Pentax Tags +* IFD has no Next-IFD pointer at end of IFD, +* and Offsets are relative to the start +* of the current IFD tag, not the TIFF header +* ---------------------------------------------------------------- +* +* +* Type 2 +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 4 Bytes "AOC\x00" +* Unknown 2 Bytes Unknown field +* IFD Data Variable NON-Standard IFD Data using Casio Type 2 Tags +* IFD has no Next-IFD pointer at end of IFD, +* and Offsets are relative to the start +* of the current IFD tag, not the TIFF header +* ---------------------------------------------------------------- +* +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + +// Pentax Type 2 makernote uses Casio Type 2 tags - ensure they are included + +include_once 'casio.php'; + + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Pentax_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Pentax_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Pentax_Makernote_Html"; + + + + + + + +/****************************************************************************** +* +* Function: get_Pentax_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Pentax_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + // Check if the Make Field contains the word Pentax or Asahi + if ( ( stristr( $Make_Field, "Pentax" ) === FALSE ) && + ( stristr( $Make_Field, "Asahi" ) === FALSE ) ) + { + // Couldn't find Pentax or Asahi in the maker - abort + return FALSE; + } + + // Check if the header exists at the start of the Makernote + if ( substr( $Makernote_Tag['Data'], 0, 4 ) == "AOC\x00" ) + { + // Type 2 Pentax Makernote + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 6 ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Casio Type 2" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Casio Type 2"; + $Makernote_Tag['Makernote Tags'] = "Casio Type 2"; + + // Return the new tag + return $Makernote_Tag; + } + else + { + // Type 1 Penax Makernote + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 0 ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Pentax" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Pentax"; + $Makernote_Tag['Makernote Tags'] = "Pentax"; + + // Return the new tag + return $Makernote_Tag; + } + + + // Shouldn't get here + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Pentax_Makernote +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: get_Pentax_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Pentax_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Pentax_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + // Check that this tag uses the Pentax tags, otherwise it can't be interpreted here + if ( $Tag_Definitions_Name == "Pentax" ) + { + // No Special Tags so far + return FALSE; + } + + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Pentax_Text_Value +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: get_Pentax_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Pentax_Makernote_Html( $Makernote_tag, $filename ) +{ + // Check that this is a Pentax type makernote + if ( $Makernote_tag['Makernote Type'] != "Pentax" ) + { + // Not a Pentax makernote - abort + return False; + } + + // Interpret the IFD and return the html + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + +} + +/****************************************************************************** +* End of Function: get_Pentax_Makernote_Html +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Pentax +* +* Contents: This global variable provides definitions of the known Pentax Type 1 +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Pentax"] = array( + + +0x0001 => array( 'Name' => "Capture Mode", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Night-scene", + 2 => "Manual", + 4 => "Multiple" ), + +0x0002 => array( 'Name' => "Quality Level", + 'Type' => "Lookup", + 0 => "Good", + 1 => "Better", + 2 => "Best" ), + +0x0003 => array( 'Name' => "Focus Mode", + 'Type' => "Lookup", + 2 => "Custom", + 3 => "Auto" ), + +0x0004 => array( 'Name' => "Flash Mode", + 'Type' => "Lookup", + 1 => "Auto", + 2 => "Flash on", + 4 => "Flash off", + 6 => "Red-eye Reduction" ), + +0x0007 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Daylight", + 2 => "Shade", + 3 => "Tungsten", + 4 => "Fluorescent", + 5 => "Manual" ), + + +0x000a => array( 'Name' => "Digital Zoom", + 'Type' => "Numeric", + 'Units' => " (0 = Off)" ), + +0x000b => array( 'Name' => "Sharpness", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Soft", + 2 => "Hard" ), + +0x000c => array( 'Name' => "Contrast", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Low", + 2 => "High" ), + +0x000d => array( 'Name' => "Saturation", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Low", + 2 => "High" ), + +0x0014 => array( 'Name' => "ISO Speed", + 'Type' => "Lookup", + 10 => "100", + 16 => "200", + 100 => "100", + 200 => "200" ), + +0x0017 => array( 'Name' => "Colour", + 'Type' => "Lookup", + 1 => "Normal", + 2 => "Black & White", + 3 => "Sepia" ), + +0x0e00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +0x1000 => array( 'Name' => "Time Zone", + 'Type' => "String" ), + +0x1001 => array( 'Name' => "Daylight Savings", + 'Type' => "String" ), + + + + + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Pentax +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/agfa.php b/includes/jpeg_metadata_tk/Makernotes/agfa.php new file mode 100644 index 0000000..214c70d --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/agfa.php @@ -0,0 +1,117 @@ +<?php + +/****************************************************************************** +* +* Filename: agfa.php +* +* Description: Agfa Makernote Parser +* Provides functions to decode an Agfa EXIF makernote and to interpret +* the resulting array into html. +* +* Agfa Makernote Format: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 8 Bytes "AGFA \x00\x01" +* IFD Data Variable Standard IFD Data using Olympus Tags +* ---------------------------------------------------------------- +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + +// Agfa makernote uses Olympus tags - ensure they are included + +include_once 'olympus.php'; + + + +// Add the Parser function to the list of Makernote Parsers. (Interpreter Functions are supplied by the Olympus script) + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Agfa_Makernote"; + + + + + +/****************************************************************************** +* +* Function: get_Agfa_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Agfa_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + // Check if the Make Field contains the word Agfa + if ( stristr( $Make_Field, "Agfa" ) === FALSE ) + { + // The Make Field doesnt contain the word Agfa + return FALSE; + } + + // Check if the header exists at the start of the Makernote + if ( substr( $Makernote_Tag['Data'], 0, 7 ) != "AGFA \x00\x01" ) + { + // This isn't a Agfa Makernote, abort + return FALSE ; + } + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Olympus" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Agfa"; + $Makernote_Tag['Makernote Tags'] = "Olympus"; + + // Return the new tag + return $Makernote_Tag; + +} + +/****************************************************************************** +* End of Function: get_Agfa_Makernote +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/canon.php b/includes/jpeg_metadata_tk/Makernotes/canon.php new file mode 100644 index 0000000..a261506 --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/canon.php @@ -0,0 +1,748 @@ +<?php + +/****************************************************************************** +* +* Filename: canon.php +* +* Description: Canon Makernote Parser +* Provides functions to decode an Canon EXIF makernote and to interpret +* the resulting array into html. +* +* Canon Makernote Format: +* +* Field Size Description +* ---------------------------------------------------------------- +* IFD Data Variable Standard IFD Data using Canon Tags +* ---------------------------------------------------------------- +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Canon_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Canon_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Canon_Makernote_Html"; + + + + + + + +/****************************************************************************** +* +* Function: get_Canon_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Canon_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + // Check if the Make Field contains the word Canon + if ( stristr( $Make_Field, "Canon" ) === FALSE ) + { + // Canon not found in Make Field - can't process this + return FALSE; + } + + // Seek to the start of the IFD + + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Canon" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Canon"; + $Makernote_Tag['Makernote Tags'] = "Canon"; + + + // Return the new tag + return $Makernote_Tag; +} + +/****************************************************************************** +* End of Function: get_Canon_Makernote +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_Canon_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Canon_Makernote_Html( $Makernote_tag, $filename ) +{ + // Check that this makernote uses canon tags + if ( $Makernote_tag['Makernote Type'] != "Canon" ) + { + // Makernote doesn't use Canon tags - cant Interpret it + return FALSE; + } + + // Interpret the IFD to html + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + +} + +/****************************************************************************** +* End of Function: get_Canon_Makernote_Html +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: get_Canon_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Canon_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Canon_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + + // Check that the tag uses Canon Definitions + if ( $Tag_Definitions_Name != "Canon" ) + { + // Tag doesn't use Canon definintions - can't process it + return FALSE; + } + + + $Tag_ID = $Exif_Tag['Tag Number']; + + + // Process the special tag according to the tag number + switch ( $Tag_ID ) + { + + // CAMERA SETTINGS 1 + case 1: + // Create an output string + $output_str = ""; + + // Cycle through each of the camera settings Values + foreach( $Exif_Tag['Data'] as $offset => $value ) + { + // Check that the value exists + if ( $value !== NULL ) + { + // Process the settings according to their offset + if ( $offset == 0 ) + { + // Do Not Show this Field ( Number of Bytes in Tag ) + } + else if ( $offset == 2 ) + { + if ( $value == 0 ) + { + $output_str .= "Self timer not used\n"; + } + else + { + $output_str .= "Self timer length : ". ($value/10) . " seconds\n"; + } + } + else if ( ( $offset == 23 ) && ( $Exif_Tag['Data'][25] != 0 )) + { + $output_str .= "Maximum Focal Length of Lens: " . ($value / $Exif_Tag['Data'][25]) . "mm\n"; + } + else if ( ( $offset == 24 ) && ( $Exif_Tag['Data'][25] != 0 )) + { + $output_str .= "Minimum Focal Length of Lens: " . ($value / $Exif_Tag['Data'][25]) . "mm\n"; + } + else if ( $offset == 25 ) + { + // Do Not Show this Field ( Focal Length units per mm ) + } + else if ( $offset == 29 ) + { + if ( $value & 0x4000 == 0x4000 ) + { + $output_str .= "External E-TTL Flash\n"; + } + if ( $value & 0x2000 == 0x2000 ) + { + $output_str .= "Internal Flash\n"; + } + if ( $value & 0x0800 == 0x0800 ) + { + $output_str .= "Flash FP sync used\n"; + } + if ( $value & 0x0080 == 0x0080 ) + { + $output_str .= "Second (Rear) curtain flash sync used\n"; + } + if ( $value & 0x0008 == 0x0008 ) + { + $output_str .= "Flash FP sync enabled\n"; + } + + } + else if ( array_key_exists( $offset, $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ] ) ) + { + if ( array_key_exists( $value, $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset] ) ) + { + $output_str .= $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset]['Name'] . ": " . $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset][$value] . "\n"; + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + $output_str .= $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset]['Name'] . ": Unknown Value ($value)\n"; + } + } + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + // Unknown Canon camera setting + $output_str .= " Unknown Setting ($offset), value: $value\n"; + } + } + } + + } + // Return the text string + return $output_str; + break; + + + // CAMERA SETTINGS 2 + case 4: + // Create an output string + $output_str = ""; + + // Cycle through each of the camera settings Values + foreach( $Exif_Tag['Data'] as $offset => $value ) + { + // Check that the value exists + if ( $value !== NULL ) + { + // Process the settings according to their offset + if ( $offset == 0 ) + { + // Do Not Show this Field ( Number of Bytes in Tag ) + } + else if ( $offset == 9 ) + { + $output_str .= "Sequence Number in a continuous burst : $value\n"; + } + else if ( $offset == 14 ) + { + $output_str .= "Number of Focus Points Available: ". ( ( $value & 0xF000 ) / 0x1000 ) . "\n"; + + if ( $value & 0x0004 == 0x0004 ) + { + $output_str .= "Left Focus Point Used\n"; + } + if ( $value & 0x0002 == 0x0002 ) + { + $output_str .= "Centre Focus Point Used\n"; + } + if ( $value & 0x0001 == 0x0001 ) + { + $output_str .= "Right Focus Point Used\n"; + } + } + else if ( $offset == 19 ) + { + $output_str .= "Subject distance: $value (units either mm or cm)\n"; + } + else if ( array_key_exists( $offset, $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ] ) ) + { + if ( array_key_exists( $value, $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset] ) ) + { + $output_str .= $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset]['Name'] . ": " . $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset][$value] . "\n"; + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + $output_str .= $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset]['Name'] . ": Unknown Value ($value)\n"; + } + } + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + $output_str .= " Unknown Setting ($offset), value: $value\n"; + } + } + } + + } + // Return the text string + return $output_str; + break; + + + // Serial Number + case 12: + $output_str = sprintf ( "%04X%05d", (($Exif_Tag['Data'][0] & 0xFF00)/256), ($Exif_Tag['Data'][0] & 0x00FF) ); + break; + + + // Custom Functions + case 15: + // Create an output string + $output_str = ""; + + // The size element is the first of the value array + // get rid of it + $tmparray = $Exif_Tag['Data']; + array_shift ( $tmparray ); + + // Cycle through each of the custom functions + foreach( $tmparray as $valorder => $value ) + { + // Figure out the function number and value + $funcno = ( $value & 0xFF00 ) / 256; + $funcval = $value & 0x00FF; + + // Check if the function exists in the lookup table of custom functions + if ( array_key_exists( $funcno, $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ] ) ) + { + // Function Exists in lookup table, + // Check if value exists for this function in the lookup table + if ( array_key_exists( $funcval, $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno] ) ) + { + // Value exists - Add it to the output text + $output_str .= $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno]['Name'] . ": " . $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno][$funcval] . "\n"; + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + // Value doesn't exist - Add a message to the output text + $output_str .= $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno]['Name'] . ": Unknown Value ($value)\n"; + } + } + } + else + { + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + // Function doesn't exist in lookup table - add a message to the output text + $output_str .= "Unknown Custom Function ($funcno), value: $funcval\n"; + } + } + } + // Return the resulting string + return $output_str; + break; + + default : + return FALSE; + } + + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Canon_Text_Value +******************************************************************************/ + + + + + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Canon +* +* Contents: This global variable provides definitions of the known Canon +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]['Canon'] = array( + +1 => array( 'Name' => "Camera Settings 1", + 'Type' => "Special" ), + +4 => array( 'Name' => "Camera Settings 2", + 'Type' => "Special" ), + +6 => array( 'Name' => "Image Type", + 'Type' => "String" ), + +7 => array( 'Name' => "Firmware Version", + 'Type' => "String" ), + +8 => array( 'Name' => "Image Number", + 'Type' => "Numeric" ), + +9 => array( 'Name' => "Owner Name", + 'Type' => "String" ), + +12 => array( 'Name' => "Camera Serial Number", + 'Type' => "Special" ), + +15 => array( 'Name' => "Custom Functions", + 'Type' => "Special" ) + +); + + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Canon +******************************************************************************/ + + + + + + + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: Canon_Camera_Settings_1_Tag_Values +* +* Contents: This global variable provides definitions for the Canon Camera +* Settings 1 Makernote tag, indexed by their offset. +* +******************************************************************************/ + +$GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ] = array( + +1 => array( 'Name' => "Macro Mode", + 1 => "Macro", + 2 => "Normal ( Not Macro )" ), + +3 => array( 'Name' => "Quality", + 2 => "Normal", + 3 => "Fine", + 5 => "Superfine" ), + +4 => array( 'Name' => "Flash Mode", + 0 => "Flash Not Fired", + 1 => "Auto", + 2 => "On", + 3 => "Red Eye Reduction", + 4 => "Slow Synchro", + 5 => "Auto + Red Eye Reduction", + 6 => "On + Red Eye Reduction", + 16 => "External Flash" ), + +5 => array( 'Name' => "Continuous drive mode", + 0 => "Single Frame or Timer Mode", + 1 => "Continuous" ), + +7 => array( 'Name' => "Focus Mode", + 0 => "One-Shot", + 1 => "AI Servo", + 2 => "AI Focus", + 3 => "Manual Focus", + 4 => "Single", + 5 => "Continuous", + 6 => "Manual Focus" ), + +10 => array( 'Name' => "Image Size", + 0 => "Large", + 1 => "Medium", + 2 => "Small" ), + +11 => array( 'Name' => "Easy shooting Mode", + 0 => "Full Auto", + 1 => "Manual", + 2 => "Landscape", + 3 => "Fast Shutter", + 4 => "Slow Shutter", + 5 => "Night", + 6 => "Black & White", + 7 => "Sepia", + 8 => "Portrait", + 9 => "Sports", + 10 => "Macro / Close-Up", + 11 => "Pan Focus" ), + + +12 => array( 'Name' => "Digital Zoom", + 0 => "No Digital Zoom", + 1 => "2x", + 2 => "4x" ), + +13 => array( 'Name' => "Contrast", + 0 => "Normal", + 1 => "High", + 65535 => "Low" ), + +14 => array( 'Name' => "Saturation", + 0 => "Normal", + 1 => "High", + 65535 => "Low" ), + +15 => array( 'Name' => "Sharpness", + 0 => "Normal", + 1 => "High", + 65535 => "Low" ), + +16 => array( 'Name' => "ISO Speed", + 0 => "Check ISOSpeedRatings EXIF tag for ISO Speed", + 15 => "Auto ISO", + 16 => "ISO 50", + 17 => "ISO 100", + 18 => "ISO 200", + 19 => "ISO 400" ), + +17 => array( 'Name' => "Metering Mode", + 3 => "Evaluative", + 4 => "Partial", + 5 => "Centre Weighted" ), + +18 => array( 'Name' => "Focus Type", + 0 => "Manual", + 1 => "Auto", + 3 => "Close-up (Macro)", + 8 => "Locked (Pan Mode)" ), + +19 => array( 'Name' => "Auto Focus Point Selected", + 12288 => "None (Manual Focus)", + 12289 => "Auto Selected", + 12290 => "Right", + 12291 => "Centre", + 12292 => "Left" ), + +20 => array( 'Name' => "Exposure Mode", + 0 => "Easy Shooting (See Easy Shooting Mode)", + 1 => "Program", + 2 => "Tv-Priority", + 3 => "Av-Priority", + 4 => "Manual", + 5 => "A-DEP" ), + +28 => array( 'Name' => "Flash Activity", + 0 => "Flash Did Not Fire", + 1 => "Flash Fired" ), + +32 => array( 'Name' => "Focus Mode", + 0 => "Focus Mode: Single", + 1 => "Focus Mode: Continuous" ) + +); + +/****************************************************************************** +* End of Global Variable: Canon_Camera_Settings_1_Tag_Values +******************************************************************************/ + + + + +/****************************************************************************** +* Global Variable: Canon_Camera_Settings_2_Tag_Values +* +* Contents: This global variable provides definitions for the Canon Camera +* Settings 2 Makernote tag, indexed by their offset. +* +******************************************************************************/ + +$GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ] = array( + +7 => array ( 'Name' => "White Balance", + 0 => "Auto", + 1 => "Sunny", + 2 => "Cloudy", + 3 => "Tungsten", + 4 => "Flourescent", + 5 => "Flash", + 6 => "Custom" ), + +15 => array( 'Name' => "Flash Bias", + 0xffc0 => "-2 EV", + 0xffcc => "-1.67 EV", + 0xffd0 => "-1.5 EV", + 0xffd4 => "-1.33 EV", + 0xffe0 => "-1 EV", + 0xffec => "-0.67 EV", + 0xfff0 => "-0.5 EV", + 0xfff4 => "-0.33 EV", + 0x0000 => "0 EV", + 0x000c => "0.33 EV", + 0x0010 => "0.5 EV", + 0x0014 => "0.67 EV", + 0x0020 => "1 EV", + 0x002c => "1.33 EV", + 0x0030 => "1.5 EV", + 0x0034 => "1.67 EV", + 0x0040 => "2 EV" ), +); + +/****************************************************************************** +* End of Global Variable: Canon_Camera_Settings_2_Tag_Values +******************************************************************************/ + + + + + + +/****************************************************************************** +* Global Variable: Canon_Custom_Functions_Tag_Values +* +* Contents: This global variable provides definitions for the Canon Custom +* Functions Makernote tag, indexed by their offset. +* +******************************************************************************/ + +$GLOBALS[ "Canon_Custom_Functions_Tag_Values" ] = array( + +1 => array ( 'Name' => "Long Exposure Noise Reduction", + 0 => "Off", + 1 => "On" ), + +2 => array ( 'Name' => "Shutter/Auto Exposure-lock buttons", + 0 => "AF/AE lock", + 1 => "AE lock/AF", + 2 => "AF/AF lock", + 3 => "AE+release/AE+AF" ), + +3 => array ( 'Name' => "Mirror lockup", + 0 => "Disable", + 1 => "Enable" ), + +4 => array ( 'Name' => "Tv/Av and exposure level", + 0 => "1/2 stop", + 1 => "1/3 stop" ), + +5 => array ( 'Name' => "AF-assist light", + 0 => "On (Auto)", + 1 => "Off" ), + +6 => array ( 'Name' => "Shutter speed in Av mode", + 0 => "Automatic", + 1 => "1/200 (fixed)" ), + +7 => array ( 'Name' => "Auto-Exposure Bracketting sequence/auto cancellation", + 0 => "0,-,+ / Enabled", + 1 => "0,-,+ / Disabled", + 2 => "-,0,+ / Enabled", + 3 => "-,0,+ / Disabled" ), + +8 => array ( 'Name' => "Shutter Curtain Sync", + 0 => "1st Curtain Sync", + 1 => "2nd Curtain Sync" ), + +9 => array ( 'Name' => "Lens Auto-Focus stop button Function Switch", + 0 => "AF stop", + 1 => "Operate AF", + 2 => "Lock AE and start timer" ), + +10 => array ( 'Name' => "Auto reduction of fill flash", + 0 => "Enable", + 1 => "Disable" ), + +11 => array ( 'Name' => "Menu button return position", + 0 => "Top", + 1 => "Previous (volatile)", + 2 => "Previous" ), + +12 => array ( 'Name' => "SET button function when shooting", + 0 => "Not Assigned", + 1 => "Change Quality", + 2 => "Change ISO Speed", + 3 => "Select Parameters" ), + +13 => array ( 'Name' => "Sensor cleaning", + 0 => "Disable", + 1 => "Enable" ) + + +); + +/****************************************************************************** +* End of Global Variable: Canon_Custom_Functions_Tag_Values +******************************************************************************/ + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/casio.php b/includes/jpeg_metadata_tk/Makernotes/casio.php new file mode 100644 index 0000000..d1cd74e --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/casio.php @@ -0,0 +1,575 @@ +<?php + +/****************************************************************************** +* +* Filename: casio.php +* +* Description: Casio Makernote Parser +* Provides functions to decode an Casio EXIF makernote and to interpret +* the resulting array into html. +* +* Casio Makernote Format: +* +* Type 1: +* +* Field Size Description +* ---------------------------------------------------------------- +* IFD Data Variable Standard IFD Data using Casio Type 1 Tags and Motorola Byte Alignment +* ---------------------------------------------------------------- +* +* Type 2: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 6 Bytes "QVC\x00\x00\x00" +* IFD Data Variable Standard IFD Data using Casio Type 2 Tags and Motorola Byte Alignment +* ---------------------------------------------------------------- +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.11 +* +* Changes: 1.00 -> 1.11 : changed get_Casio_Makernote_Html to allow thumbnail links to work when +* toolkit is portable across directories +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Casio_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Casio_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Casio_Makernote_Html"; + +include_once dirname(__FILE__) .'/../pjmt_utils.php'; // Change: as of version 1.11 - added to allow directory portability + + +/****************************************************************************** +* +* Function: get_Casio_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Casio_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + + // Check if the Make Field contains the word Casio + if ( stristr( $Make_Field, "Casio" ) === FALSE ) + { + return FALSE; + } + + + if ( substr( $Makernote_Tag['Data'],0 , 6 ) == "QVC\x00\x00\x00" ) + { + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 6 ); + + $Makernote_Tag['ByteAlign'] = "MM"; + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Casio Type 2" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Casio Type 2"; + $Makernote_Tag['Makernote Tags'] = "Casio Type 2"; + + // Return the new tag + return $Makernote_Tag; + } + else + { + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 0 ); + + $Makernote_Tag['ByteAlign'] = "MM"; + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Casio Type 1" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Casio Type 1"; + $Makernote_Tag['Makernote Tags'] = "Casio Type 1"; + + // Return the new tag + return $Makernote_Tag; + } +} + +/****************************************************************************** +* End of Function: get_Casio_Makernote +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_Casio_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Casio_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Casio_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + + // Check that this tag uses the Casio tag definitions, otherwise it can't be decoded here + if ( $Tag_Definitions_Name == "Casio Type 2" ) + { + // Tag Uses Casio Type 2 Tag definitions + // Process the tag according to it's tag number + if ( $Exif_Tag['Tag Number'] == 0x001D ) + { + return $Exif_Tag['Data'][0]/10 . $Exif_Tag['Units']; + } + else + { + return FALSE; + } + } + else if ( $Tag_Definitions_Name == "Casio Type 1" ) + { + // Tag Uses Casio Type 1 Tags + return FALSE; + } + else + { + // Tag does NOT use Casio Tag definitions + return FALSE; + } + + // Shouldn't get here + return FALSE; + +} + +/****************************************************************************** +* End of Function: get_Casio_Text_Value +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: get_Casio_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Casio_Makernote_Html( $Makernote_tag, $filename ) +{ + // Check that this tag uses the Casio tags, otherwise it can't be interpreted here + if ( ( $Makernote_tag['Makernote Type'] != "Casio Type 1" ) && + ( $Makernote_tag['Makernote Type'] != "Casio Type 2" ) ) + { + // Not Casio tags - can't interpret with this function + return FALSE; + } + + // Casio Thumbnail (Tag 4) + if ( ( array_key_exists( 4, $Makernote_tag['Decoded Data'][0] ) ) && + ( $Makernote_tag['Makernote Tags'] == "Casio Type 2" ) ) + { + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/get_casio_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ); + + // Add thumbnail link to html + $Makernote_tag['Decoded Data'][0][4]['Text Value'] = "<a class=\"EXIF_Casio_Thumb_Link\" href=\"$link_str\"><img class=\"EXIF_Casio_Thumb\" src=\"$link_str\"></a></td></tr>\n"; + $Makernote_tag['Decoded Data'][0][4]['Type'] = "String"; + } + + + // Casio Thumbnail (Tag 8192) + if ( ( array_key_exists( 8192, $Makernote_tag['Decoded Data'][0] ) ) && + ( $Makernote_tag['Makernote Tags'] == "Casio Type 2" ) ) + { + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/.." . "/get_casio_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) . "/.." ); + + // Add thumbnail link to html + $Makernote_tag['Decoded Data'][0][8192]['Text Value'] = "<a class=\"EXIF_Casio_Thumb_Link\" href=\"$link_str\"><img class=\"EXIF_Casio_Thumb\" src=\"$link_str\"></a></td></tr>\n"; + $Makernote_tag['Decoded Data'][0][8192]['Type'] = "String"; + } + + + // Check if there are two thumbnail offset tags + if ( ( array_key_exists( 4, $Makernote_tag['Decoded Data'][0] ) ) && + ( array_key_exists( 8192, $Makernote_tag['Decoded Data'][0] ) ) ) + { + // There are two copies of the thumbnail offset - Remove one + array_splice( $Makernote_tag['Decoded Data'][0], 4, 1); + } + + + // Interpret the IFD and return the html + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + +} + +/****************************************************************************** +* End of Function: get_Casio_Makernote_Html +******************************************************************************/ + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Casio Type 1 +* +* Contents: This global variable provides definitions of the known Casio Type 1 +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + + +$GLOBALS[ "IFD_Tag_Definitions" ]["Casio Type 1"] = array( + +1 => array( 'Name' => "Recording Mode", + 'Type' => "Lookup", + 1 => "Single Shutter", + 2 => "Panorama", + 3 => "Night Scene", + 4 => "Portrait", + 5 => "Landscape" ), + +2 => array( 'Name' => "Quality", + 'Type' => "Lookup", + 1 => "Economy", + 2 => "Normal", + 3 => "Fine" ), + +3 => array( 'Name' => "Focusing Mode", + 'Type' => "Lookup", + 2 => "Macro", + 3 => "Auto Focus", + 4 => "Manual Focus", + 5 => "Infinity" ), + +4 => array( 'Name' => "Flash Mode", + 'Type' => "Lookup", + 1 => "Auto", + 2 => "On", + 3 => "Off", + 4 => "Off" ), + +5 => array( 'Name' => "Flash Intensity", + 'Type' => "Lookup", + 11 => "Weak", + 13 => "Normal", + 15 => "Strong" ), + +6 => array( 'Name' => "Object Distance", + 'Type' => "Numeric", + 'Units' => "mm" ), + +7 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 1 => "Auto", + 2 => "Tungsten", + 3 => "Daylight", + 4 => "Flourescent", + 5 => "Shade", + 129 => "Manual" ), + +10 => array( 'Name' => "Digital Zoom", + 'Type' => "Lookup", + 0x10000 => "Off", + 0x10001 => "2x Digital Zoom", + 0x20000 => "2x Digital Zoom", + 0x40000 => "4x Digital Zoom" ), + +11 => array( 'Name' => "Sharpness", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Soft", + 2 => "Hard" ), + +12 => array( 'Name' => "Contrast", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Low", + 2 => "High" ), + +13 => array( 'Name' => "Saturation", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Low", + 2 => "High" ), + +20 => array( 'Name' => "CCD Sensitivity", + 'Type' => "Lookup", + 64 => "Normal", + 125 => "+1.0", + 250 => "+2.0", + 244 => "+3.0", + 80 => "Normal (ISO 80 equivalent)", + 100 => "High" ), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Casio Type 1 +******************************************************************************/ + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Casio Type 2 +* +* Contents: This global variable provides definitions of the known Casio Type 2 +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Casio Type 2"] = array( + +0x0002 => array( 'Name' => "Preview Thumbnail Dimensions", + 'Type' => "Numeric", + 'Units' => "(x,y pixels)" ), + +0x0003 => array( 'Name' => "Preview Thumbnail Size", + 'Type' => "Numeric", + 'Units' => "bytes" ), + +0x0004 => array( 'Name' => "Preview Thumbnail", // thumbnail offset + 'Type' => "Numeric" ), + + +0x0008 => array( 'Name' => "Quality Mode", + 'Type' => "Lookup", + 1 => "Fine", + 2 => "Super Fine" ), + +0x0009 => array( 'Name' => "Image Size", + 'Type' => "Lookup", + 20 => "2288 x 1712 pixels", + 36 => "3008 x 2008 pixels", + 5 => "2048 x 1536 pixels", + 4 => "1600 x 1200 pixels", + 21 => "2592 x 1944 pixels", + 0 => "640 x 480 pixels", + 22 => "2304 x 1728 pixels" ), + +0x000D => array( 'Name' => "Focus Mode", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Macro" ), + + +0x0014 => array( 'Name' => "Iso Sensitivity", + 'Type' => "Lookup", + 3 => "50", + 4 => "64", + 6 => "100", + 9 => "200" ), + + +0x0019 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Daylight", + 2 => "Shade", + 3 => "Tungsten", + 4 => "Fluorescent", + 5 => "Manual" ), + +0x001D => array( 'Name' => "Focal Length", + 'Type' => "Special", + 'Units' => "mm" ), + +0x001F => array( 'Name' => "Saturation", + 'Type' => "Lookup", + 0 => "-1", + 1 => "Normal", + 2 => "+1", ), + +0x0020 => array( 'Name' => "Contrast", + 'Type' => "Lookup", + 0 => "-1", + 1 => "Normal", + 2 => "+1", ), + +0x0021 => array( 'Name' => "Sharpness", + 'Type' => "Lookup", + 0 => "-1", + 1 => "Normal", + 2 => "+1", ), + + +0x0e00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + + +0x2000 => array( 'Name' => "Casio Preview Thumbnail", // thumbnail offset + 'Type' => "String" ), + + + +0x2011 => array( 'Name' => "White Balance Bias", + 'Type' => "Numeric" ), + + +0x2012 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 12 => "Flash", + 0 => "Manual", + 1 => "Auto?", + 4 => "Flash?", ), + +0x2022 => array( 'Name' => "Object Distance", + 'Type' => "Numeric", + 'Units' => "mm" ), + + +0x2034 => array( 'Name' => "Flash Distance", + 'Type' => "Numeric", + 'Units' => " (0=Off)" ), + +0x3000 => array( 'Name' => "Record Mode", + 'Type' => "Lookup", + 2 => "Normal Mode" ), + +0x3001 => array( 'Name' => "Self Timer?", + 'Type' => "Lookup", + 1 => "Off?" ), + + +0x3002 => array( 'Name' => "Quality", + 'Type' => "Lookup", + 3 => "Fine" ), + +0x3003 => array( 'Name' => "Focus Mode", + 'Type' => "Lookup", + 6 => "Multi-Area Auto Focus", + 1 => "Fixation" ), + + +0x3006 => array( 'Name' => "Time Zone", + 'Type' => "String" ), + + +0x3007 => array( 'Name' => "Bestshot Mode", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On?" ), + + +0x3014 => array( 'Name' => "CCD ISO Sensitivity", + 'Type' => "Numeric" ), + + + +0x3015 => array( 'Name' => "Colour Mode", + 'Type' => "Lookup", + 0 => "Off" ), + + +0x3016 => array( 'Name' => "Enhancement", + 'Type' => "Lookup", + 0 => "Off" ), + +0x3017 => array( 'Name' => "Filter", + 'Type' => "Lookup", + 0 => "Off" ), + + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Casio Type 2 +******************************************************************************/ + + + + + + + + + + + + + + + + + + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Makernotes/epson.php b/includes/jpeg_metadata_tk/Makernotes/epson.php new file mode 100644 index 0000000..16057bb --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/epson.php @@ -0,0 +1,119 @@ +<?php + +/****************************************************************************** +* +* Filename: epson.php +* +* Description: Epson Makernote Parser +* Provides functions to decode an Epson EXIF makernote and to interpret +* the resulting array into html. +* +* Epson Makernote Format: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 8 Bytes "EPSON\x00\x01\x00" +* IFD Data Variable Standard IFD Data using Olympus Tags +* ---------------------------------------------------------------- +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + +// Epson makernote uses Olympus tags - ensure they are included + +include_once 'olympus.php'; + + + + +// Add the Parser function to the list of Makernote Parsers. (Interpreter Functions are supplied by the Olympus script) + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Epson_Makernote"; + + + + + +/****************************************************************************** +* +* Function: get_Epson_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Epson_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + + // Check if the Make Field contains the word Epson + if ( stristr( $Make_Field, "Epson" ) === FALSE ) + { + return FALSE; + } + + // Check if the header exists at the start of the Makernote + if ( substr( $Makernote_Tag['Data'], 0, 8 ) != "EPSON\x00\x01\x00" ) + { + // This isn't a Epson Makernote, abort + return FALSE ; + } + + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Olympus" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Epson"; + $Makernote_Tag['Makernote Tags'] = "Olympus"; + + + // Return the new tag + return $Makernote_Tag; + +} + +/****************************************************************************** +* End of Function: get_Epson_Makernote +******************************************************************************/ + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/fujifilm.php b/includes/jpeg_metadata_tk/Makernotes/fujifilm.php new file mode 100644 index 0000000..5d135cd --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/fujifilm.php @@ -0,0 +1,344 @@ +<?php + +/****************************************************************************** +* +* Filename: fujifilm.php +* +* Description: Fujifilm Makernote Parser +* Provides functions to decode an Fujifilm EXIF makernote and to interpret +* the resulting array into html. +* This Makernote format is also used by one Nikon Camera +* +* Fujifilm Makernote Format: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 8 Bytes "FUJIFILM" +* IFD Offset 4 Bytes Intel Byte aligned offset to IFD from start of Makernote +* IFD Data Variable NON-Standard IFD Data using Fujifilm Tags +* Offsets are relative to start of makernote +* Byte alignment is always Intel +* ---------------------------------------------------------------- +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Fujifilm_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Fujifilm_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Fujifilm_Makernote_Html"; + + + + + + +/****************************************************************************** +* +* Function: get_Fujifilm_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Fujifilm_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + + // Check if the Make Field contains the word Fuji or Nikon (One Nikon camera uses this format Makernote) + if ( ( stristr( $Make_Field, "Fuji" ) === FALSE ) && + ( stristr( $Make_Field, "Nikon" ) === FALSE ) ) + { + // Couldn't find Fuji or Nikon in the maker name - abort + return FALSE; + } + + // Check if the header exists at the start of the Makernote + if ( substr( $Makernote_Tag['Data'], 0, 8 ) != "FUJIFILM" ) + { + // This isn't a Fuji Makernote, abort + return FALSE; + } + + // The 4 bytes after the header are the offset to the Fujifilm IFD + // Get the offset of the IFD + $ifd_offset = hexdec( bin2hex( strrev( substr( $Makernote_Tag['Data'], 8, 4 ) ) ) ); + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + $ifd_offset ); + + // Fuji Makernotes are always Intel Byte Aligned + $Makernote_Tag['ByteAlign'] = "II"; + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'], $Makernote_Tag['ByteAlign'], "Fujifilm" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Fujifilm"; + $Makernote_Tag['Makernote Tags'] = "Fujifilm"; + + + // Return the new tag + return $Makernote_Tag; + +} + +/****************************************************************************** +* End of Function: get_Fujifilm_Makernote +******************************************************************************/ + + + + + + + + + +/****************************************************************************** +* +* Function: get_Fujifilm_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Fujifilm_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Fujifilm_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + // Check that this tag uses the Fujifilm tag Definitions, otherwise it can't be decoded here + if ( $Tag_Definitions_Name == "Fujifilm" ) + { + // No special Tags at this time + return FALSE; + } + + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Fujifilm_Text_Value +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* +* Function: get_Fujifilm_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Fujifilm_Makernote_Html( $Makernote_tag, $filename ) +{ + // Check that this tag uses the Fujifilm tags, otherwise it can't be interpreted here + if ( $Makernote_tag['Makernote Type'] != "Fujifilm" ) + { + // Not Fujifilm tags - can't interpret with this function + return FALSE; + } + + // Interpret the IFD normally + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + +} + +/****************************************************************************** +* End of Function: get_Fujifilm_Makernote_Html +******************************************************************************/ + + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Fujifilm +* +* Contents: This global variable provides definitions of the known Fujifilm +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Fujifilm"] = array( + +0 => array( 'Name' => "Version", + 'Type' => "String" ), + +4096 => array( 'Name' => "Quality", + 'Type' => "String" ), + +4097 => array( 'Name' => "Sharpness", + 'Type' => "Lookup", + 1 => "Softest", + 2 => "Soft", + 3 => "Normal", + 4 => "Hard", + 5 => "Hardest" ), + +4098 => array( 'Name' => "White Balance", + 'Type' => "Lookup", + 0 => "Auto", + 256 => "Daylight", + 512 => "Cloudy", + 768 => "DaylightColour-fluorescence", + 769 => "DaywhiteColour-fluorescence", + 770 => "White-fluorescence", + 1024 => "Incandenscense", + 3840 => "Custom white balance" ), + +4099 => array( 'Name' => "Colour Saturation", + 'Type' => "Lookup", + 0 => "Normal", + 256 => "High", + 512 => "Low" ), + +4100 => array( 'Name' => "Tone (Contrast)", + 'Type' => "Lookup", + 0 => "Normal", + 256 => "High", + 512 => "Low" ), + +4112 => array( 'Name' => "Flash Mode", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "On", + 2 => "Off", + 3 => "Red-eye Reduction" ), + +4113 => array( 'Name' => "Flash Strength", + 'Type' => "Numeric", + 'Units' => "EV" ), + +4128 => array( 'Name' => "Macro", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On" ), + +4129 => array( 'Name' => "Focus Mode", + 'Type' => "Lookup", + 0 => "Auto Focus", + 1 => "Manual Focus" ), + +4144 => array( 'Name' => "Slow Sync", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On" ), + +4145 => array( 'Name' => "Picture Mode", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Portrait Scene", + 2 => "Landscape Scene", + 4 => "Sports Scene", + 5 => "Night Scene", + 6 => "Program AE", + 256 => "Aperture priority AE", + 512 => "Shutter priority AE", + 768 => "Manual Exposure" ), + +4352 => array( 'Name' => "Continuous taking or auto bracketing mode", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On" ), + +4864 => array( 'Name' => "Blur Warning", + 'Type' => "Lookup", + 0 => "No Blur Warning", + 1 => "Blur Warning" ), + +4865 => array( 'Name' => "Focus warning", + 'Type' => "Lookup", + 0 => "Auto Focus Good", + 1 => "Out of Focus" ), + +4866 => array( 'Name' => "Auto Exposure Warning", + 'Type' => "Lookup", + 0 => "Auto Exposure Good", + 1 => "Over exposure (>1/1000s,F11)" ) + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Fujifilm +******************************************************************************/ + + + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/konica_minolta.php b/includes/jpeg_metadata_tk/Makernotes/konica_minolta.php new file mode 100644 index 0000000..17bbfbd --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/konica_minolta.php @@ -0,0 +1,745 @@ +<?php + +/****************************************************************************** +* +* Filename: konica_minolta.php +* +* Description: Konica/Minolta Makernote Parser +* Provides functions to decode an Konica/Minolta EXIF makernote and +* to interpret the resulting array into html. +* +* Konica/Minolta Makernote Formats: +* +* Type 1: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 3 Bytes "MLY" +* Unknown Data Variable Unknown Data +* ---------------------------------------------------------------- +* +* Type 2: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 2 Bytes "KC" +* Unknown Data Variable Unknown Data +* ---------------------------------------------------------------- +* +* Type 3: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 8 Bytes "+M+M+M+M" +* Unknown Data Variable Unknown Data +* ---------------------------------------------------------------- +* +* Type 4: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 5 Bytes "MINOL" +* Unknown Data Variable Unknown Data +* ---------------------------------------------------------------- +* +* Type 5: NO HEADER +* +* Field Size Description +* ---------------------------------------------------------------- +* IFD Data Variable Standard IFD with Olympus Tags +* ---------------------------------------------------------------- +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + +// Konica/Minolta makernote uses Olympus tags - ensure they are included + +include_once 'olympus.php'; + + +// Add the parser functions to the list of Makernote parsers . (Interpreting done by Olympus script) + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Minolta_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Minolta_Text_Value"; + + + +/****************************************************************************** +* +* Function: get_Minolta_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Minolta_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + + if ( ( stristr( $Make_Field, "Konica" ) === FALSE ) && + ( stristr( $Make_Field, "Minolta" ) === FALSE ) ) + { + // Not a Konica/Minolta Makernote - Cannot decode it + return False; + } + + // There are several different headers for a Konica/Minolta Makernote + // Unfortunately only one type can be decoded (the one without a header) + // Check which header exists (if any) + if ( substr( $Makernote_Tag['Data'], 0, 3 ) == "MLY" ) + { + // MLY Header - Can't Decode this + return $Makernote_Tag; + } + else if ( substr( $Makernote_Tag['Data'], 0, 2 ) == "KC" ) + { + // KC Header - Can't Decode this + return $Makernote_Tag; + } + if ( substr( $Makernote_Tag['Data'], 0, 8 ) == "+M+M+M+M" ) + { + // +M+M+M+M Header - Can't Decode this + return $Makernote_Tag; + } + else if ( substr( $Makernote_Tag['Data'], 0, 5 ) == "MINOL" ) + { + // MINOL Header - Can't Decode this + return $Makernote_Tag; + } + else + { + // No Header - Decode the IFD + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Olympus" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Minolta"; + $Makernote_Tag['Makernote Tags'] = "Olympus"; + + + // Return the new tag + return $Makernote_Tag; + + } + + + // Shouldn't get here + return False; +} + +/****************************************************************************** +* End of Function: get_Minolta_Makernote +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: get_Minolta_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Olympus_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Minolta_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + // Check that this Tag uses Olympus type tags - otherwise it cannot be processed here + if ( $Tag_Definitions_Name !== "Olympus" ) + { + // Not Olympus Tags - cannot be processed here + return FALSE; + } + + + // Process the tag acording to it's tag number, to produce a text value + + if ( ( $Exif_Tag['Tag Number'] == 0x0001 ) || // Minolta Camera Settings + ( $Exif_Tag['Tag Number'] == 0x0003 ) ) + { + + // Create the output string + $output_str = ""; + + // Cycle through each camera setting record which are 4 byte Longs + + for ( $i = 1; $i*4 <= strlen( $Exif_Tag['Data'] ); $i++) + { + + // Exract the current 4 byte Long value (Motorola byte alignment) + $value = get_IFD_Data_Type( substr($Exif_Tag['Data'], ($i-1)*4, 4) , 4, "MM" ); + + // Corrupt settings can cause huge values, which automatically get + // put into floating point variables instead of integer variables + // Hence Check that this is an integer, as problems will occur if it isn't + if ( is_integer( $value ) ) + { + + // Check if the current setting number is in the Definitions array + if ( array_key_exists( $i, ($GLOBALS[ "Minolta_Camera_Setting_Definitions" ]) ) === TRUE ) + { + // Setting is in definitions array + + // Get some of the information from the settings definitions array + $tagname = $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ][ 'Name' ]; + $units = ""; + if ( array_key_exists( 'Units', $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ] ) ) + { + $units = $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ][ 'Units' ]; + } + // Check what type of field the setting is, and process accordingly + + if ( $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ]['Type'] == "Lookup" ) + { + // This is a lookup table field + + // Check if the value read is in the lookup table + if ( array_key_exists( $value, $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ] ) ) + { + // Value is in the lookup table - Add it to the text + $output_str .= $tagname . ": " . $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ][ $value ] . "\n"; + } + else + { + // Value is Not in the lookup table + // Add a message if the user has requested to see unknown tags + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + $output_str .= $tagname . ": Unknown Reserved Value $value\n"; + } + + } + } + else if ( $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ]['Type'] == "Numeric" ) + { + // This is a numeric type add it as is to the output, with units + $output_str .= $tagname . ": $value $units\n"; + } + else if ( $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ]['Type'] == "Special" ) + { + // This is a special setting, Process it according to the setting number + switch ( $i ) + { + case 9: // Apex Film Speed Value + $output_str .= $tagname . ": " . ($value/8-1) . " ( ISO " . ((pow(2,($value/8-1)))*3.125) . " )\n"; + break; + + case 10: // Apex Shutter Speed Time Value + $output_str .= $tagname . ": " . ($value/8-6); + if ( $value == 8 ) + { + $output_str .= " ( 30 seconds )\n"; + } + else + { + $output_str .= " ( " . ( pow(2, (48-$value)/8 ) ) . " seconds )\n"; + } + break; + + case 11: // Apex Aperture Value + $output_str .= $tagname . ": " . ($value/8-1) . " ( F Stop: " . (pow(2,( $value/16-0.5 ))) . " )\n"; + break; + + case 14: // Exposure Compensation + $output_str .= $tagname . ": " . ($value/3-2) . " $units\n"; + break; + + case 17: // Interval Length + $output_str .= $tagname . ": " . ($value+1) . " $units\n"; + break; + + case 19: // Focal Length + $output_str .= $tagname . ": " . ($value/256) . " $units\n"; + break; + + case 22: // Date + $output_str .= $tagname . ": " . sprintf( "%d/%d/%d", ($value%256), floor(($value - floor($value/65536)*65536)/256 ), floor($value/65536) ) . " $units\n"; + break; + + case 23: // Time + $output_str .= $tagname . ": " . sprintf( "%2d:%02d:%02d", floor($value/65536), floor(($value - floor($value/65536)*65536)/256 ), ($value%256) ) . " $units\n"; + break; + + case 24: // Max Aperture at this focal length + $output_str .= $tagname . ": F" . (pow(2,($value/16-0.5))) ." $units\n"; + break; + + case 29: // White Balance Red + case 30: // White Balance Green + case 31: // White Balance Blue + $output_str .= $tagname . ": " . ($value/256) ." $units\n"; + break; + + case 32: // Saturation + case 33: // Contrast + $output_str .= $tagname . ": " . ($value-3) ." $units\n"; + break; + + case 36: // Flash Compensation + $output_str .= $tagname . ": " . (($value-6)/3) ." $units\n"; + break; + + case 42: // Color Filter + $output_str .= $tagname . ": " . ($value-3) ." $units\n"; + break; + + case 45: // Apex Brightness Value + $output_str .= $tagname . ": " . ($value/8-6) ." $units\n"; + break; + + default: // Unknown Special Setting + // If user has requested to see the unknown tags, then add the setting to the output + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + $output_str .= "Unknown Special Tag: $tagname, Value: $value $units\n"; + } + break; + } + } + else + { + // Unknown Setting Type + // If user has requested to see the unknown tags, then add the setting to the output + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + $output_str .= "Unknown Tag Type Tag $i, Value: " . $value . "\n"; + } + } + + + } + else + { + // Unknown Setting + // If user has requested to see the unknown tags, then add the setting to the output + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + $output_str .= "Unknown Minolta Camera Setting Tag $i, Value: " . $value . "\n"; + } + } + } + + } + + // Return the text string + return $output_str; + } + else if ( ( $Exif_Tag['Tag Number'] == 0x0088 ) || + ( $Exif_Tag['Tag Number'] == 0x0081 ) ) + { + // Konica/Minolta Thumbnail + return "Thumbnail"; + } + else + { + return FALSE; + } + +} + +/****************************************************************************** +* End of Function: get_Minolta_Text_Value +******************************************************************************/ + + + + + + + +/****************************************************************************** +* Global Variable: Minolta_Camera_Setting_Definitions +* +* Contents: This global variable provides definitions for the fields +* contained in the Konica/Minolta Camera Settings Makernote tag, +* indexed by their setting number. +* +******************************************************************************/ + +$GLOBALS[ "Minolta_Camera_Setting_Definitions" ] = array( + +2 => array ( 'Name' => "Exposure Mode", + 'Type' => "Lookup", + 0 => "P", + 1 => "A", + 2 => "S", + 3 => "M" ), + +3 => array ( 'Name' => "Flash Mode", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Red-eye reduction", + 2 => "Rear flash sync", + 3 => "Wireless" ), + +4 => array ( 'Name' => "White Balance", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Daylight", + 2 => "Cloudy", + 3 => "Tungsten", + 5 => "Custom", + 7 => "Fluorescent", + 8 => "Fluorescent 2", + 11 => "Custom 2", + 12 => "Custom 3" ), + +5 => array ( 'Name' => "Image Size", + 'Type' => "Lookup", + 0 => "2560 x 1920 (2048x1536 - DiMAGE 5 only)", + 1 => "1600 x 1200", + 2 => "1280 x 960", + 3 => "640 x 480" ), + + +6 => array ( 'Name' => "Image Quality", + 'Type' => "Lookup", + 0 => "Raw", + 1 => "Super Fine", + 2 => "Fine", + 3 => "Standard", + 4 => "Economy", + 5 => "Extra Fine" ), + +7 => array ( 'Name' => "Shooting Mode", + 'Type' => "Lookup", + 0 => "Single", + 1 => "Continuous", + 2 => "Self-timer", + 4 => "Bracketing", + 5 => "Interval", + 6 => "UHS Continuous", + 7 => "HS Continuous" ), + + +8 => array ( 'Name' => "Metering Mode", + 'Type' => "Lookup", + 0 => "Multi-Segment", + 1 => "Centre Weighted", + 2 => "Spot" ), + + +9 => array ( 'Name' => "Apex Film Speed Value", + 'Type' => "Special" ), + +// 09 FilmSpeed , APEX Film Speed Value , Speed value = x/8-1 , ISO= (2^(x/8-1))*3.125 + + +10 => array ( 'Name' => "Apex Shutter Speed Time Value", + 'Type' => "Special", + 'Units' => "Seconds?" ), + +// APEX Time Value , Time value = x/8-6 , ShutterSpeed = 2^( (48-x)/8 ), ! Due to rounding error x=8 should be displayed as 30 sec. + +11 => array ( 'Name' => "Apex Aperture Value", + 'Type' => "Special" ), + +// APEX Aperture Value ApertureValue = x/8-1 , Aperture = 2^( x/16-0.5 ) + + +12 => array ( 'Name' => "Macro Mode", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On" ), + +13 => array ( 'Name' => "Digital Zoom", + 'Type' => "Lookup", + 0 => "Off", + 1 => "Electronic magnification was used", + 2 => "Digital zoom 2x" ), + + +14 => array ( 'Name' => "Exposure Compensation", + 'Type' => "Special", + 'Units' => "EV" ), + +// EV = x/3 -2 Exposure compensation in EV + + +15 => array ( 'Name' => "Bracket Step", + 'Type' => "Lookup", + 0 => "1/3 EV", + 1 => "2/3 EV", + 2 => "1 EV" ), + + +17 => array ( 'Name' => "Interval Length", + 'Type' => "Special", + 'Units' => "Min" ), + +// interval is x+1 min (used with interval mode) + + +18 => array ( 'Name' => "Interval Number", + 'Type' => "Numeric", + 'Units' => "frames" ), + +19 => array ( 'Name' => "Focal Length", + 'Type' => "Special", + 'Units' => "mm" ), + +// x / 256 is real focal length in mm , x / 256 * 3.9333 is 35-mm equivalent + + +20 => array ( 'Name' => "Focus Distance", + 'Type' => "Numeric", + 'Units' => "mm ( 0 = Infinity)" ), + + +21 => array ( 'Name' => "Flash Fired", + 'Type' => "Lookup", + 0 => "No", + 1 => "Yes" ), + +22 => array ( 'Name' => "Date", + 'Type' => "Special" ), + +// yyyymmdd , year = x/65536 , month = x/256-x/65536*256 , day = x%256 + +23 => array ( 'Name' => "Time", + 'Type' => "Special" ), + +// hhhhmmss , hour = x/65536 , minute = x/256-x/65536*256 , second = x%256 + + +24 => array ( 'Name' => "Max Aperture at this focal length", + 'Type' => "Special" ), + +// Fno = 2^(x/16-0.5) + + +27 => array ( 'Name' => "File Number Memory", + 'Type' => "Lookup", + 0 => "Off", + 1 => "On" ), + +28 => array ( 'Name' => "Last File Number", + 'Type' => "Numeric", + 'Units' => " ( 0 = File Number Memory is Off)" ), + + +29 => array ( 'Name' => "White Balance Red", + 'Type' => "Special" ), + +// x/256 - red white balance coefficient used for this picture + + +30 => array ( 'Name' => "White Balance Green", + 'Type' => "Special" ), + +// x/256 - green white balance coefficient used for this picture + +31 => array ( 'Name' => "White Balance Blue", + 'Type' => "Special" ), + +// x/256 - blue white balance coefficient used for this picture + + +32 => array ( 'Name' => "Saturation", + 'Type' => "Special" ), + +// x-3 = saturation + + +33 => array ( 'Name' => "Contrast", + 'Type' => "Special" ), + +// x-3 - contrast + + +34 => array ( 'Name' => "Sharpness", + 'Type' => "Lookup", + 0 => "Hard", + 1 => "Normal", + 2 => "Soft" ), + + +35 => array ( 'Name' => "Subject Program", + 'Type' => "Lookup", + 0 => "none", + 1 => "portrait", + 2 => "text", + 3 => "night portrait", + 4 => "sunset", + 5 => "sports action" ), + + +36 => array ( 'Name' => "Flash Compensation", + 'Type' => "Special", + 'Units' => "EV" ), + +// (x-6)/3 = flash compensation in EV + + +37 => array ( 'Name' => "ISO Setting", + 'Type' => "Lookup", + 0 => "100", + 1 => "200", + 2 => "400", + 3 => "800", + 4 => "auto", + 5 => "64" ), + + +38 => array ( 'Name' => "Camera Model", + 'Type' => "Lookup", + 0 => "DiMAGE 7", + 1 => "DiMAGE 5", + 2 => "DiMAGE S304", + 3 => "DiMAGE S404", + 4 => "DiMAGE 7i", + 5 => "DiMAGE 7Hi", + 6 => "DiMAGE A1", + 7 => "DiMAGE S414" ), + + +39 => array ( 'Name' => "Interval Mode", + 'Type' => "Lookup", + 0 => "Still Image", + 1 => "Time-lapse Movie" ), + + +40 => array ( 'Name' => "Folder Name", + 'Type' => "Lookup", + 0 => "Standard Form", + 1 => "Data Form" ), + + +41 => array ( 'Name' => "Color Mode", + 'Type' => "Lookup", + 0 => "Natural Color", + 1 => "Black & White", + 2 => "Vivid Color", + 3 => "Solarization", + 4 => "Adobe RGB" ), + + +42 => array ( 'Name' => "Color Filter", + 'Type' => "Special" ), + +// x-3 = color filter + + +43 => array ( 'Name' => "Black & White Filter", + 'Type' => "Numeric" ), + + + +44 => array ( 'Name' => "Internal Flash", + 'Type' => "Lookup", + 0 => "Not Fired", + 1 => "Fired" ), + + + +45 => array ( 'Name' => "Apex Brightness Value", + 'Type' => "Special" ), + +// Brightness Value = x/8-6 + + + + +46 => array ( 'Name' => "Spot Focus Point X Coordinate", + 'Type' => "Numeric" ), + + + +47 => array ( 'Name' => "Spot Focus Point Y Coordinate", + 'Type' => "Numeric" ), + + + +48 => array ( 'Name' => "Wide Focus Zone", + 'Type' => "Lookup", + 0 => "No Zone or AF Failed", + 1 => "Center Zone (Horizontal Orientation)", + 2 => "Center Zone (Vertical Orientation)", + 3 => "Left Zone", + 4 => "Right Zone" ), + + +49 => array ( 'Name' => "Focus Mode", + 'Type' => "Lookup", + 0 => "Auto Focus", + 1 => "Manual Focus" ), + + +50 => array ( 'Name' => "Focus Area", + 'Type' => "Lookup", + 0 => "Wide Focus (normal)", + 1 => "Spot Focus" ), + + +51 => array ( 'Name' => "DEC Switch Position", + 'Type' => "Lookup", + 0 => "Exposure", + 1 => "Contrast", + 2 => "Saturation", + 3 => "Filter" ), + + + +); + +/****************************************************************************** +* End of Global Variable: Minolta_Camera_Setting_Definitions +******************************************************************************/ + + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/kyocera.php b/includes/jpeg_metadata_tk/Makernotes/kyocera.php new file mode 100644 index 0000000..bb5f17e --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/kyocera.php @@ -0,0 +1,241 @@ +<?php + +/****************************************************************************** +* +* Filename: kyocera.php +* +* Description: Kyocera Makernote Parser +* Provides functions to decode an Kyocera EXIF makernote and to interpret +* the resulting array into html. Includes Kyocera's Contax brand +* +* Kyocera Makernote Format: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 22 Bytes "KYOCERA \x00\x00\x00" +* IFD Data Variable NON-Standard IFD Data using Kyocera Tags +* IFD has no Next-IFD pointer at end of IFD, +* and Offsets are relative to the start +* of the current IFD tag, not the TIFF header +* ---------------------------------------------------------------- +* +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Kyocera_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Kyocera_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Kyocera_Makernote_Html"; + + + + +/****************************************************************************** +* +* Function: get_Kyocera_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Kyocera_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + + // Check if the Make Field contains the word Contax or Kyocera + if ( ( stristr( $Make_Field, "Contax" ) === FALSE ) && + ( stristr( $Make_Field, "Kyocera" ) === FALSE ) ) + { + // Kyocera or Contax not found in maker field - abort + return FALSE; + } + + + // Check if the header exists at the start of the Makernote + if ( substr( $Makernote_Tag['Data'], 0, 22 ) != "KYOCERA \x00\x00\x00" ) + { + // This isn't a Kyocera Makernote, abort + return FALSE ; + } + + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 22 ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Kyocera", True, False ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Kyocera"; + $Makernote_Tag['Makernote Tags'] = "Kyocera"; + + + // Return the new tag + return $Makernote_Tag; +} + +/****************************************************************************** +* End of Function: get_Kyocera_Makernote +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: get_Kyocera_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Kyocera_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Kyocera_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + // Check that this tag uses Kyocera tags, otherwise it can't be interpreted here + if ( $Tag_Definitions_Name == "Kyocera" ) + { + // No Special Kyocera tags so far + return FALSE; + } + + return FALSE; + +} + +/****************************************************************************** +* End of Function: get_Kyocera_Text_Value +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: get_Kyocera_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Kyocera_Makernote_Html( $Makernote_tag, $filename ) +{ + + // Check that this is a Kyocera Makernote, otherwise it can't be interpreted here + if ( $Makernote_tag['Makernote Type'] != "Kyocera" ) + { + // Not a Kyocera Makernote - cannot interpret it - abort + return False; + } + + // Interpret the IFD and return the HTML + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + +} + +/****************************************************************************** +* End of Function: get_Kyocera_Makernote_Html +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Kyocera +* +* Contents: This global variable provides definitions of the known Kyocera +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Kyocera"] = array( + +1 => array( 'Name' => "Kyocera Proprietory Format Thumbnail", + 'Type' => "Unknown" ), + +0x0E00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Kyocera +******************************************************************************/ + + + + + + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/nikon.php b/includes/jpeg_metadata_tk/Makernotes/nikon.php new file mode 100644 index 0000000..b860d51 --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/nikon.php @@ -0,0 +1,731 @@ +<?php + +/****************************************************************************** +* +* Filename: Nikon.php +* +* Description: Nikon Makernote Parser +* Provides functions to decode an Nikon EXIF makernote and to interpret +* the resulting array into html. +* +* Nikon Makernote Format: +* +* Type 1 +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 8 Bytes "Nikon\x00\x01\x00" +* IFD Data Variable Standard IFD Data using Nikon Type 1 Tags +* ---------------------------------------------------------------- +* +* Type 2 +* +* Field Size Description +* ---------------------------------------------------------------- +* IFD Data Variable Standard IFD Data using Nikon Type 3 Tags +* ---------------------------------------------------------------- +* +* Type 3 +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 10 Bytes "Nikon\x00\x02\x10\x00\x00" +* or +* "Nikon\x00\x02\x00\x00\x00" +* TIFF Data Variable TIFF header, with associated +* Standard IFD Data using, Nikon +* Type 3 Tags. Offsets are from +* this second tiff header +* ---------------------------------------------------------------- +* +* // Note: The Nikon Coolpix 775 uses the Fujifilm makernote format +* +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Nikon_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Nikon_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Nikon_Makernote_Html"; + + + + +/****************************************************************************** +* +* Function: get_Nikon_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Nikon_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + // Check if the Make Field contains the word Nikon + if ( stristr( $Make_Field, "Nikon" ) === FALSE ) + { + // Nikon not found in maker field - abort + return FALSE; + } + + + // Check if the header exists at the start of the Makernote + if ( substr( $Makernote_Tag['Data'],0 , 8 ) == "Nikon\x00\x01\x00" ) + { + // Nikon Type 1 Makernote + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Nikon Type 1" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Nikon Type 1"; + $Makernote_Tag['Makernote Tags'] = "Nikon Type 1"; + + + // Return the new tag + return $Makernote_Tag; + + + } + else if ( ( substr( $Makernote_Tag['Data'],0 , 10 ) == "Nikon\x00\x02\x10\x00\x00" ) || + ( substr( $Makernote_Tag['Data'],0 , 10 ) == "Nikon\x00\x02\x00\x00\x00" ) ) + { + // Nikon Type 3 Makernote + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 10 ); + + // Read the TIFF header and IFD(s) into an array + $Makernote_Tag['Decoded Data'] = process_TIFF_Header( $filehnd, "Nikon Type 3" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Makernote Type'] = "Nikon Type 3"; + $Makernote_Tag['Makernote Tags'] = "Nikon Type 3"; + $Makernote_Tag['Decoded'] = TRUE; + + + // Return the new tag + return $Makernote_Tag; + } + else if ( substr( $Makernote_Tag['Data'],0 , 8 ) == "FUJIFILM" ) + { + // Fuji Makernote - used by Nikon Coolpix 775 + // Let the Fujifilm library handle it + return False; + } + else + { + // No header - Nikon Type 2 + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 0 ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Nikon Type 3" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Nikon Type 2"; + $Makernote_Tag['Makernote Tags'] = "Nikon Type 3"; + + + // Return the new tag + return $Makernote_Tag; + } + + + // Shouldn't get here + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Nikon_Makernote +******************************************************************************/ + + + + + + + + + + + +/****************************************************************************** +* +* Function: get_Nikon_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Nikon_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Nikon_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + + // Check that this tag uses the Nikon tags, otherwise it can't be interpreted here + // And check which variety of tags + if ( $Tag_Definitions_Name == "Nikon Type 1" ) + { + // No special tags for Nikon type 1 so far + return FALSE; + } + else if ( $Tag_Definitions_Name == "Nikon Type 3" ) + { + // Nikon Type 3 special tag + + // Process tag according to it's tag number + + if ( $Exif_Tag['Tag Number'] == 1) // Nikon Makernote Version - some are binary, some are text + { + return "\"" .HTML_UTF8_Escape( $Exif_Tag['Data'] ) . "\" (" . bin2hex( $Exif_Tag['Data'] ) . " hex)"; + } + + else if ( ( $Exif_Tag['Tag Number'] == 2 ) || // ISO Speed Used + ( $Exif_Tag['Tag Number'] == 19 ) ) // ISO Speed Requested + { + // ISO speed settings - should be the second of two values + if ( count( $Exif_Tag['Data'] ) == 2 ) + { + // There are two values - display the second + return $Exif_Tag['Data'][1] . " " . $Exif_Tag['Units']; + } + else + { + // There is not two values - display generic version of values + return get_IFD_value_as_text( $Exif_Tag['Data'] ) . " " . $Exif_Tag['Units']; + } + } + else if ( $Exif_Tag['Tag Number'] == 137 ) // Bracketing & Shooting Mode + { + // Add shooting mode to output from first two bits + switch ( $Exif_Tag['Data'][0] & 0x03 ) + { + case 0x00: + $outputstr = "Shooting Mode: Single Frame\n"; + break; + case 0x01: + $outputstr = "Shooting Mode: Continuous\n"; + break; + case 0x02: + $outputstr = "Shooting Mode: Self Timer\n"; + break; + case 0x03: + $outputstr = "Shooting Mode: Remote??\n"; + break; + default: + $outputstr = "Shooting Mode: Unknown\n"; + break; + } + + // Add flash bracketing to output from fifth bit + if ( ( $Exif_Tag['Data'][0] & 0x10 ) == 0x10 ) + { + $outputstr .= "AE/Flash Bracketing On\n"; + } + else + { + $outputstr .= "AE/Flash Bracketing Off\n"; + } + + // Add white balance bracketing to output from seventh bit + if ( ( $Exif_Tag['Data'][0] & 0x40 ) == 0x40 ) + { + $outputstr .= "White Balance Bracketing On\n"; + } + else + { + $outputstr .= "White Balance Bracketing Off\n"; + } + + // Return the output + return $outputstr; + + } + else if ( $Exif_Tag['Tag Number'] == 136 ) // Auto Focus Area + { + // Create a string to receive the output + $outputstr = ""; + + // If all zeros, this could be manual focus + if ( $Exif_Tag['Data'] == "\x00\x00\x00\x00" ) + { + $outputstr .= "Manual Focus, or\n"; + } + + // Add AF mode according to the first byte + switch ( ord($Exif_Tag['Data']{0}) ) + { + case 0x00: + $outputstr .= "Auto Focus Mode: Single Area\n"; + break; + case 0x01: + $outputstr .= "Auto Focus Mode: Dynamic Area\n"; + break; + case 0x02: + $outputstr .= "Auto Focus Mode: Closest Subject\n"; + break; + default: + $outputstr .= "Auto Focus Mode: Unknown AF Mode\n"; + break; + } + + // Add AF area according to second byte + switch ( ord($Exif_Tag['Data']{1}) ) + { + case 0x00: + $outputstr .= "Auto Focus Area Selected: Centre\n"; + break; + case 0x01: + $outputstr .= "Auto Focus Area Selected: Top\n"; + break; + case 0x02: + $outputstr .= "Auto Focus Area Selected: Bottom\n"; + break; + case 0x03: + $outputstr .= "Auto Focus Area Selected: Left\n"; + break; + case 0x04: + $outputstr .= "Auto Focus Area Selected: Right\n"; + break; + } + + // Add properly focused areas to output according to byte 3 bits + + $outputstr .= "Properly Focused Area(s): "; + if ( ord($Exif_Tag['Data']{3}) == 0x00 ) + { + $outputstr .= "None"; + } + if ( ( ord($Exif_Tag['Data']{3}) & 0x01 ) == 0x01 ) + { + $outputstr .= "Centre "; + } + if ( ( ord($Exif_Tag['Data']{3}) & 0x02 ) == 0x02 ) + { + $outputstr .= "Top "; + } + if ( ( ord($Exif_Tag['Data']{3}) & 0x04 ) == 0x04 ) + { + $outputstr .= "Bottom "; + } + if ( ( ord($Exif_Tag['Data']{3}) & 0x08 ) == 0x08 ) + { + $outputstr .= "Left "; + } + if ( ( ord($Exif_Tag['Data']{3}) & 0x10 ) == 0x10 ) + { + $outputstr .= "Right "; + } + $outputstr .= "\n"; + + // return the string + return $outputstr; + } + else + { + // Unknown special tag + return FALSE; + } + } + + + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Nikon_Text_Value +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_Nikon_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Nikon_Makernote_Html( $Makernote_tag, $filename ) +{ + + // Check that this is a Nikon Makernote, otherwise it can't be interpreted here + if ( ( $Makernote_tag['Makernote Type'] != "Nikon Type 1" ) && + ( $Makernote_tag['Makernote Type'] != "Nikon Type 2" ) && + ( $Makernote_tag['Makernote Type'] != "Nikon Type 3" ) ) + { + // Not a Nikon Makernote - cannot interpret it - abort + return FALSE;; + } + + // Interpret the IFD and return the HTML + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); +} + +/****************************************************************************** +* End of Function: get_Nikon_Makernote_Html +******************************************************************************/ + + + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Nikon Type 1 +* +* Contents: This global variable provides definitions of the known Nikon Type 1 +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Nikon Type 1"] = array( + + +3 => array( 'Name' => "Quality", + 'Description' => "1: VGA Basic, 2: VGA Normal, 3: VGA Fine, 4: SXGA Basic, 5: SXGA Normal, 6: SXGA Fine", + 'Type' => "Lookup", + 1 => "VGA (640x480) Basic", + 2 => "VGA (640x480) Normal", + 3 => "VGA (640x480) Fine", + 4 => "SXGA (1280x960) Basic", + 5 => "SXGA (1280x960) Normal", + 6 => "SXGA (1280x960) Fine", + 7 => "Unknown, Possibly XGA (1024x768) Basic", + 8 => "Unknown, Possibly XGA (1024x768) Basic", + 9 => "Unknown, Possibly XGA (1024x768) Basic", + 10 => "UXGA (1600x1200) Basic", + 11 => "UXGA (1600x1200) Normal", + 12 => "UXGA (1600x1200) Fine" ), + +4 => array( 'Name' => "Colour Mode", + 'Description' => "1: Colour, 2: Monochrome.", + 'Type' => "Lookup", + 1 => "Colour", + 2 => "Monochrome" ), + +5 => array( 'Name' => "Image Adjustment", + 'Description' => "0: Normal, 1: Bright+, 2: Bright-, 3: Contrast+, 4: Contrast-.", + 'Type' => "Lookup", + 0 => "Normal", + 1 => "Bright+", + 2 => "Bright-", + 3 => "Contrast+", + 4 => "Contrast-" ), + +6 => array( 'Name' => "CCD Sensitivity", + 'Description' => "0: ISO80, 2: ISO160, 4: ISO320, 5: ISO100", + 'Type' => "Lookup", + 0 => "ISO 80", + 2 => "ISO 160", + 4 => "ISO 320", + 5 => "ISO 100" ), + +7 => array( 'Name' => "White Balance", + 'Description' => "0: Auto, 1: Preset, 2: Daylight, 3: Incandescense, 4: Fluorescence, 5: Cloudy, 6: SpeedLight", + 'Type' => "Lookup", + 0 => "Auto", + 1 => "Preset", + 2 => "Daylight", + 3 => "Incandescense", + 4 => "Flourescence", + 5 => "Cloudy", + 6 => "Speedlight" ), + +8 => array( 'Name' => "Focus", + 'Description' => "If infinite focus, value is '1/0'.", + 'Type' => "Numeric" ), + +10 => array( 'Name' => "Digital Zoom", + 'Description' => "'160/100' means 1.6x digital zoom, '0/100' means no digital zoom (optical zoom only).", + 'Type' => "Numeric" ), + +11 => array( 'Name' => "Converter", + 'Description' => "If Fisheye Converter is used, value is 1", + 'Type' => "Lookup", + 0 => "No Converter Used", + 1 => "Fish-eye Converter Used" ) + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Nikon Type 1 +******************************************************************************/ + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Nikon Type 3 +* +* Contents: This global variable provides definitions of the known Nikon Type 3 +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Nikon Type 3"] = array( + + +1 => array( 'Name' => "Nikon Makernote Version", + 'Type' => "Special" ), + +2 => array( 'Name' => "ISO Speed Used", + 'Type' => "Special" ), + +3 => array( 'Name' => "Colour Mode", + 'Type' => "String" ), + +4 => array( 'Name' => "Quality", + 'Type' => "String" ), + +5 => array( 'Name' => "White Balance", + 'Type' => "String" ), + +6 => array( 'Name' => "Sharpening", + 'Type' => "String" ), + +7 => array( 'Name' => "Focus Mode", + 'Type' => "String" ), + +8 => array( 'Name' => "Flash Setting", + 'Type' => "String" ), + +9 => array( 'Name' => "Auto Flash Mode", + 'Type' => "String" ), + +11 => array( 'Name' => "White Balance Bias Value", + 'Type' => "Numeric", + 'Units' => "(Units Approx: 100 Mired per increment)" ), + +12 => array( 'Name' => "White Balance Red, Blue Coefficients?", + 'Type' => "Numeric" ), + +15 => array( 'Name' => "ISO Selection?", + 'Type' => "String" ), + + +18 => array( 'Name' => "Flash Compensation", + 'Type' => "Lookup", + 0x06 => "+1.0 EV", + 0x04 => "+0.7 EV", + 0x03 => "+0.5 EV", + 0x02 => "+0.3 EV", + 0x00 => "0.0 EV", + 0xfe => "-0.3 EV", + 0xfd => "-0.5 EV", + 0xfc => "-0.7 EV", + 0xfa => "-1.0 EV", + 0xf8 => "-1.3 EV", + 0xf7 => "-1.5 EV", + 0xf6 => "-1.7 EV", + 0xf4 => "-2.0 EV", + 0xf2 => "-2.3 EV", + 0xf1 => "-2.5 EV", + 0xf0 => "-2.7 EV", + 0xee => "-3.0 EV" ), + +19 => array( 'Name' => "ISO Speed Requested", + 'Type' => "Special", + 'Units' => "(May be different to Speed Used when Auto ISO is on)" ), + + +22 => array( 'Name' => "Photo corner coordinates", + 'Type' => "Numeric", + 'Units' => "Pixels" ), + +24 => array( 'Name' => "Flash Bracket Compensation Applied", + 'Type' => "Lookup", + 0x06 => "+1.0 EV", + 0x04 => "+0.7 EV", + 0x03 => "+0.5 EV", + 0x02 => "+0.3 EV", + 0x00 => "0.0 EV", + 0xfe => "-0.3 EV", + 0xfd => "-0.5 EV", + 0xfc => "-0.7 EV", + 0xfa => "-1.0 EV", + 0xf8 => "-1.3 EV", + 0xf7 => "-1.5 EV", + 0xf6 => "-1.7 EV", + 0xf4 => "-2.0 EV", + 0xf2 => "-2.3 EV", + 0xf1 => "-2.5 EV", + 0xf0 => "-2.7 EV", + 0xee => "-3.0 EV" ), + +25 => array( 'Name' => "AE Bracket Compensation Applied", + 'Type' => "Numeric", + 'Units' => "EV" ), + +128 => array( 'Name' => "Image Adjustment?", + 'Type' => "String" ), + +129 => array( 'Name' => "Tone Compensation (Contrast)", + 'Type' => "String" ), + +130 => array( 'Name' => "Auxiliary Lens (Adapter)", + 'Type' => "String" ), + +131 => array( 'Name' => "Lens Type?", + 'Type' => "Lookup", + 6 => "Nikon D series Lens", + 14 => "Nikon G series Lens" ), + +132 => array( 'Name' => "Lens Min/Max Focal Length, Min/Max Aperture", + 'Type' => "Numeric", + 'Units' => " mm, mm, F#, F#" ), + +133 => array( 'Name' => "Manual Focus Distance?", + 'Type' => "Numeric"), + +134 => array( 'Name' => "Digital Zoom Factor?", + 'Type' => "Numeric" ), + +135 => array( 'Name' => "Flash Used", + 'Type' => "Lookup", + 0 => "Flash Not Used", + 9 => "Flash Fired" ), + +136 => array( 'Name' => "Auto Focus Area", + 'Description' => "byte 1 : AF Mode: 00 = single area, 01 = Dynamic Area, 02 = Closest Subject\n + byte 2 : AF Area Selected : 00 = Centre, 01 = Top, 02 = Bottom, 03 = Left, 04 = Right\n + byte 3 : Unknown, always zero\n + byte 4 : Properly focused Area(s) : bit 0 = Centre, bit 1 = Top, bit 2 = Bottom, bit 3 = Left, bit 4 = Right", + 'Type' => "Special" ), + +137 => array( 'Name' => "Bracketing & Shooting Mode", + 'Description' => "bit 0&1 (0 = single frame, 1 = continuous,2=timer, 3=remote timer? 4 = remote?\n + bit 4, Bracketing on or off\n + bit 6, white Balance Bracketing on", + 'Type' => "Special" ), + +141 => array( 'Name' => "Colour Mode", + 'Description' =>"1a = Portrait sRGB, 2 = Adobe RGB, 3a = Landscape sRGB", + 'Type' => "String" ), + +143 => array( 'Name' => "Scene Mode?", + 'Type' => "Numeric" ), + +144 => array( 'Name' => "Lighting Type", + 'Type' => "String" ), + +146 => array( 'Name' => "Hue Adjustment", + 'Type' => "Numeric", + 'Units' => "Degrees" ), + +148 => array( 'Name' => "Saturation?", + 'Type' => "Lookup", + -3 => "Black and White", + -2 => "-2", + -1 => "-1", + 0 => "Normal", + 1 => "+1", + 2 => "+2" ), + +149 => array( 'Name' => "Noise Reduction", + 'Type' => "String" ), + +167 => array( 'Name' => "Total Number of Shutter Releases for Camera", + 'Type' => "Numeric", + 'Units' => "Shutter Releases" ), + +169 => array( 'Name' => "Image optimisation", + 'Type' => "String" ), + +170 => array( 'Name' => "Saturation", + 'Type' => "String" ), + +171 => array( 'Name' => "Digital Vari-Program", + 'Type' => "String" ) + + +// Tags that exist but are unknown: 10, 13, 14, 16, 17, 23, 24, 138, 139, 145, +// 151, 152, 160, 162 163, 165, 166, 168 + + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Nikon Type 3 +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/olympus.php b/includes/jpeg_metadata_tk/Makernotes/olympus.php new file mode 100644 index 0000000..a9717b0 --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/olympus.php @@ -0,0 +1,486 @@ +<?php + +/****************************************************************************** +* +* Filename: olympus.php +* +* Description: Olympus Makernote Parser +* Provides functions to decode an Olympus EXIF makernote and to interpret +* the resulting array into html. +* +* Olympus Makernote Format: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 7 Bytes "OLYMP\x00\x01" or "OLYMP\x00\x02" +* Unknown 1 Bytes Unknown +* IFD Data Variable Standard IFD Data using Olympus Tags +* ---------------------------------------------------------------- +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.11 +* +* Changes: 1.00 -> 1.11 : changed get_Olympus_Makernote_Html to allow thumbnail links to work when +* toolkit is portable across directories +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Olympus_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Olympus_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Olympus_Makernote_Html"; + + + + +include_once dirname(__FILE__) .'/../pjmt_utils.php'; // Change: as of version 1.11 - added to allow directory portability + + + + +/****************************************************************************** +* +* Function: get_Olympus_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Olympus_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + + // Check if the Make Field contains the word Olympus + if ( stristr( $Make_Field, "Olympus" ) === FALSE ) + { + return FALSE; + } + + // Check if the header exists at the start of the Makernote + if ( ( substr( $Makernote_Tag['Data'], 0, 7 ) != "OLYMP\x00\x01" ) && + ( substr( $Makernote_Tag['Data'], 0, 7 ) != "OLYMP\x00\x02" ) ) + { + // This isn't a Olympus Makernote, abort + return FALSE ; + } + + + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Olympus" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Olympus"; + $Makernote_Tag['Makernote Tags'] = "Olympus"; + + + // Return the new tag + return $Makernote_Tag; +} + +/****************************************************************************** +* End of Function: get_Olympus_Makernote +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: get_Olympus_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Olympus_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Olympus_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + // Check that this tag uses the Olympus tags, otherwise it can't be decoded here + if ( $Tag_Definitions_Name !== "Olympus" ) + { + // Not an Olympus tag - can't decode it + return FALSE; + } + + + // Process the tag acording to it's tag number, to produce a text value + if ( $Exif_Tag['Tag Number'] == 0x200 ) + { + // Special Mode Tag + + // Add info from the first value to the output string + switch ( $Exif_Tag['Data'][0] ) + { + case 0: $outputstr = "Normal\n"; + break; + case 2: $outputstr = "Fast\n"; + break; + case 3: $outputstr = "Panorama\n"; + break; + default: $outputstr = "Unknown Mode ( " . $Exif_Tag['Data'][0] . " )\n"; + break; + } + + // Add info from the second value to the output string + $outputstr .= "Sequence Number: " . $Exif_Tag['Data'][1] . "\n"; + + // Add info from the third value to the output string + switch ( $Exif_Tag['Data'][2] ) + { + case 0: // Do nothing + break; + case 1: $outputstr .= "Panorama Direction: Left to Right\n"; + break; + case 2: $outputstr .= "Panorama Direction: Right to Left\n"; + break; + case 3: $outputstr .= "Panorama Direction: Bottom to Top\n"; + break; + case 4: $outputstr .= "Panorama Direction: Top to Bottom\n"; + break; + default: $outputstr .= "Unknown Panorama Direction\n"; + break; + } + + // Return the output string + return $outputstr; + } + else + { + // Unknown special tag - can't process it here + return FALSE; + } + + // Unknown special tag - can't process it here + return FALSE; + +} + +/****************************************************************************** +* End of Function: get_Olympus_Text_Value +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: get_Olympus_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Olympus_Makernote_Html( $Makernote_tag, $filename ) +{ + + // Check that this tag uses the Olympus tags, otherwise it can't be interpreted here + if ( $Makernote_tag['Makernote Tags'] != "Olympus" ) + { + // Not Olympus tags - can't interpret with this function + return FALSE; + } + + // Check if the Decoded data is valid + if ( $Makernote_tag['Decoded Data'][0] === FALSE ) + { + // Decoded data is not valid - can't interpret with this function + return FALSE; + } + + // Minolta Thumbnail 1 + if ( ( array_key_exists( 0x0088, $Makernote_tag['Decoded Data'][0] ) ) && + ( $Makernote_tag['Makernote Tags'] == "Olympus" ) ) + { + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/../get_minolta_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ."/.." ); + + // Add thumbnail link to html + $Makernote_tag['Decoded Data'][0][0x0088]['Text Value'] = "<a class=\"EXIF_Minolta_Thumb_Link\" href=\"$link_str\" ><img class=\"EXIF_Minolta_Thumb\" src=\"$link_str\"></a>"; + + $Makernote_tag['Decoded Data'][0][0x0088]['Type'] = "String"; + } + // Minolta Thumbnail 2 + if ( ( array_key_exists( 0x0081, $Makernote_tag['Decoded Data'][0] ) ) && + ( $Makernote_tag['Makernote Tags'] == "Olympus" ) ) + { + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . " /../get_minolta_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ."/.." ); + + // Add thumbnail link to html + $Makernote_tag['Decoded Data'][0][0x0081]['Text Value'] = "<a class=\"EXIF_Minolta_Thumb_Link\" href=\"$link_str\" ><img class=\"EXIF_Minolta_Thumb\" src=\"$link_str\"></a>"; + $Makernote_tag['Decoded Data'][0][0x0081]['Type'] = "String"; + } + + // Interpret the IFD and return the HTML + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + +} + +/****************************************************************************** +* End of Function: get_Olympus_Makernote_Html +******************************************************************************/ + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Olympus +* +* Contents: This global variable provides definitions of the known Olympus +* Makernote tags, indexed by their tag number. +* It also includes Minolta and Agfa tags, as they use many of the +* same tags +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Olympus"] = array( + +0x0000 => array( 'Name' => "Makernote Version", // Minolta + 'Type' => "String" ), + +0x0001 => array( 'Name' => "Camera Settings", // Minolta + 'Type' => "Special" ), + +0x0003 => array( 'Name' => "Camera Settings", // Minolta + 'Type' => "Special" ), + +0x0040 => array( 'Name' => "Compressed Image Size", // Minolta + 'Type' => "Numeric", + 'Units' => "Bytes" ), + +0x0081 => array( 'Name' => "Minolta Thumbnail", // Minolta + 'Type' => "Special" ), + +0x0088 => array( 'Name' => "Minolta Thumbnail", // Minolta + 'Type' => "Special" ), + +0x0089 => array( 'Name' => "Minolta Thumbnail Length", // Minolta + 'Type' => "Numeric", + 'Units' => "bytes" ), + +0x0101 => array( 'Name' => "Colour Mode", // Minolta + 'Type' => "Lookup", + 0 => "Natural Colour", + 1 => "Black & White", + 2 => "Vivid colour", + 3 => "Solarization", + 4 => "AdobeRGB" ), + +0x0102 => array( 'Name' => "Image Quality", // Minolta + 'Type' => "Lookup", + 0 => "Raw", + 1 => "Super Fine", + 2 => "Fine", + 3 => "Standard", + 4 => "Extra Fine" ), + +0x0103 => array( 'Name' => "Image Quality?", // Minolta + 'Type' => "Lookup", + 0 => "Raw", + 1 => "Super Fine", + 2 => "Fine", + 3 => "Standard", + 4 => "Extra Fine" ), + + + +0x0200 => array( 'Name' => "Special Mode", + 'Type' => "Special" ), + + +0x0201 => array( 'Name' => "JPEG Quality", + 'Type' => "Lookup", + 1 => "Standard Quality", + 2 => "High Quality", + 3 => "Super High Quality" ), + +0x0202 => array( 'Name' => "Macro", + 'Type' => "Lookup", + 0 => "Normal (Not Macro)", + 1 => "Macro" ), + +0x0204 => array( 'Name' => "Digital Zoom", + 'Type' => "Numeric", + 'Units' => " x Digital Zoom, (0 or 1 = normal)" ), + +0x0207 => array( 'Name' => "Firmware Version", + 'Type' => "String" ), + + +0x0208 => array( 'Name' => "Picture Info Data", + 'Type' => "String" ), + +0x0209 => array( 'Name' => "Camera ID", + 'Type' => "String" ), + + +0x020B => array( 'Name' => "Image Width", // Epson Tag + 'Type' => "Pixels" ), + +0x020C => array( 'Name' => "Image Height", // Epson Tag + 'Type' => "Pixels" ), + +0x020D => array( 'Name' => "Original Manufacturer Model?", // Epson Tag + 'Type' => "String" ), + +0x0E00 => array( 'Name'=> "Print Image Matching Info", // Minolta Tag + 'Type' => "PIM" ), + +0x1004 => array( 'Name' => "Flash Mode", + 'Type' => "Numeric" ), + +0x1006 => array( 'Name' => "Bracket", + 'Type' => "Numeric" ), + +0x100B => array( 'Name' => "Focus Mode", + 'Type' => "Numeric" ), + +0x100C => array( 'Name' => "Focus Distance", + 'Type' => "Numeric" ), + +0x100D => array( 'Name' => "Zoom", + 'Type' => "Numeric" ), + +0x100E => array( 'Name' => "Macro Focus", + 'Type' => "Numeric" ), + +0x100F => array( 'Name' => "Sharpness", + 'Type' => "Numeric" ), + +0x1011 => array( 'Name' => "Colour Matrix", + 'Type' => "Numeric" ), + +0x1012 => array( 'Name' => "Black Level", + 'Type' => "Numeric" ), + +0x1015 => array( 'Name' => "White Balance", + 'Type' => "Numeric" ), + +0x1017 => array( 'Name' => "Red Bias", + 'Type' => "Numeric" ), + +0x1018 => array( 'Name' => "Blue Bias", + 'Type' => "Numeric" ), + +0x101A => array( 'Name' => "Serial Number", + 'Type' => "Numeric" ), + +0x1023 => array( 'Name' => "Flash Bias", + 'Type' => "Numeric" ), + +0x1029 => array( 'Name' => "Contrast", + 'Type' => "Numeric" ), + +0x102A => array( 'Name' => "Sharpness Factor", + 'Type' => "Numeric" ), + +0x102B => array( 'Name' => "Colour Control", + 'Type' => "Numeric" ), + +0x102C => array( 'Name' => "Valid Bits", + 'Type' => "Numeric" ), + +0x102D => array( 'Name' => "Coring Filter", + 'Type' => "Numeric" ), + +0x102E => array( 'Name' => "Final Width", + 'Type' => "Numeric" ), + +0x102F => array( 'Name' => "Final Height", + 'Type' => "Numeric" ), + +0x1034 => array( 'Name' => "Compression Ratio", + 'Type' => "Numeric" ), + + + +); + + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Olympus +******************************************************************************/ + + + + + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Makernotes/panasonic.php b/includes/jpeg_metadata_tk/Makernotes/panasonic.php new file mode 100644 index 0000000..2da827f --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/panasonic.php @@ -0,0 +1,292 @@ +<?php + +/****************************************************************************** +* +* Filename: panasonic.php +* +* Description: Panasonic Makernote Parser +* Provides functions to decode a Panasonic EXIF makernote and to interpret +* the resulting array into html. +* +* Panasonic Makernote Format: +* +* Type 1 - IFD form +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 12 Bytes "Panasonic\x00\x00\x00" +* IFD Data Variable NON-Standard IFD Data using Panasonic Tags +* There is no Next-IFD pointer after the IFD +* ---------------------------------------------------------------- +* +* Type 2 - Blank (Header only) +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 4 Bytes "MKED" +* Junk 1 or 2 bytes Blank or Junk data +* ---------------------------------------------------------------- +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Panasonic_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Panasonic_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Panasonic_Makernote_Html"; + + + + +/****************************************************************************** +* +* Function: get_Panasonic_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Panasonic_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + // Check if the Make Field contains the word Panasonic + if ( stristr( $Make_Field, "Panasonic" ) === FALSE ) + { + // No Panasonic in the maker - abort + return FALSE; + } + + + // Check if the header exists at the start of the Makernote + if ( substr( $Makernote_Tag['Data'], 0, 4 ) == "MKED" ) + { + // Panasonic Type 2 - Empty Makernote + // No Makernote Data + $Makernote_Tag['Makernote Type'] = "Panasonic Empty Makernote"; + $Makernote_Tag['Makernote Tags'] = "-"; + $Makernote_Tag['Decoded'] = TRUE; + + // Return the new tag + return $Makernote_Tag; + } + else if ( substr( $Makernote_Tag['Data'], 0, 12 ) == "Panasonic\x00\x00\x00" ) + { + // Panasonic Type 1 - IFD Makernote + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 12 ); + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Panasonic", FALSE, FALSE ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Panasonic"; + $Makernote_Tag['Makernote Tags'] = "Panasonic"; + + // Return the new tag + return $Makernote_Tag; + } + else + { + // Unknown Header + return FALSE; + } + + // Shouldn't get here + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Panasonic_Makernote +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* +* Function: get_Panasonic_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Panasonic_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Panasonic_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + + // Check that this tag uses the Olympus tags, otherwise it can't be decoded here + if ( $Tag_Definitions_Name == "Panasonic" ) + { + // No Special Tags yet + return FALSE; + } + + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Panasonic_Text_Value +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* +* Function: get_Panasonic_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Panasonic_Makernote_Html( $Makernote_tag, $filename ) +{ + if ( $Makernote_tag['Makernote Type'] == "Panasonic" ) + { + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + } + else if ( $Makernote_tag['Makernote Type'] == "Panasonic Empty Makernote" ) + { + // Do Nothing + return ""; + } + else + { + // Unknown Makernote Type + return FALSE; + } + + // Shouldn't get here + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Panasonic_Makernote_Html +******************************************************************************/ + + + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Panasonic +* +* Contents: This global variable provides definitions of the known Panasonic +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Panasonic"] = array( + +0x01 => array( 'Name' => "Quality Mode", + 'Type' => "Numeric" ), + +0x02 => array( 'Name' => "Version", + 'Type' => "String" ), + +0x1c => array( 'Name' => "Macro Mode", + 'Type' => "Lookup", + 1 => "On", + 2 => "Off" ), + +0x1f => array( 'Name' => "Record Mode", + 'Type' => "Lookup", + 1 => "Normal", + 2 => "Portrait", + 9 => "Macro" ), + +0xE00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Panasonic +******************************************************************************/ + + + + + +?> diff --git a/includes/jpeg_metadata_tk/Makernotes/ricoh.php b/includes/jpeg_metadata_tk/Makernotes/ricoh.php new file mode 100644 index 0000000..f858bbc --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/ricoh.php @@ -0,0 +1,415 @@ +<?php + +/****************************************************************************** +* +* Filename: ricoh.php +* +* Description: Ricoh Makernote Parser +* Provides functions to decode an Ricoh EXIF makernote and to interpret +* the resulting array into html. +* +* Ricoh Makernote Format: +* +* Type 1 - Text Makernote +* +* Field Size Description +* ---------------------------------------------------------------- +* Text Variable Text, beginning with "Rv" or "Rev" +* --------------------------------------------------------------- +* +* +* Type 2 - Empty Makernote +* +* Field Size Description +* ---------------------------------------------------------------- +* Blank Variable Blank field filled with 0x00 characters +* ---------------------------------------------------------------* +* +* +* Type 3 - IFD Makernote +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 5 Bytes "Ricoh" or "RICOH" +* Unknown 1 Byte Unknown field +* Zeros 2 Bytes Two 0x00 characters +* IFD Data Variable Standard IFD Data using Ricoh Tags +* with Motorola byte alignment +* --------------------------------------------------------------- +* +* Within Makernote Type 3, Tag 0x2001 is the Ricoh Camera Info Sub-IFD +* It has the following format: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 19 Bytes "[Ricoh Camera Info]" +* Unknown 1 Byte Unknown field +* IFD Data Variable NON-Standard IFD Data using Ricoh +* Sub-IFD Tags with Motorola byte alignment +* and has No final Next-IFD pointer +* --------------------------------------------------------------- +* +* +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Ricoh_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Ricoh_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Ricoh_Makernote_Html"; + + + + +/****************************************************************************** +* +* Function: get_Ricoh_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Ricoh_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + // Check if the Make Field contains the word Ricoh + if ( stristr( $Make_Field, "Ricoh" ) === FALSE ) + { + // Ricoh not in the Maker field - abort + return FALSE; + } + + + // Check if the Text Makernote header exists at the start of the Makernote + if ( ( substr( $Makernote_Tag['Data'], 0, 2 ) === "Rv" ) || + ( substr( $Makernote_Tag['Data'], 0, 3 ) === "Rev" ) ) + { + // This is a text makernote - Label it as such + $Makernote_Tag['Makernote Type'] = "Ricoh Text"; + $Makernote_Tag['Makernote Tags'] = "None"; + $Makernote_Tag['Decoded'] = TRUE; + + // Return the new Makernote tag + return $Makernote_Tag; + + } + // Check if the Empty Makernote header exists at the start of the Makernote + else if ( $Makernote_Tag['Data'] === str_repeat ( "\x00", strlen( $Makernote_Tag['Data'] )) ) + { + // This is an Empty Makernote - Label it as such + $Makernote_Tag['Makernote Type'] = "Ricoh Empty Makernote"; + $Makernote_Tag['Makernote Tags'] = "None"; + $Makernote_Tag['Decoded'] = TRUE; + + // Return the new Makernote tag + return $Makernote_Tag; + + } + // Check if the IFD Makernote header exists at the start of the Makernote + else if ( ( substr( $Makernote_Tag['Data'], 0, 5 ) === "RICOH" ) || + ( substr( $Makernote_Tag['Data'], 0, 5 ) === "Ricoh" ) ) + { + //This is an IFD Makernote + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 ); + + // Ricoh Makernote always uses Motorola Byte Alignment + $Makernote_Tag['ByteAlign'] = "MM"; + + // Read the IFD(s) into an array + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Ricoh" ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Ricoh"; + $Makernote_Tag['Makernote Tags'] = "Ricoh"; + + // Ricoh Makernotes can have a tag 0x2001 which is a Sub-IFD + // Check if the tag exists + if ( ( $Makernote_Tag['Decoded Data'][0] !== FALSE ) && + ( array_key_exists( 0x2001, $Makernote_Tag['Decoded Data'][0] ) ) ) + { + // Ricoh Sub-IFD tag exists - Process it + + // Grab the Sub-IFD tag for easier processing + $SubIFD_Tag = &$Makernote_Tag['Decoded Data'][0][0x2001]; + + // Check if the Sub-IFD starts with the correct header + if ( substr( $SubIFD_Tag['Data'], 0, 19 ) === "[Ricoh Camera Info]" ) + { + // Correct Header found + + // Seek to the start of the Sub-IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $SubIFD_Tag['Offset'] + 20 ); + + // Ricoh Makernote Sub-IFD always uses Motorola Byte Alignment + $SubIFD_Tag['ByteAlign'] = "MM"; + + + // Read the IFD(s) into an array + $SubIFD_Tag['Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $SubIFD_Tag['ByteAlign'], "RicohSubIFD", False, False ); + + // Save some information into the Tag element to aid interpretation + $SubIFD_Tag['Decoded'] = TRUE; + $SubIFD_Tag['Makernote Type'] = "Ricoh"; + $SubIFD_Tag['Makernote Tags'] = "RicohSubIFD"; + + // Change the tag type to a Sub-IFD so it is handled automatically for interpretation + $SubIFD_Tag['Type'] = "SubIFD"; + $SubIFD_Tag['Tags Name'] = "RicohSubIFD"; + + } + else + { + // Couldn't find header of Sub-IFD - Probably corrupt + $SubIFD_Tag['Type'] = "String"; + $SubIFD_Tag['Text Value'] = "Corrupted Ricoh Sub IFD"; + } + + } + + // Return the new makernote tag + return $Makernote_Tag; + } + else + { + // Unrecognised header for makernote - abort + return FALSE; + } + + // Shouldn't get here + return False; +} + +/****************************************************************************** +* End of Function: get_Ricoh_Makernote +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: get_Ricoh_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Ricoh_Makernote_Html( $Makernote_tag, $filename ) +{ + + // Check if this makernote is Ricoh IFD type + if ( $Makernote_tag['Makernote Type'] == "Ricoh" ) + { + // This is a Ricoh IFD makernote - interpret it + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + } + // Check if this makernote is Ricoh Text type + else if ( $Makernote_tag['Makernote Type'] == "Ricoh Text" ) + { + // This is a Ricoh text makernote + // Construct the start of enclosing html for the text + $output_str = "<table class=\"EXIF_Table\"border=1><tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Value_Cell\">"; + + // Replace the semicolon dividers with line break html tags + $output_str .= str_replace ( ";", "<BR>\n", $Makernote_tag['Data'] ); + + // Close the html + $output_str .= "</td></tr></table>"; + + // Return the html + return $output_str; + } + // Check if this makernote is a Ricoh Empty makernote + else if ( $Makernote_tag['Makernote Type'] == "Ricoh Empty Makernote" ) + { + // Do Nothing + return ""; + } + else + { + // Don't recognise the Makernote type - not a Ricoh makernote + return FALSE; + } + + // shouldn't get here + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Ricoh_Makernote_Html +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: get_Ricoh_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Ricoh_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Ricoh_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + + // Check that this tag uses the Ricoh tags, otherwise it can't be decoded here + if ( $Tag_Definitions_Name == "Ricoh" ) + { + + // Process the tag acording to it's tag number, to produce a text value + if ( $Exif_Tag['Tag Number'] == 0x0002 ) // Version tag + { + $tmp = implode ( "\x00", $Exif_Tag['Data']); + return "\"" .HTML_UTF8_Escape( $tmp ) . "\" (" . bin2hex( $tmp ) . " hex)"; + } + + } + + // Unknown tag or tag definitions + return FALSE; + +} + +/****************************************************************************** +* End of Function: get_Ricoh_Text_Value +******************************************************************************/ + + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Ricoh +* +* Contents: This global variable provides definitions of the known Ricoh +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Ricoh"] = array( + + +0x0001 => array( 'Name' => "Makernote Data Type", + 'Type' => "String" ), + +0x0002 => array( 'Name' => "Version", + 'Type' => "Special" ), + +0x0e00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +0x2001 => array( 'Name' => "Ricoh Camera Info Makernote Sub-IFD", + 'Type' => "Special" ), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Ricoh +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, RicohSubIFD +* +* Contents: This global variable provides definitions of the known Ricoh +* Camera Info Sub-IFD Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["RicohSubIFD"] = array( + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, RicohSubIFD +******************************************************************************/ + + + + + + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Makernotes/sony.php b/includes/jpeg_metadata_tk/Makernotes/sony.php new file mode 100644 index 0000000..2690d81 --- /dev/null +++ b/includes/jpeg_metadata_tk/Makernotes/sony.php @@ -0,0 +1,244 @@ +<?php + +/****************************************************************************** +* +* Filename: sony.php +* +* Description: Sony Makernote Parser +* Provides functions to decode an Sony EXIF makernote and to interpret +* the resulting array into html. +* +* Sony Makernote Format: +* +* Field Size Description +* ---------------------------------------------------------------- +* Header 12 Bytes "SONY CAM \x00\x00\x00" +* or +* "SONY DSC \x00\x00\x00" +* IFD Data Variable NON-Standard IFD Data using Sony Tags +* IFD has no Next-IFD pointer at end of IFD, +* ---------------------------------------------------------------- +* +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* This file may be used freely for non-commercial purposes.For +* commercial uses please contact the author: evan@ozhiker.com +* +******************************************************************************/ + + + + +// Add the parser and interpreter functions to the list of Makernote parsers and interpreters. + +$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Sony_Makernote"; +$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Sony_Text_Value"; +$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Sony_Makernote_Html"; + + + + + +/****************************************************************************** +* +* Function: get_Sony_Makernote +* +* Description: Decodes the Makernote tag and returns the new tag with the decoded +* information attached. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* EXIF_Array - the entire EXIF array containing the +* makernote, as returned from get_EXIF_JPEG, in +* case more information is required for decoding +* filehnd - an open file handle for the file containing the +* makernote - does not have to be positioned at the +* start of the makernote +* Make_Field - The contents of the EXIF Make field, to aid +* determining whether this script can decode +* the makernote +* +* +* Returns: Makernote_Tag - the Makernote_Tag from the parameters, but +* modified to contain the decoded information +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Sony_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field ) +{ + + // Check if the Make Field contains the word Sony + if ( stristr( $Make_Field, "Sony" ) === FALSE ) + { + // This isn't a Sony makernote + return FALSE; + } + + // Check if the header exists at the start of the Makernote + if ( ( substr( $Makernote_Tag['Data'], 0, 12 ) != "SONY CAM \x00\x00\x00" ) && + ( substr( $Makernote_Tag['Data'], 0, 12 ) != "SONY DSC \x00\x00\x00" ) ) + { + // This isn't a Sony Makernote, abort + return FALSE ; + } + + // Seek to the start of the IFD + fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 12 ); + + // Read the IFD(s) into an array + + + $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Sony", FALSE, FALSE ); + + // Save some information into the Tag element to aid interpretation + $Makernote_Tag['Decoded'] = TRUE; + $Makernote_Tag['Makernote Type'] = "Sony"; + $Makernote_Tag['Makernote Tags'] = "sony"; + + + // Return the new tag + return $Makernote_Tag; + + +} + +/****************************************************************************** +* End of Function: get_Sony_Makernote +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: get_Sony_Text_Value +* +* Description: Provides a text value for any tag marked as special for makernotes +* that this script can decode. Returns false if this is not a makernote +* that can be processed with this script +* +* Parameters: Exif_Tag - the element of an the Makernote array containing the +* tag in question, as returned from get_Sony_Makernote +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* +* Returns: output - the text value for the tag +* FALSE - If this script could not decode the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Sony_Text_Value( $Exif_Tag, $Tag_Definitions_Name ) +{ + // Check that this tag uses the Sony tags, otherwise it can't be decoded here + if ( $Tag_Definitions_Name == "Sony" ) + { + // No Special Tags yet + return FALSE; + } + + return FALSE; +} + +/****************************************************************************** +* End of Function: get_Sony_Text_Value +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: get_Sony_Makernote_Html +* +* Description: Attempts to interpret a makernote into html. Returns false if +* it is not a makernote that can be processed with this script +* +* Parameters: Makernote_Tag - the element of an EXIF array containing the +* makernote, as returned from get_EXIF_JPEG +* filename - the name of the JPEG file being processed ( used +* by scripts which display embedded thumbnails) +* +* +* Returns: output - the html representing the makernote +* FALSE - If this script could not interpret the makernote, or if +* an error occured in decoding +* +******************************************************************************/ + +function get_Sony_Makernote_Html( $Makernote_tag, $filename ) +{ + + // Check that this tag uses the Sony tags, otherwise it can't be interpreted here + if ( $Makernote_tag['Makernote Type'] != "Sony" ) + { + // Not Sony tags - can't interpret with this function + return FALSE; + } + + // Interpret the IFD and return the HTML + return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename ); + +} + +/****************************************************************************** +* End of Function: get_Sony_Makernote_Html +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* Global Variable: IFD_Tag_Definitions, Sony +* +* Contents: This global variable provides definitions of the known Sony +* Makernote tags, indexed by their tag number. +* +******************************************************************************/ + +$GLOBALS[ "IFD_Tag_Definitions" ]["Sony"] = array( + +0xE00 => array( 'Name' => "Print Image Matching Info", + 'Type' => "PIM" ), + +); + +/****************************************************************************** +* End of Global Variable: IFD_Tag_Definitions, Sony +******************************************************************************/ + + + + + + + + + + + +?> diff --git a/includes/jpeg_metadata_tk/PIM.php b/includes/jpeg_metadata_tk/PIM.php new file mode 100644 index 0000000..70be02d --- /dev/null +++ b/includes/jpeg_metadata_tk/PIM.php @@ -0,0 +1,279 @@ +<?php + +/****************************************************************************** +* +* Filename: PIM.php +* +* Description: Provides functions for reading, writing and interpreting a +* Print Image Matching information data block. +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + + +include_once "EXIF.php"; + +// TODO Find out definitions of Print Image Matching Info tags + + +/****************************************************************************** +* +* Function: Decode_PIM +* +* Description: Decodes the contents of a EXIF tag containing Print Image +* Matching information, and returns the contents as an array +* +* Parameters: tag - An EXIF tag containing Print Image Matching information +* as from get_EXIF_JPEG +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* Returns: newtag - The EXIF tag, modified with the data field containing +* an array of the PIM contents +* +******************************************************************************/ + +function Decode_PIM( $tag, $Tag_Definitions_Name ) +{ + + // Create a new EXIF tag for the output + $newtag = $tag; + + // Check that this tag is for Print Image Matching Info + if ( $tag['Type'] == "PIM" ) + { + + // Check that the data starts with PrintIM + if ( substr( $tag['Data'], 0, 8 ) == "PrintIM\x00" ) + { + + // Find the end of the version string + if ( ( $ver_pos = strpos ( $tag['Data'], "\0", 8 ) ) == -1 ) + { + // couldn't find the start of the version string + return $newtag; + } + + // Create an array to receive the Data + $newtag['Data'] = array( ); + + // Extract the PrintIM version + $newtag['Data']['Version'] = substr( $tag['Data'], 8, $ver_pos - 8 ); + // Skip the position over the version + $count_pos = $ver_pos+2; + + // Extract the count of tags - 2 bytes + $PI_tag_count = get_IFD_Data_Type( substr($tag['Data'], $count_pos, 2) , 3, $tag['Byte Align'] ); + + // Panasonic have put an extra Null after the Version, which + // causes the tag count to be wrong - + // check if it is zero - i.e. possibly wrong + if ( ( $PI_tag_count == 0 ) ) + { + // Tag count is zero - try moving the position by one, + // then re-extracting the count + $count_pos++; + $PI_tag_count = get_IFD_Data_Type( substr($tag['Data'], $count_pos, 2) , 3, $tag['Byte Align'] ); + } + + // Extract the data part of the PrintIM block + $data_part = substr($tag['Data'], $count_pos+2); + + // Cycle through each tag + for ( $a = 0; $a < $PI_tag_count; $a++ ) + { + // Read the tag number - 2 bytes + $PI_tag = get_IFD_Data_Type( substr($data_part, $a*6, 2) , 3, $tag['Byte Align'] ); + + // Read the tag data - 4 bytes + $newtag['Data'][ ] = array( 'Tag Number' => $PI_tag, 'Data' => substr($data_part, $a*6+2, 4) , 'Decoded' => False ); + } + } + + } + + // Return the updated tag + return $newtag; + +} + +/****************************************************************************** +* End of Function: Decode_PIM +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: Encode_PIM +* +* Description: Encodes the contents of a EXIF tag containing Print Image +* Matching information, and returns the contents as a packed binary string +* +* Parameters: tag - An EXIF tag containing Print Image Matching information +* as from get_EXIF_JPEG +* Byte_Align - the Byte alignment to use - "MM" or "II" +* +* Returns: packed_data - The packed binary string representing the PIM data +* +******************************************************************************/ + +function Encode_PIM( $tag, $Byte_Align) +{ + + // Create a string to receive the packed data + $packed_data = ""; + + // Check that this tag is for Print Image Matching Info + if ( $tag['Type'] == "PIM" ) + { + // Check that the tag has been decoded - otherwise we don't need to do anything + if ( ( is_array( $tag['Data'] ) ) && + ( count ( $tag['Data'] ) > 0 ) ) + { + // Add the header to the packed data + $packed_data .= "PrintIM\x00"; + + // Add the version to the packed data + $packed_data .= $tag['Data']['Version'] . "\x00"; + + // Create a string to receive the tag data + $tag_data_str = ""; + + // Cycle through each tag + $tag_count = 0; + foreach( $tag['Data'] as $key => $curr_tag ) + { + // Make sure this is a tag and not supplementary info + if ( is_numeric( $key ) ) + { + // Count how many tags are created + $tag_count++; + + // Add the tag number to the packed tag data + $tag_data_str .= put_IFD_Data_Type( $curr_tag['Tag Number'], 3, $Byte_Align ); + + // Add the tag data to the packed tag data + $tag_data_str .= $curr_tag['Data']; + } + } + + // Add the tag count to the packed data + $packed_data .= put_IFD_Data_Type( $tag_count, 3, $Byte_Align ); + + // Add the packed tag data to the packed data + $packed_data .= $tag_data_str; + } + } + + // Return the resulting packed data + return $packed_data; + +} + +/****************************************************************************** +* End of Function: Encode_PIM +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* +* Function: get_PIM_Text_Value +* +* Description: Interprets the contents of a EXIF tag containing Print Image +* Matching information, and returns content as as a text string +* +* Parameters: tag - An EXIF tag containing Print Image Matching information +* as from get_EXIF_JPEG +* Tag_Definitions_Name - The name of the Tag Definitions group +* within the global array IFD_Tag_Definitions +* +* Returns: output_str - The text string representing the PIM info +* +******************************************************************************/ + +function get_PIM_Text_Value( $Tag, $Tag_Definitions_Name ) +{ + + // Create a string to receive the output + $output_str = ""; + + // Check if the PIM tag has been decoded + if ( ( is_array( $Tag['Data'] ) ) && + ( count ( $Tag['Data'] ) > 0 ) ) + { + // The tag has been decoded + + // Add the Version to the output + $output_str = "Version: " . $Tag['Data']['Version'] . "\n"; + + // Check if the user wants to hide unknown tags + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE ) + { + // The user wants to see unknown tags + // Cycle through each tag + foreach ( $Tag['Data'] as $PIM_tag_Key => $PIM_tag ) + { + // Check that the tag is not the version array element + if ( $PIM_tag_Key !== 'Version' ) + { + // Add the tag to the output + $output_str .= "Unknown Tag " . $PIM_tag['Tag Number'] . ": (" . strlen( $PIM_tag['Data'] ) . " bytes of data)\n"; + } + } + } + } + + // Return the output text + return $output_str; +} + +/****************************************************************************** +* End of Function: get_PIM_Text_Value +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Photoshop_File_Info.php b/includes/jpeg_metadata_tk/Photoshop_File_Info.php new file mode 100644 index 0000000..1da7a8c --- /dev/null +++ b/includes/jpeg_metadata_tk/Photoshop_File_Info.php @@ -0,0 +1,2498 @@ +<?php + +/****************************************************************************** +* +* Filename: Photoshop_File_Info.php +* +* Description: Provides functions that mimic the way Photoshop reads and writes +* metadata in it's 'File Info' dialog +* +* Author: Evan Hunter +* +* Date: 11/11/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.11 +* Changes: 1.10 -> 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<br>"; + 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<br>"; + 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 +******************************************************************************/ + + + + + +?> diff --git a/includes/jpeg_metadata_tk/Photoshop_IRB.php b/includes/jpeg_metadata_tk/Photoshop_IRB.php new file mode 100644 index 0000000..fa09b49 --- /dev/null +++ b/includes/jpeg_metadata_tk/Photoshop_IRB.php @@ -0,0 +1,1514 @@ +<? + +/****************************************************************************** +* +* Filename: Photoshop_IRB.php +* +* Description: Provides functions for reading and writing information to/from +* the 'App 13' Photoshop Information Resource Block segment of +* JPEG format files +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* Changes: 1.00 -> 1.02 : changed get_Photoshop_IRB to work with corrupted +* resource names which Photoshop can still read +* 1.02 -> 1.03 : Fixed put_Photoshop_IRB to output "Photoshop 3.0\x00" +* string with every APP13 segment, not just the first one +* 1.03 -> 1.10 : changed get_Photoshop_IRB to fix processing of embedded resource names, +* after discovering that Photoshop does not process +* resource names according to the standard : +* "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000" +* This is an update to the change 1.00 -> 1.02, which was not fully correct +* changed put_Photoshop_IRB to fix the writing of embedded resource name, +* to avoid creating blank resources, and to fix a problem +* causing the IRB block to be incorrectly positioned if no APP segments existed. +* changed get_Photoshop_IPTC to initialise the output array correctly. +* 1.10 -> 1.11 : Moved code out of get_Photoshop_IRB into new function unpack_Photoshop_IRB_Data +* to allow reading of IRB blocks embedded within EXIF (for TIFF Files) +* Moved code out of put_Photoshop_IRB into new function pack_Photoshop_IRB_Data +* to allow writing of IRB blocks embedded within EXIF (for TIFF Files) +* Enabled the usage of $GLOBALS['HIDE_UNKNOWN_TAGS'] to hide unknown resources +* changed Interpret_IRB_to_HTML to allow thumbnail links to work when +* toolkit is portable across directories +* +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + +// Change: as of version 1.11 - added to ensure the HIDE_UNKNOWN_TAGS variable is set even if EXIF.php is not included +if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) ) $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE; + +include_once 'IPTC.php'; +include_once 'Unicode.php'; + + + +// TODO: Many Photoshop IRB resources not interpeted +// TODO: Obtain a copy of the Photoshop CS File Format Specification +// TODO: Find out what Photoshop IRB resources 1061, 1062 & 1064 are +// TODO: Test get_Photoshop_IRB and put_Photoshop_IRB with multiple APP13 segments + +/****************************************************************************** +* +* Function: get_Photoshop_IRB +* +* Description: Retrieves the Photoshop Information Resource Block (IRB) information +* from an App13 JPEG segment and returns it as an array. This may +* include IPTC-NAA IIM Information. Uses information +* supplied by the get_jpeg_header_data function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: IRBdata - The array of Photoshop IRB records +* FALSE - if an APP 13 Photoshop IRB segment could not be found, +* or if an error occured +* +******************************************************************************/ + +function get_Photoshop_IRB( $jpeg_header_data ) +{ + // Photoshop Image Resource blocks can span several JPEG APP13 segments, so we need to join them up if there are more than one + $joined_IRB = ""; + + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP13 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP13" ) == 0 ) + { + // And if it has the photoshop label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "Photoshop 3.0\x00", 14) == 0 ) + { + // join it to the other previous IRB data + $joined_IRB .= substr ( $jpeg_header_data[$i]['SegData'], 14 ); + } + } + } + + // If there was some Photoshop IRB information found, + if ( $joined_IRB != "" ) + { + // Found a Photoshop Image Resource Block - extract it. + // Change: Moved code into unpack_Photoshop_IRB_Data to allow TIFF reading as of 1.11 + return unpack_Photoshop_IRB_Data( $joined_IRB ); + + } + else + { + // No Photoshop IRB found + return FALSE; + } + +} + +/****************************************************************************** +* End of Function: get_Photoshop_IRB +******************************************************************************/ + + + + + + + + + + +/****************************************************************************** +* +* Function: put_Photoshop_IRB +* +* Description: Adds or modifies the Photoshop Information Resource Block (IRB) +* information from an App13 JPEG segment. If a Photoshop IRB already +* exists, it is replaced, otherwise a new one is inserted, using the +* supplied data. Uses information supplied by the get_jpeg_header_data +* function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* new_IRB_data - an array of the data to be stored in the Photoshop +* IRB segment. Should be in the same format as received +* from get_Photoshop_IRB +* +* Returns: jpeg_header_data - the JPEG header data array with the +* Photoshop IRB added. +* FALSE - if an error occured +* +******************************************************************************/ + +function put_Photoshop_IRB( $jpeg_header_data, $new_IRB_data ) +{ + // Delete all existing Photoshop IRB blocks - the new one will replace them + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ) ; $i++ ) + { + // If we find an APP13 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP13" ) == 0 ) + { + // And if it has the photoshop label, + if( strncmp ( $jpeg_header_data[$i]['SegData'], "Photoshop 3.0\x00", 14) == 0 ) + { + // Delete the block information - it needs to be rebuilt + array_splice( $jpeg_header_data, $i, 1 ); + } + } + } + + + // Now we have deleted the pre-existing blocks + + + // Retrieve the Packed Photoshop IRB Data + // Change: Moved code into pack_Photoshop_IRB_Data to allow TIFF writing as of 1.11 + $packed_IRB_data = pack_Photoshop_IRB_Data( $new_IRB_data ); + + // Change : This section changed to fix incorrect positioning of IRB segment, as of revision 1.10 + // when there are no APP segments present + + //Cycle through the header segments in reverse order (to find where to put the APP13 block - after any APP0 to APP12 blocks) + $i = count( $jpeg_header_data ) - 1; + while (( $i >= 0 ) && ( ( $jpeg_header_data[$i]['SegType'] > 0xED ) || ( $jpeg_header_data[$i]['SegType'] < 0xE0 ) ) ) + { + $i--; + } + + + + // Cycle through the packed output data until it's size is less than 32000 bytes, outputting each 32000 byte block to an APP13 segment + while ( strlen( $packed_IRB_data ) > 32000 ) + { + // Change: Fixed put_Photoshop_IRB to output "Photoshop 3.0\x00" string with every APP13 segment, not just the first one, as of 1.03 + + // Write a 32000 byte APP13 segment + array_splice($jpeg_header_data, $i +1 , 0, array( "SegType" => 0xED, + "SegName" => "APP13", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xED ], + "SegData" => "Photoshop 3.0\x00" . substr( $packed_IRB_data,0,32000) ) ); + + // Delete the 32000 bytes from the packed output data, that were just output + $packed_IRB_data = substr_replace($packed_IRB_data, '', 0, 32000); + $i++; + } + + // Write the last block of packed output data to an APP13 segment - Note array_splice doesn't work with multidimensional arrays, hence inserting a blank string + array_splice($jpeg_header_data, $i + 1 , 0, "" ); + $jpeg_header_data[$i + 1] = array( "SegType" => 0xED, + "SegName" => "APP13", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xED ], + "SegData" => "Photoshop 3.0\x00" . $packed_IRB_data ); + + return $jpeg_header_data; +} + +/****************************************************************************** +* End of Function: put_Photoshop_IRB +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: get_Photoshop_IPTC +* +* Description: Retrieves IPTC-NAA IIM information from within a Photoshop +* IRB (if it is present) and returns it in an array. Uses +* information supplied by the get_jpeg_header_data function +* +* Parameters: Photoshop_IRB_data - an array of Photoshop IRB records, as +* returned from get_Photoshop_IRB +* +* Returns: IPTC_Data_Out - The array of IPTC-NAA IIM records +* FALSE - if an IPTC-NAA IIM record could not be found, or if +* an error occured +* +******************************************************************************/ + +function get_Photoshop_IPTC( $Photoshop_IRB_data ) +{ + + // Change: Initialise array correctly, as of revision 1.10 + $IPTC_Data_Out = array(); + + //Cycle through the Photoshop 8BIM records looking for the IPTC-NAA record + for( $i = 0; $i < count( $Photoshop_IRB_data ); $i++ ) + { + // Check if each record is a IPTC record (which has id 0x0404) + if ( $Photoshop_IRB_data[$i]['ResID'] == 0x0404 ) + { + // We've found an IPTC block - Decode it + $IPTC_Data_Out = get_IPTC( $Photoshop_IRB_data[$i]['ResData'] ); + } + } + + // If there was no records put into the output array, + if ( count( $IPTC_Data_Out ) == 0 ) + { + // Then return false + return FALSE; + } + else + { + // Otherwise return the array + return $IPTC_Data_Out; + } + +} +/****************************************************************************** +* End of Function: get_Photoshop_IPTC +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: put_Photoshop_IPTC +* +* Description: Inserts a new IPTC-NAA IIM resource into a Photoshop +* IRB, or replaces an the existing resource if one is present. +* Uses information supplied by the get_Photoshop_IRB function +* +* Parameters: Photoshop_IRB_data - an array of Photoshop IRB records, as +* returned from get_Photoshop_IRB, into +* which the IPTC-NAA IIM record will be inserted +* new_IPTC_block - an array of IPTC-NAA records in the same format +* as those returned by get_Photoshop_IPTC +* +* Returns: Photoshop_IRB_data - The Photoshop IRB array with the +* IPTC-NAA IIM resource inserted +* +******************************************************************************/ + +function put_Photoshop_IPTC( $Photoshop_IRB_data, $new_IPTC_block ) +{ + $iptc_block_pos = -1; + + //Cycle through the 8BIM records looking for the IPTC-NAA record + for( $i = 0; $i < count( $Photoshop_IRB_data ); $i++ ) + { + // Check if each record is a IPTC record (which has id 0x0404) + if ( $Photoshop_IRB_data[$i]['ResID'] == 0x0404 ) + { + // We've found an IPTC block - save the position + $iptc_block_pos = $i; + } + } + + // If no IPTC block was found, create a new one + if ( $iptc_block_pos == -1 ) + { + // New block position will be at the end of the array + $iptc_block_pos = count( $Photoshop_IRB_data ); + } + + + // Write the new IRB resource to the Photoshop IRB array with no data + $Photoshop_IRB_data[$iptc_block_pos] = array( "ResID" => 0x0404, + "ResName" => $GLOBALS['Photoshop_ID_Names'][ 0x0404 ], + "ResDesc" => $GLOBALS[ "Photoshop_ID_Descriptions" ][ 0x0404 ], + "ResEmbeddedName" => "\x00\x00", + "ResData" => put_IPTC( $new_IPTC_block ) ); + + + // Return the modified IRB + return $Photoshop_IRB_data; +} + +/****************************************************************************** +* End of Function: put_Photoshop_IPTC +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Function: Interpret_IRB_to_HTML +* +* Description: Generates html showing the information contained in a Photoshop +* IRB data array, as retrieved with get_Photoshop_IRB, including +* any IPTC-NAA IIM records found. +* +* Please note that the following resource numbers are not currently +* decoded: ( Many of these do not apply to JPEG images) +* 0x03E9, 0x03EE, 0x03EF, 0x03F0, 0x03F1, 0x03F2, 0x03F6, 0x03F9, +* 0x03FA, 0x03FB, 0x03FD, 0x03FE, 0x0400, 0x0401, 0x0402, 0x0405, +* 0x040E, 0x040F, 0x0410, 0x0412, 0x0413, 0x0415, 0x0416, 0x0417, +* 0x041B, 0x041C, 0x041D, 0x0BB7 +* +* ( Also these Obsolete resource numbers) +* 0x03E8, 0x03EB, 0x03FC, 0x03FF, 0x0403 +* +* +* Parameters: IRB_array - a Photoshop IRB data array as from get_Photoshop_IRB +* filename - the name of the JPEG file being processed ( used +* by the script which displays the Photoshop thumbnail) +* +* +* Returns: output_str - the HTML string +* +******************************************************************************/ + +function Interpret_IRB_to_HTML( $IRB_array, $filename ) +{ + // Create a string to receive the HTML + $output_str = ""; + + // Check if the Photoshop IRB array is valid + if ( $IRB_array !== FALSE ) + { + + // Create another string to receive secondary HTML to be appended at the end + $secondary_output_str = ""; + + // Add the Heading to the HTML + $output_str .= "<h2 class=\"Photoshop_Main_Heading\">Contains Photoshop Information Resource Block (IRB)</h2>"; + + // Add Table to the HTML + $output_str .= "<table class=\"Photoshop_Table\" border=1>\n"; + + // Cycle through each of the Photoshop IRB records, creating HTML for each + foreach( $IRB_array as $IRB_Resource ) + { + // Check if the entry is a known Photoshop IRB resource + + // Get the Name of the Resource + if ( array_key_exists( $IRB_Resource['ResID'], $GLOBALS[ "Photoshop_ID_Names" ] ) ) + { + $Resource_Name = $GLOBALS['Photoshop_ID_Names'][ $IRB_Resource['ResID'] ]; + } + else + { + // Change: Added check for $GLOBALS['HIDE_UNKNOWN_TAGS'] to allow hiding of unknown resources as of 1.11 + if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == TRUE ) + { + continue; + } + else + { + // Unknown Resource - Make appropriate name + $Resource_Name = "Unknown Resource (". $IRB_Resource['ResID'] .")"; + } + } + + // Add HTML for the resource as appropriate + switch ( $IRB_Resource['ResID'] ) + { + + case 0x0404 : // IPTC-NAA IIM Record + $secondary_output_str .= Interpret_IPTC_to_HTML( get_IPTC( $IRB_Resource['ResData'] ) ); + break; + + case 0x040B : // URL + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><a href=\"" . $IRB_Resource['ResData'] . "\">" . htmlentities( $IRB_Resource['ResData'] ) ."</a></td></tr>\n"; + break; + + case 0x040A : // Copyright Marked + if ( hexdec( bin2hex( $IRB_Resource['ResData'] ) ) == 1 ) + { + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Image is Copyrighted Material</pre></td></tr>\n"; + } + else + { + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Image is Not Copyrighted Material</pre></td></tr>\n"; + } + break; + + case 0x040D : // Global Lighting Angle + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Global lighting angle for effects layer = " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . " degrees</pre></td></tr>\n"; + break; + + case 0x0419 : // Global Altitude + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Global Altitude = " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . "</pre></td></tr>\n"; + break; + + case 0x0421 : // Version Info + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n"; + $output_str .= "Version = " . hexdec( bin2hex( substr( $IRB_Resource['ResData'], 0, 4 ) ) ) . "\n"; + $output_str .= "Has Real Merged Data = " . ord( $IRB_Resource['ResData']{4} ) . "\n"; + $writer_size = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 5, 4 ) ) ) * 2; + + $output_str .= "Writer Name = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 9, $writer_size ), TRUE ) . "\n"; + $reader_size = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 9 + $writer_size , 4 ) ) ) * 2; + $output_str .= "Reader Name = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 13 + $writer_size, $reader_size ), TRUE ) . "\n"; + $output_str .= "File Version = " . hexdec( bin2hex( substr( $IRB_Resource['ResData'], 13 + $writer_size + $reader_size, 4 ) ) ) . "\n"; + $output_str .= "</pre></td></tr>\n"; + break; + + case 0x0411 : // ICC Untagged + if ( $IRB_Resource['ResData'] == "\x01" ) + { + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Intentionally untagged - any assumed ICC profile handling disabled</pre></td></tr>\n"; + } + else + { + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Unknown value (0x" .bin2hex( $IRB_Resource['ResData'] ). ")</pre></td></tr>\n"; + } + break; + + case 0x041A : // Slices + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">"; + + // Unpack the first 24 bytes + $Slices_Info = unpack("NVersion/NBound_top/NBound_left/NBound_bottom/NBound_right/NStringlen", $IRB_Resource['ResData'] ); + $output_str .= "Version = " . $Slices_Info['Version'] . "<br>\n"; + $output_str .= "Bounding Rectangle = Top:" . $Slices_Info['Bound_top'] . ", Left:" . $Slices_Info['Bound_left'] . ", Bottom:" . $Slices_Info['Bound_bottom'] . ", Right:" . $Slices_Info['Bound_right'] . " (Pixels)<br>\n"; + $Slicepos = 24; + + // Extract a Unicode String + $output_str .= "Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 24, $Slices_Info['Stringlen']*2), TRUE ) . "'<br>\n"; + $Slicepos += $Slices_Info['Stringlen'] * 2; + + // Unpack the number of Slices + $Num_Slices = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); + $output_str .= "Number of Slices = " . $Num_Slices . "\n"; + $Slicepos += 4; + + // Cycle through the slices + for( $i = 1; $i <= $Num_Slices; $i++ ) + { + $output_str .= "<br><br>Slice $i:<br>\n"; + + // Unpack the first 16 bytes of the slice + $SliceA = unpack("NID/NGroupID/NOrigin/NStringlen", substr($IRB_Resource['ResData'], $Slicepos ) ); + $Slicepos += 16; + $output_str .= "ID = " . $SliceA['ID'] . "<br>\n"; + $output_str .= "Group ID = " . $SliceA['GroupID'] . "<br>\n"; + $output_str .= "Origin = " . $SliceA['Origin'] . "<br>\n"; + + // Extract a Unicode String + $output_str .= "Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $SliceA['Stringlen']*2), TRUE ) . "'<br>\n"; + $Slicepos += $SliceA['Stringlen'] * 2; + + // Unpack the next 24 bytes of the slice + $SliceB = unpack("NType/NLeftPos/NTopPos/NRightPos/NBottomPos/NURLlen", substr($IRB_Resource['ResData'], $Slicepos ) ); + $Slicepos += 24; + $output_str .= "Type = " . $SliceB['Type'] . "<br>\n"; + $output_str .= "Position = Top:" . $SliceB['TopPos'] . ", Left:" . $SliceB['LeftPos'] . ", Bottom:" . $SliceB['BottomPos'] . ", Right:" . $SliceB['RightPos'] . " (Pixels)<br>\n"; + + // Extract a Unicode String + $output_str .= "URL = <a href='" . substr( $IRB_Resource['ResData'], $Slicepos, $SliceB['URLlen']*2) . "'>" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $SliceB['URLlen']*2), TRUE ) . "</a><br>\n"; + $Slicepos += $SliceB['URLlen'] * 2; + + // Unpack the length of a Unicode String + $Targetlen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); + $Slicepos += 4; + // Extract a Unicode String + $output_str .= "Target = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $Targetlen*2), TRUE ) . "'<br>\n"; + $Slicepos += $Targetlen * 2; + + // Unpack the length of a Unicode String + $Messagelen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); + $Slicepos += 4; + // Extract a Unicode String + $output_str .= "Message = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $Messagelen*2), TRUE ) . "'<br>\n"; + $Slicepos += $Messagelen * 2; + + // Unpack the length of a Unicode String + $AltTaglen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); + $Slicepos += 4; + // Extract a Unicode String + $output_str .= "Alt Tag = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $AltTaglen*2), TRUE ) . "'<br>\n"; + $Slicepos += $AltTaglen * 2; + + // Unpack the HTML flag + if ( ord( $IRB_Resource['ResData']{ $Slicepos } ) === 0x01 ) + { + $output_str .= "Cell Text is HTML<br>\n"; + } + else + { + $output_str .= "Cell Text is NOT HTML<br>\n"; + } + $Slicepos++; + + // Unpack the length of a Unicode String + $CellTextlen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) ); + $Slicepos += 4; + // Extract a Unicode String + $output_str .= "Cell Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $CellTextlen*2), TRUE ) . "'<br>\n"; + $Slicepos += $CellTextlen * 2; + + + // Unpack the last 12 bytes of the slice + $SliceC = unpack("NAlignH/NAlignV/CAlpha/CRed/CGreen/CBlue", substr($IRB_Resource['ResData'], $Slicepos ) ); + $Slicepos += 12; + $output_str .= "Alignment = Horizontal:" . $SliceC['AlignH'] . ", Vertical:" . $SliceC['AlignV'] . "<br>\n"; + $output_str .= "Alpha Colour = " . $SliceC['Alpha'] . "<br>\n"; + $output_str .= "Red = " . $SliceC['Red'] . "<br>\n"; + $output_str .= "Green = " . $SliceC['Green'] . "<br>\n"; + $output_str .= "Blue = " . $SliceC['Blue'] . "\n"; + } + + $output_str .= "</td></tr>\n"; + + break; + + + case 0x0408 : // Grid and Guides information + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">"; + + // Unpack the Grids info + $Grid_Info = unpack("NVersion/NGridCycleH/NGridCycleV/NGuideCount", $IRB_Resource['ResData'] ); + $output_str .= "Version = " . $Grid_Info['Version'] . "<br>\n"; + $output_str .= "Grid Cycle = " . $Grid_Info['GridCycleH']/32 . " Pixel(s) x " . $Grid_Info['GridCycleV']/32 . " Pixel(s)<br>\n"; + $output_str .= "Number of Guides = " . $Grid_Info['GuideCount'] . "\n"; + + // Cycle through the Guides + for( $i = 0; $i < $Grid_Info['GuideCount']; $i++ ) + { + // Unpack the info for this guide + $Guide_Info = unpack("NLocation/CDirection", substr($IRB_Resource['ResData'],16+$i*5,5) ); + $output_str .= "<br>Guide $i : Location = " . $Guide_Info['Location']/32 . " Pixel(s) from edge"; + if ( $Guide_Info['Direction'] === 0 ) + { + $output_str .= ", Vertical\n"; + } + else + { + $output_str .= ", Horizontal\n"; + } + } + break; + $output_str .= "</td></tr>\n"; + + case 0x0406 : // JPEG Quality + $Qual_Info = unpack("nQuality/nFormat/nScans/Cconst", $IRB_Resource['ResData'] ); + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">"; + switch ( $Qual_Info['Quality'] ) + { + case 0xFFFD: + $output_str .= "Quality 1 (Low)<br>\n"; + break; + case 0xFFFE: + $output_str .= "Quality 2 (Low)<br>\n"; + break; + case 0xFFFF: + $output_str .= "Quality 3 (Low)<br>\n"; + break; + case 0x0000: + $output_str .= "Quality 4 (Low)<br>\n"; + break; + case 0x0001: + $output_str .= "Quality 5 (Medium)<br>\n"; + break; + case 0x0002: + $output_str .= "Quality 6 (Medium)<br>\n"; + break; + case 0x0003: + $output_str .= "Quality 7 (Medium)<br>\n"; + break; + case 0x0004: + $output_str .= "Quality 8 (High)<br>\n"; + break; + case 0x0005: + $output_str .= "Quality 9 (High)<br>\n"; + break; + case 0x0006: + $output_str .= "Quality 10 (Maximum)<br>\n"; + break; + case 0x0007: + $output_str .= "Quality 11 (Maximum)<br>\n"; + break; + case 0x0008: + $output_str .= "Quality 12 (Maximum)<br>\n"; + break; + default: + $output_str .= "Unknown Quality (" . $Qual_Info['Quality'] . ")<br>\n"; + break; + } + + switch ( $Qual_Info['Format'] ) + { + case 0x0000: + $output_str .= "Standard Format\n"; + break; + case 0x0001: + $output_str .= "Optimised Format\n"; + break; + case 0x0101: + $output_str .= "Progressive Format<br>\n"; + break; + default: + $output_str .= "Unknown Format (" . $Qual_Info['Format'] .")\n"; + break; + } + if ( $Qual_Info['Format'] == 0x0101 ) + { + switch ( $Qual_Info['Scans'] ) + { + case 0x0001: + $output_str .= "3 Scans\n"; + break; + case 0x0002: + $output_str .= "4 Scans\n"; + break; + case 0x0003: + $output_str .= "5 Scans\n"; + break; + default: + $output_str .= "Unknown number of scans (" . $Qual_Info['Scans'] .")\n"; + break; + } + } + $output_str .= "</td></tr>\n"; + break; + + case 0x0409 : // Thumbnail Resource + case 0x040C : // Thumbnail Resource + $thumb_data = unpack("NFormat/NWidth/NHeight/NWidthBytes/NSize/NCompressedSize/nBitsPixel/nPlanes", $IRB_Resource['ResData'] ); + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n"; + $output_str .= "Format = " . (( $thumb_data['Format'] == 1 ) ? "JPEG RGB\n" : "Raw RGB\n"); + $output_str .= "Width = " . $thumb_data['Width'] . "\n"; + $output_str .= "Height = " . $thumb_data['Height'] . "\n"; + $output_str .= "Padded Row Bytes = " . $thumb_data['WidthBytes'] . " bytes\n"; + $output_str .= "Total Size = " . $thumb_data['Size'] . " bytes\n"; + $output_str .= "Compressed Size = " . $thumb_data['CompressedSize'] . " bytes\n"; + $output_str .= "Bits per Pixel = " . $thumb_data['BitsPixel'] . " bits\n"; + $output_str .= "Number of planes = " . $thumb_data['Planes'] . " bytes\n"; + + // Change: as of version 1.11 - Changed to make thumbnail link portable across directories + // Build the path of the thumbnail script and its filename parameter to put in a url + $link_str = get_relative_path( dirname(__FILE__) . "/get_ps_thumb.php" , getcwd ( ) ); + $link_str .= "?filename="; + $link_str .= get_relative_path( $filename, dirname(__FILE__) ); + + // Add thumbnail link to html + $output_str .= "Thumbnail Data:</pre><a class=\"Photoshop_Thumbnail_Link\" href=\"$link_str\"><img class=\"Photoshop_Thumbnail_Link\" src=\"$link_str\"></a>\n"; + + $output_str .= "</td></tr>\n"; + break; + + case 0x0414 : // Document Specific ID's + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>" . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . "</pre></td></tr>\n"; + break; + + case 0x041E : // URL List + $URL_count = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 0, 4 ) ) ); + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">\n"; + $output_str .= "$URL_count URL's in list<br>\n"; + $urlstr = substr( $IRB_Resource['ResData'], 4 ); + // TODO: Check if URL List in Photoshop IRB works + for( $i = 0; $i < $URL_count; $i++ ) + { + $url_data = unpack( "NLong/NID/NURLSize", $urlstr ); + $output_str .= "URL $i info: long = " . $url_data['Long'] .", "; + $output_str .= "ID = " . $url_data['ID'] . ", "; + $urlstr = substr( $urlstr, 12 ); + $url = substr( $urlstr, 0, $url_data['URLSize'] ); + $output_str .= "URL = <a href=\"" . xml_UTF16_clean( $url, TRUE ) . "\">" . HTML_UTF16_Escape( $url, TRUE ) . "</a><br>\n"; + } + $output_str .= "</td></tr>\n"; + break; + case 0x03F4 : // Grayscale and multichannel halftoning information. + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n"; + $output_str .= Interpret_Halftone( $IRB_Resource['ResData'] ); + $output_str .= "</pre></td></tr>\n"; + break; + case 0x03F5 : // Color halftoning information + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n"; + $output_str .= "Cyan Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 0, 18 ) ) . "\n\n"; + $output_str .= "Magenta Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 18, 18 ) ) . "\n\n"; + $output_str .= "Yellow Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 36, 18 ) ) . "\n"; + $output_str .= "Black Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 54, 18 ) ) . "\n"; + $output_str .= "</pre></td></tr>\n"; + break; + + case 0x03F7 : // Grayscale and multichannel transfer function. + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n"; + $output_str .= Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 0, 28 ) ) ; + $output_str .= "</pre></td></tr>\n"; + break; + + case 0x03F8 : // Color transfer functions + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n"; + $output_str .= "Red Transfer Function: \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 0, 28 ) ) . "\n\n"; + $output_str .= "Green Transfer Function: \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 28, 28 ) ) . "\n\n"; + $output_str .= "Blue Transfer Function: \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 56, 28 ) ) . "\n"; + $output_str .= "</pre></td></tr>\n"; + break; + + case 0x03F3 : // Print Flags + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n"; + if ( $IRB_Resource['ResData']{0} == "\x01" ) + { + $output_str .= "Labels Selected\n"; + } + else + { + $output_str .= "Labels Not Selected\n"; + } + if ( $IRB_Resource['ResData']{1} == "\x01" ) + { + $output_str .= "Crop Marks Selected\n"; + } + else + { + $output_str .= "Crop Marks Not Selected\n"; + } + if ( $IRB_Resource['ResData']{2} == "\x01" ) + { + $output_str .= "Color Bars Selected\n"; + } + else + { + $output_str .= "Color Bars Not Selected\n"; + } + if ( $IRB_Resource['ResData']{3} == "\x01" ) + { + $output_str .= "Registration Marks Selected\n"; + } + else + { + $output_str .= "Registration Marks Not Selected\n"; + } + if ( $IRB_Resource['ResData']{4} == "\x01" ) + { + $output_str .= "Negative Selected\n"; + } + else + { + $output_str .= "Negative Not Selected\n"; + } + if ( $IRB_Resource['ResData']{5} == "\x01" ) + { + $output_str .= "Flip Selected\n"; + } + else + { + $output_str .= "Flip Not Selected\n"; + } + if ( $IRB_Resource['ResData']{6} == "\x01" ) + { + $output_str .= "Interpolate Selected\n"; + } + else + { + $output_str .= "Interpolate Not Selected\n"; + } + if ( $IRB_Resource['ResData']{7} == "\x01" ) + { + $output_str .= "Caption Selected"; + } + else + { + $output_str .= "Caption Not Selected"; + } + $output_str .= "</pre></td></tr>\n"; + break; + + case 0x2710 : // Print Flags Information + $PrintFlags = unpack( "nVersion/CCentCrop/Cjunk/NBleedWidth/nBleedWidthScale", $IRB_Resource['ResData'] ); + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n"; + $output_str .= "Version = " . $PrintFlags['Version'] . "\n"; + $output_str .= "Centre Crop Marks = " . $PrintFlags['CentCrop'] . "\n"; + $output_str .= "Bleed Width = " . $PrintFlags['BleedWidth'] . "\n"; + $output_str .= "Bleed Width Scale = " . $PrintFlags['BleedWidthScale']; + $output_str .= "</pre></td></tr>\n"; + break; + + case 0x03ED : // Resolution Info + $ResInfo = unpack( "nhRes_int/nhResdec/nhResUnit/nwidthUnit/nvRes_int/nvResdec/nvResUnit/nheightUnit", $IRB_Resource['ResData'] ); + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n"; + $output_str .= "Horizontal Resolution = " . ($ResInfo['hRes_int'] + $ResInfo['hResdec']/65536) . " pixels per Inch\n"; + $output_str .= "Vertical Resolution = " . ($ResInfo['vRes_int'] + $ResInfo['vResdec']/65536) . " pixels per Inch\n"; + if ( $ResInfo['hResUnit'] == 1 ) + { + $output_str .= "Display units for Horizontal Resolution = Pixels per Inch\n"; + } + elseif ( $ResInfo['hResUnit'] == 2 ) + { + $output_str .= "Display units for Horizontal Resolution = Pixels per Centimetre\n"; + } + else + { + $output_str .= "Display units for Horizontal Resolution = Unknown Value (". $ResInfo['hResUnit'] .")\n"; + } + + if ( $ResInfo['vResUnit'] == 1 ) + { + $output_str .= "Display units for Vertical Resolution = Pixels per Inch\n"; + } + elseif ( $ResInfo['vResUnit'] == 2 ) + { + $output_str .= "Display units for Vertical Resolution = Pixels per Centimetre\n"; + } + else + { + $output_str .= "Display units for Vertical Resolution = Unknown Value (". $ResInfo['vResUnit'] .")\n"; + } + + if ( $ResInfo['widthUnit'] == 1 ) + { + $output_str .= "Display units for Image Width = Inches\n"; + } + elseif ( $ResInfo['widthUnit'] == 2 ) + { + $output_str .= "Display units for Image Width = Centimetres\n"; + } + elseif ( $ResInfo['widthUnit'] == 3 ) + { + $output_str .= "Display units for Image Width = Points\n"; + } + elseif ( $ResInfo['widthUnit'] == 4 ) + { + $output_str .= "Display units for Image Width = Picas\n"; + } + elseif ( $ResInfo['widthUnit'] == 5 ) + { + $output_str .= "Display units for Image Width = Columns\n"; + } + else + { + $output_str .= "Display units for Image Width = Unknown Value (". $ResInfo['widthUnit'] .")\n"; + } + + if ( $ResInfo['heightUnit'] == 1 ) + { + $output_str .= "Display units for Image Height = Inches"; + } + elseif ( $ResInfo['heightUnit'] == 2 ) + { + $output_str .= "Display units for Image Height = Centimetres"; + } + elseif ( $ResInfo['heightUnit'] == 3 ) + { + $output_str .= "Display units for Image Height = Points"; + } + elseif ( $ResInfo['heightUnit'] == 4 ) + { + $output_str .= "Display units for Image Height = Picas"; + } + elseif ( $ResInfo['heightUnit'] == 5 ) + { + $output_str .= "Display units for Image Height = Columns"; + } + else + { + $output_str .= "Display units for Image Height = Unknown Value (". $ResInfo['heightUnit'] .")"; + } + $output_str .= "</pre></td></tr>\n"; + break; + + default : // All other records + $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">RESOURCE DECODING NOT IMPLEMENTED YET<BR>" . strlen( $IRB_Resource['ResData'] ) . " bytes</td></tr>\n"; + + } + + } + + // Add the table end to the HTML + $output_str .= "</table>\n"; + + // Add any secondary output to the HTML + $output_str .= $secondary_output_str; + + } + + // Return the HTML + return $output_str; +} + +/****************************************************************************** +* End of Function: Interpret_IRB_to_HTML +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* INTERNAL FUNCTIONS +* +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: unpack_Photoshop_IRB_Data +* +* Description: Extracts Photoshop Information Resource Block (IRB) information +* from a binary string containing the IRB, as read from a file +* +* Parameters: IRB_Data - The binary string containing the IRB +* +* Returns: IRBdata - The array of Photoshop IRB records +* +******************************************************************************/ + +function unpack_Photoshop_IRB_Data( $IRB_Data ) +{ + $pos = 0; + + // Cycle through the IRB and extract its records - Records are started with 8BIM, so cycle until no more instances of 8BIM can be found + while ( ( $pos < strlen( $IRB_Data ) ) && ( ($pos = strpos( $IRB_Data, "8BIM", $pos) ) !== FALSE ) ) + { + // Skip the position over the 8BIM characters + $pos += 4; + + // Next two characters are the record ID - denoting what type of record it is. + $ID = ord( $IRB_Data{ $pos } ) * 256 + ord( $IRB_Data{ $pos +1 } ); + + // Skip the positionover the two record ID characters + $pos += 2; + + // Next comes a Record Name - usually not used, but it should be a null terminated string, padded with 0x00 to be an even length + $namestartpos = $pos; + + // Change: Fixed processing of embedded resource names, as of revision 1.10 + + // NOTE: Photoshop does not process resource names according to the standard : + // "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000" + // + // The resource name is actually formatted as follows: + // One byte name length, followed by the null terminated ascii name string. + // The field is then padded with a Null character if required, to ensure that the + // total length of the name length and name is even. + + // Name - process it + // Get the length + $namelen = ord ( $IRB_Data{ $namestartpos } ); + + // Total length of name and length info must be even, hence name length must be odd + // Check if the name length is even, + if ( $namelen % 2 == 0 ) + { + // add one to length to make it odd + $namelen ++; + } + // Extract the name + $resembeddedname = trim( substr ( $IRB_Data, $namestartpos+1, $namelen) ); + $pos += $namelen + 1; + + + // Next is a four byte size field indicating the size in bytes of the record's data - MSB first + $datasize = ord( $IRB_Data{ $pos } ) * 16777216 + ord( $IRB_Data{ $pos + 1 } ) * 65536 + + ord( $IRB_Data{ $pos + 2 } ) * 256 + ord( $IRB_Data{ $pos + 3 } ); + $pos += 4; + + // The record is stored padded with 0x00 characters to make the size even, so we need to calculate the stored size + $storedsize = $datasize + ($datasize % 2); + + $resdata = substr ( $IRB_Data, $pos, $datasize ); + + // Get the description for this resource + // Check if this is a Path information Resource, since they have a range of ID's + if ( ( $ID >= 0x07D0 ) && ( $ID <= 0x0BB6 ) ) + { + $ResDesc = "ID Info : Path Information (saved paths)."; + } + else + { + if ( array_key_exists( $ID, $GLOBALS[ "Photoshop_ID_Descriptions" ] ) ) + { + $ResDesc = $GLOBALS[ "Photoshop_ID_Descriptions" ][ $ID ]; + } + else + { + $ResDesc = ""; + } + } + + // Get the Name of the Resource + if ( array_key_exists( $ID, $GLOBALS[ "Photoshop_ID_Names" ] ) ) + { + $ResName = $GLOBALS['Photoshop_ID_Names'][ $ID ]; + } + else + { + $ResName = ""; + } + + + // Store the Resource in the array to be returned + + $IRB_Array[] = array( "ResID" => $ID, + "ResName" => $ResName, + "ResDesc" => $ResDesc, + "ResEmbeddedName" => $resembeddedname, + "ResData" => $resdata ); + + // Jump over the data to the next record + $pos += $storedsize; + } + + // Return the array created + return $IRB_Array; +} + +/****************************************************************************** +* End of Function: unpack_Photoshop_IRB_Data +******************************************************************************/ + + + + + + + + + + + +/****************************************************************************** +* +* Function: pack_Photoshop_IRB_Data +* +* Description: Packs a Photoshop Information Resource Block (IRB) array into it's +* binary form, which can be written to a file +* +* Parameters: IRB_data - an Photoshop IRB array to be converted. Should be in +* the same format as received from get_Photoshop_IRB +* +* Returns: packed_IRB_data - the binary string of packed IRB data +* +******************************************************************************/ + +function pack_Photoshop_IRB_Data( $IRB_data ) +{ + $packed_IRB_data = ""; + + // Cycle through each resource in the IRB, + foreach ($IRB_data as $resource) + { + + // Change: Fix to avoid creating blank resources, as of revision 1.10 + + // Check if there is actually any data for this resource + if( strlen( $resource['ResData'] ) == 0 ) + { + // No data for resource - skip it + continue; + } + + // Append the 8BIM tag, and resource ID to the packed output data + $packed_IRB_data .= pack("a4n", "8BIM", $resource['ResID'] ); + + + // Change: Fixed processing of embedded resource names, as of revision 1.10 + + // NOTE: Photoshop does not process resource names according to the standard : + // "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000" + // + // The resource name is actually formatted as follows: + // One byte name length, followed by the null terminated ascii name string. + // The field is then padded with a Null character if required, to ensure that the + // total length of the name length and name is even. + + // Append Name Size + $packed_IRB_data .= pack( "c", strlen(trim($resource['ResEmbeddedName']))); + + // Append the Resource Name to the packed output data + $packed_IRB_data .= trim($resource['ResEmbeddedName']); + + // If the resource name is even length, then with the addition of + // the size it becomes odd and needs to be padded to an even number + if ( strlen( trim($resource['ResEmbeddedName']) ) % 2 == 0 ) + { + // then it needs to be evened up by appending another null + $packed_IRB_data .= "\x00"; + } + + // Append the resource data size to the packed output data + $packed_IRB_data .= pack("N", strlen( $resource['ResData'] ) ); + + // Append the resource data to the packed output data + $packed_IRB_data .= $resource['ResData']; + + // If the resource data is odd length, + if ( strlen( $resource['ResData'] ) % 2 == 1 ) + { + // then it needs to be evened up by appending another null + $packed_IRB_data .= "\x00"; + } + } + + // Return the packed data string + return $packed_IRB_data; +} + +/****************************************************************************** +* End of Function: pack_Photoshop_IRB_Data +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Internal Function: Interpret_Transfer_Function +* +* Description: Used by Interpret_IRB_to_HTML to interpret Color transfer functions +* for Photoshop IRB resource 0x03F8. Converts the transfer function +* information to a human readable version. +* +* Parameters: Transfer_Function_Binary - a 28 byte Ink curves structure string +* +* Returns: output_str - the text string containing the transfer function +* information +* +******************************************************************************/ + +function Interpret_Transfer_Function( $Transfer_Function_Binary ) +{ + // Unpack the Transfer function information + $Trans_vals = unpack ( "n13Curve/nOverride", $Transfer_Function_Binary ); + + $output_str = "Transfer Function Points: "; + + // Cycle through each of the Transfer function array values + foreach ( $Trans_vals as $Key => $val ) + { + // Check if the value should be negative + if ($val > 32768 ) + { + // Value should be negative - make it so + $val = $val - 65536; + } + // Check that the Override item is not getting in this list, and + // that the value is not -1, which means ignored + if ( ( $Key != "Override" ) && ( $val != -1 ) ) + { + // This is a valid transfer function point, output it + $output_str .= $val/10 . "%, "; + } + } + + // Output the override info + if ( $Trans_vals['Override'] == 0 ) + { + $output_str .= "\nOverride: Let printer supply curve"; + } + else + { + $output_str .= "\nOverride: Override printer’s default transfer curve"; + } + + // Return the result + return $output_str; +} + +/****************************************************************************** +* End of Function: Interpret_Transfer_Function +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Internal Function: Interpret_Halftone +* +* Description: Used by Interpret_IRB_to_HTML to interpret Color halftoning information +* for Photoshop IRB resource 0x03F5. Converts the halftoning info +* to a human readable version. +* +* Parameters: Transfer_Function_Binary - a 18 byte Halftone screen parameter +& structure string +* +* Returns: output_str - the text string containing the transfer function +* information +* +******************************************************************************/ + +function Interpret_Halftone( $Halftone_Binary ) +{ + // Create a string to receive the output + $output_str = ""; + + // Unpack the binary data into an array + $HalftoneInfo = unpack( "nFreqVal_int/nFreqVal_dec/nFreqScale/nAngle_int/nAngle_dec/nShapeCode/NMisc/CAccurate/CDefault", $Halftone_Binary ); + + // Interpret Ink Screen Frequency + $output_str .= "Ink Screen Frequency = " . ($HalftoneInfo['FreqVal_int'] + $HalftoneInfo['FreqVal_dec']/65536) . " lines per Inch\n"; + if ( $HalftoneInfo['FreqScale'] == 1 ) + { + $output_str .= "Display units for Ink Screen Frequency = Inches\n"; + } + else + { + $output_str .= "Display units for Ink Screen Frequency = Centimetres\n"; + } + + // Interpret Angle for screen + $output_str .= "Angle for screen = " . ($HalftoneInfo['Angle_int'] + $HalftoneInfo['Angle_dec']/65536) . " degrees\n"; + + // Interpret Shape of Halftone Dots + if ($HalftoneInfo['ShapeCode'] > 32768 ) + { + $HalftoneInfo['ShapeCode'] = $HalftoneInfo['ShapeCode'] - 65536; + } + if ( $HalftoneInfo['ShapeCode'] == 0 ) + { + $output_str .= "Shape of Halftone Dots = Round\n"; + } + elseif ( $HalftoneInfo['ShapeCode'] == 1 ) + { + $output_str .= "Shape of Halftone Dots = Ellipse\n"; + } + elseif ( $HalftoneInfo['ShapeCode'] == 2 ) + { + $output_str .= "Shape of Halftone Dots = Line\n"; + } + elseif ( $HalftoneInfo['ShapeCode'] == 3 ) + { + $output_str .= "Shape of Halftone Dots = Square\n"; + } + elseif ( $HalftoneInfo['ShapeCode'] == 4 ) + { + $output_str .= "Shape of Halftone Dots = Cross\n"; + } + elseif ( $HalftoneInfo['ShapeCode'] == 6 ) + { + $output_str .= "Shape of Halftone Dots = Diamond\n"; + } + else + { + $output_str .= "Shape of Halftone Dots = Unknown shape (" . $HalftoneInfo['ShapeCode'] . ")\n"; + } + + // Interpret Accurate Screens + if ( $HalftoneInfo['Accurate'] == 1 ) + { + $output_str .= "Use Accurate Screens Selected\n"; + } + else + { + $output_str .= "Use Other (not Accurate) Screens Selected\n"; + } + + // Interpret Printer Default Screens + if ( $HalftoneInfo['Default'] == 1 ) + { + $output_str .= "Use printer’s default screens\n"; + } + else + { + $output_str .= "Use Other (not Printer Default) Screens Selected\n"; + } + + // Return Text + return $output_str; + +} + +/****************************************************************************** +* End of Global Variable: Interpret_Halftone +******************************************************************************/ + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: Photoshop_ID_Names +* +* Contents: The Names of the Photoshop IRB resources, indexed by their +* resource number +* +******************************************************************************/ + +$GLOBALS[ "Photoshop_ID_Names" ] = array( +0x03E8 => "Number of channels, rows, columns, depth, and mode. (Obsolete)", +0x03E9 => "Macintosh print manager info ", +0x03EB => "Indexed color table (Obsolete)", +0x03ED => "Resolution Info", +0x03EE => "Alpha Channel Names", +0x03EF => "Display Info", +0x03F0 => "Caption String", +0x03F1 => "Border information", +0x03F2 => "Background color", +0x03F3 => "Print flags", +0x03F4 => "Grayscale and multichannel halftoning information", +0x03F5 => "Color halftoning information", +0x03F6 => "Duotone halftoning information", +0x03F7 => "Grayscale and multichannel transfer function", +0x03F8 => "Color transfer functions", +0x03F9 => "Duotone transfer functions", +0x03FA => "Duotone image information", +0x03FB => "Black and white values", +0x03FC => "Obsolete Resource.", +0x03FD => "EPS options", +0x03FE => "Quick Mask information", +0x03FF => "Obsolete Resource", +0x0400 => "Layer state information", +0x0401 => "Working path (not saved)", +0x0402 => "Layers group information", +0x0403 => "Obsolete Resource", +0x0404 => "IPTC-NAA record", +0x0405 => "Raw Format Image mode", +0x0406 => "JPEG quality", +0x0408 => "Grid and guides information", +0x0409 => "Thumbnail resource", +0x040A => "Copyright flag", +0x040B => "URL", +0x040C => "Thumbnail resource", +0x040D => "Global Angle", +0x040E => "Color samplers resource", +0x040F => "ICC Profile", +0x0410 => "Watermark", +0x0411 => "ICC Untagged", +0x0412 => "Effects visible", +0x0413 => "Spot Halftone", +0x0414 => "Document Specific IDs", +0x0415 => "Unicode Alpha Names", +0x0416 => "Indexed Color Table Count", +0x0417 => "Tansparent Index. Index of transparent color, if any.", +0x0419 => "Global Altitude", +0x041A => "Slices", +0x041B => "Workflow URL", +0x041C => "Jump To XPEP", +0x041D => "Alpha Identifiers", +0x041E => "URL List", +0x0421 => "Version Info", +0x0BB7 => "Name of clipping path.", +0x2710 => "Print flags information" +); + +/****************************************************************************** +* End of Global Variable: Photoshop_ID_Names +******************************************************************************/ + + + + + +/****************************************************************************** +* Global Variable: Photoshop_ID_Descriptions +* +* Contents: The Descriptions of the Photoshop IRB resources, indexed by their +* resource number +* +******************************************************************************/ + +$GLOBALS[ "Photoshop_ID_Descriptions" ] = array( +0x03E8 => "Obsolete—Photoshop 2.0 only. number of channels, rows, columns, depth, and mode.", +0x03E9 => "Optional. Macintosh print manager print info record.", +0x03EB => "Obsolete—Photoshop 2.0 only. Contains the indexed color table.", +0x03ED => "ResolutionInfo structure. See Appendix A in Photoshop SDK Guide.pdf", +0x03EE => "Names of the alpha channels as a series of Pascal strings.", +0x03EF => "DisplayInfo structure. See Appendix A in Photoshop SDK Guide.pdf", +0x03F0 => "Optional. The caption as a Pascal string.", +0x03F1 => "Border information. border width, border units", +0x03F2 => "Background color.", +0x03F3 => "Print flags. labels, crop marks, color bars, registration marks, negative, flip, interpolate, caption.", +0x03F4 => "Grayscale and multichannel halftoning information.", +0x03F5 => "Color halftoning information.", +0x03F6 => "Duotone halftoning information.", +0x03F7 => "Grayscale and multichannel transfer function.", +0x03F8 => "Color transfer functions.", +0x03F9 => "Duotone transfer functions.", +0x03FA => "Duotone image information.", +0x03FB => "Effective black and white values for the dot range.", +0x03FC => "Obsolete Resource.", +0x03FD => "EPS options.", +0x03FE => "Quick Mask information. Quick Mask channel ID, Mask initially empty.", +0x03FF => "Obsolete Resource.", +0x0400 => "Layer state information. Index of target layer.", +0x0401 => "Working path (not saved).", +0x0402 => "Layers group information. Group ID for the dragging groups. Layers in a group have the same group ID.", +0x0403 => "Obsolete Resource.", +0x0404 => "IPTC-NAA record. This contains the File Info... information. See the IIMV4.pdf document.", +0x0405 => "Image mode for raw format files.", +0x0406 => "JPEG quality. Private.", +0x0408 => "Grid and guides information.", +0x0409 => "Thumbnail resource.", +0x040A => "Copyright flag. Boolean indicating whether image is copyrighted. Can be set via Property suite or by user in File Info...", +0x040B => "URL. Handle of a text string with uniform resource locator. Can be set via Property suite or by user in File Info...", +0x040C => "Thumbnail resource.", +0x040D => "Global Angle. Global lighting angle for effects layer.", +0x040E => "Color samplers resource.", +0x040F => "ICC Profile. The raw bytes of an ICC format profile, see the ICC34.pdf and ICC34.h files from the Internation Color Consortium.", +0x0410 => "Watermark.", +0x0411 => "ICC Untagged. Disables any assumed profile handling when opening the file. 1 = intentionally untagged.", +0x0412 => "Effects visible. Show/hide all the effects layer.", +0x0413 => "Spot Halftone. Version, length, variable length data.", +0x0414 => "Document specific IDs for layer identification", +0x0415 => "Unicode Alpha Names. Length and the string", +0x0416 => "Indexed Color Table Count. Number of colors in table that are actually defined", +0x0417 => "Transparent Index. Index of transparent color, if any.", +0x0419 => "Global Altitude.", +0x041A => "Slices.", +0x041B => "Workflow URL. Length, string.", +0x041C => "Jump To XPEP. Major version, Minor version, Count. Table which can include: Dirty flag, Mod date.", +0x041D => "Alpha Identifiers.", +0x041E => "URL List. Count of URLs, IDs, and strings", +0x0421 => "Version Info. Version, HasRealMergedData, string of writer name, string of reader name, file version.", +0x0BB7 => "Name of clipping path.", +0x2710 => "Print flags information. Version, Center crop marks, Bleed width value, Bleed width scale." +); + +/****************************************************************************** +* End of Global Variable: Photoshop_ID_Descriptions +******************************************************************************/ + + + + + + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/PictureInfo.php b/includes/jpeg_metadata_tk/PictureInfo.php new file mode 100644 index 0000000..05edd00 --- /dev/null +++ b/includes/jpeg_metadata_tk/PictureInfo.php @@ -0,0 +1,284 @@ +<?php + +/****************************************************************************** +* +* Filename: PictureInfo.php +* +* Description: Provides functions for reading and writing information to/from +* the 'App 12' Picture Info segment of JPEG format files +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + +include_once 'Unicode.php'; + +/****************************************************************************** +* +* Function: get_jpeg_App12_Pic_Info +* +* Description: Retrieves the Picture Info text information from an App12 +* JPEG segment and returns it as a string. Uses information +* supplied by the get_jpeg_header_data function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: App12_Head - The text preceeding the Picture Info (often +* the camera manufacturer's name) +* App12_Text - The Picture Info Text +* FALSE, FALSE - if an APP 12 Picture Info segment could not be found +* +******************************************************************************/ + +function get_jpeg_App12_Pic_Info( $jpeg_header_data ) +{ + // Flag that an APP12 segment has not been found yet + $App12_PI_Location = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // Check if we have found an APP12 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP12" ) == 0 ) + { + // Found an APP12 segment + // Check if the APP12 has one of the correct labels (headers) + // for a picture info segment + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "[picture info]", 14) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "SEIKO EPSON CORP. \00", 20) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "Agfa Gevaert \x00", 16) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "SanyoElectricDSC\x00", 17) == 0 ) || + ( strncmp ( substr($jpeg_header_data[$i]['SegData'],1,3), "\x00\x00\x00", 3) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "Type=", 5) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "OLYMPUS OPTICAL CO.,LTD.", 24) == 0 ) ) + { + // A Picture Info segment was found, mark this position + $App12_PI_Location = $i; + } + } + } + + // Check if a Picture Info Segment was found + if ( $App12_PI_Location != -1 ) + { + // A picture Info Segment was found - Process it + + // Determine the length of the header if there is one + $head_length = 0; + + if ( strncmp ( $jpeg_header_data[$App12_PI_Location]['SegData'], "App12 Gevaert \x00", 16) == 0 ) + { + $head_length = 16; + } + else if ( strncmp ( $jpeg_header_data[$App12_PI_Location]['SegData'], "OLYMPUS OPTICAL CO.,LTD.", 24) == 0 ) + { + $head_length = 25; + } + else if ( strncmp ( $jpeg_header_data[$App12_PI_Location]['SegData'], "SEIKO EPSON CORP. \00", 20) == 0 ) + { + $head_length = 20; + } + else if ( strncmp ( $jpeg_header_data[$App12_PI_Location]['SegData'], "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) + { + $head_length = 5; + } + else if ( strncmp ( substr($jpeg_header_data[$App12_PI_Location]['SegData'],1,3), "\x00\x00\x00", 3) == 0 ) // HP + { + $head_length = 0; + } + else if ( strncmp ( $jpeg_header_data[$App12_PI_Location]['SegData'], "SanyoElectricDSC\x00", 17) == 0 ) + { + $head_length = 17; + } + else + { + $head_length = 0; + } + + // Extract the header and the Picture Info Text from the APP12 segment + $App12_PI_Head = substr( $jpeg_header_data[$App12_PI_Location]['SegData'], 0, $head_length ); + $App12_PI_Text = substr( $jpeg_header_data[$App12_PI_Location]['SegData'], $head_length ); + + + // Return the text which was extracted + + if ( ($pos = strpos ( $App12_PI_Text, "[end]" ) ) !== FALSE ) + { + return array( "Header" => $App12_PI_Head, "Picture Info" => substr( $App12_PI_Text, 0, $pos + 5 ) ); + } + else + { + return array( "Header" => $App12_PI_Head, "Picture Info" => $App12_PI_Text ); + } + } + + // No Picture Info Segment Found - Return False + return array( FALSE, FALSE ); +} + +/****************************************************************************** +* End of Function: get_jpeg_header_data +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: put_jpeg_App12_Pic_Info +* +* Description: Writes Picture Info text into an App12 JPEG segment. Uses information +* supplied by the get_jpeg_header_data function. If no App12 exists +* already a new one is created, otherwise it replaces the old one +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* new_Pic_Info_Text - The Picture Info Text, including any header +* that is required +* +* Returns: jpeg_header_data - the JPEG header array with the new Picture +* info segment inserted +* FALSE - if an error occured +* +******************************************************************************/ + +function put_jpeg_App12_Pic_Info( $jpeg_header_data, $new_Pic_Info_Text ) +{ + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // Check if we have found an APP12 header, + if ( strcmp ( $jpeg_header_data[$i][SegName], "APP12" ) == 0 ) + { + // Found an APP12 segment + // Check if the APP12 has one of the correct labels (headers) + // for a picture info segment + if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "[picture info]", 14) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "SEIKO EPSON CORP. \x00", 20) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "Agfa Gevaert \x00", 16) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "SanyoElectricDSC\x00", 17) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "Type=", 5) == 0 ) || + ( strncmp ( $jpeg_header_data[$i]['SegData'], "OLYMPUS OPTICAL CO.,LTD.", 24) == 0 ) ) + { + // Found a preexisting Picture Info segment - Replace it with the new one and return. + $jpeg_header_data[$i][SegData] = $new_Pic_Info_Text; + return $jpeg_header_data; + } + } + } + + // No preexisting Picture Info segment found, insert a new one at the start of the header data. + + // Determine highest position of an APP segment at or below APP12, so we can put the + // new APP12 at this position + + + $highest_APP = -1; + + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // Check if we have found an APP segment at or below APP12, + if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xEC ) ) + { + // Found an APP segment at or below APP12 + $highest_APP = $i; + } + } + + // Insert the new Picture Info segment + array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array( "SegType" => 0xEC, + "SegName" => "APP12", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xEC ], + "SegData" => $new_Pic_Info_Text ) ) ); + + return $jpeg_header_data; + + +} + +/****************************************************************************** +* End of Function: put_jpeg_header_data +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: Interpret_App12_Pic_Info_to_HTML +* +* Description: Generates html showing the contents of any JPEG App12 Picture +* Info segment +* +* Parameters: jpeg_header_data - the JPEG header data, as retrieved +* from the get_jpeg_header_data function +* +* Returns: output - the HTML +* +******************************************************************************/ + +function Interpret_App12_Pic_Info_to_HTML( $jpeg_header_data ) +{ + // Create a string to receive the output + $output = ""; + + // read the App12 Picture Info segment + $PI = get_jpeg_App12_Pic_Info( $jpeg_header_data ); + + // Check if the Picture Info segment was valid + if ( $PI !== array(FALSE, FALSE) ) + { + // Picture Info exists - add it to the output + $output .= "<h2 class=\"Picture_Info_Main_Heading\">Picture Info Text</h2>\n"; + $output .= "<p><span class=\"Picture_Info_Caption_Text\">Header: </span><span class=\"Picture_Info_Value_Text\">" . HTML_UTF8_Escape( $PI['Header'] ) . "</span></p>\n"; + $output .= "<p class=\"Picture_Info_Caption_Text\">Picture Info Text:</p><pre class=\"Picture_Info_Value_Text\">" . HTML_UTF8_Escape( $PI['Picture Info'] ) . "</pre>\n"; + } + + // Return the result + return $output; +} + +/****************************************************************************** +* End of Function: Interpret_App12_Pic_Info_to_HTML +******************************************************************************/ + + + +?> diff --git a/includes/jpeg_metadata_tk/Toolkit_Version.php b/includes/jpeg_metadata_tk/Toolkit_Version.php new file mode 100644 index 0000000..99be1f3 --- /dev/null +++ b/includes/jpeg_metadata_tk/Toolkit_Version.php @@ -0,0 +1,50 @@ +<?php +/****************************************************************************** +* +* Filename: XMP.php +* +* Description: Provides a single place where the current version of the toolkit +* is stored. +* +* Author: Evan Hunter +* +* Date: 27/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.11 +* +* NOTE: This file will change with every revision update, hence will not +* be shown in the changes list +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + +$GLOBALS['Toolkit_Version'] = "1.11"; + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Unicode.php b/includes/jpeg_metadata_tk/Unicode.php new file mode 100644 index 0000000..19cbc5b --- /dev/null +++ b/includes/jpeg_metadata_tk/Unicode.php @@ -0,0 +1,1227 @@ +<?php + +/****************************************************************************** +* +* Filename: Unicode.php +* +* Description: Provides functions for handling Unicode strings in PHP without +* needing to configure the non-default mbstring extension +* +* Author: Evan Hunter +* +* Date: 27/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.10 +* +* Changes: 1.00 -> 1.10 : Added the following functions: +* smart_HTML_Entities +* smart_htmlspecialchars +* HTML_UTF16_UnEscape +* HTML_UTF8_UnEscape +* changed HTML_UTF8_Escape and HTML_UTF16_Escape to +* use smart_htmlspecialchars, so that characters which +* were already escaped would remain intact +* +* +* 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: UTF-16 functions have not been tested fully + + + +/****************************************************************************** +* +* Unicode UTF-8 Encoding Functions +* +* Description: UTF-8 is a Unicode encoding system in which extended characters +* use only the upper half (128 values) of the byte range, thus it +* allows the use of normal 7-bit ASCII text. +* 7-Bit ASCII will pass straight through UTF-8 encoding/decoding without change +* +* +* The encoding is as follows: +* Unicode Value : Binary representation (x=data bit) +*-------------------------------------------------------------------------------- +* U-00000000 - U-0000007F: 0xxxxxxx <- This is 7-bit ASCII +* U-00000080 - U-000007FF: 110xxxxx 10xxxxxx +* U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx +* U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx +* U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx +* U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx +*-------------------------------------------------------------------------------- +* +******************************************************************************/ + + + + +/****************************************************************************** +* +* Unicode UTF-16 Encoding Functions +* +* Description: UTF-16 is a Unicode encoding system uses 16 bit values for representing +* characters. +* It also has an extended set of characters available by the use +* of surrogate pairs, which are a pair of 16 bit values, giving a +* total data length of 20 useful bits. +* +* +* The encoding is as follows: +* Unicode Value : Binary representation (x=data bit) +*-------------------------------------------------------------------------------- +* U-000000 - U-00D7FF: xxxxxxxx xxxxxxxx +* U-00D800 - U-00DBFF: Not available - used for high surrogate pairs +* U-00DC00 - U-00DFFF: Not available - used for low surrogate pairs + U-00E000 - U-00FFFF: xxxxxxxx xxxxxxxx +* U-010000 - U-10FFFF: 110110ww wwxxxxxx 110111xx xxxxxxxx ( wwww = (uni-0x10000)/0x10000 ) +*-------------------------------------------------------------------------------- +* +* Surrogate pair Calculations +* +* $hi = ($uni - 0x10000) / 0x400 + 0xD800; +* $lo = ($uni - 0x10000) % 0x400 + 0xDC00; +* +* +* $uni = 0x10000 + ($hi - 0xD800) * 0x400 + ($lo - 0xDC00); +* +* +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: UTF8_fix +* +* Description: Checks a string for badly formed Unicode UTF-8 coding and +* returns the same string containing only the parts which +* were properly formed UTF-8 data. +* +* Parameters: utf8_text - a string with possibly badly formed UTF-8 data +* +* Returns: output - the well formed UTF-8 version of the string +* +******************************************************************************/ + +function UTF8_fix( $utf8_text ) +{ + // Initialise the current position in the string + $pos = 0; + + // Create a string to accept the well formed output + $output = "" ; + + // Cycle through each group of bytes, ensuring the coding is correct + while ( $pos < strlen( $utf8_text ) ) + { + // Retreive the current numerical character value + $chval = ord($utf8_text{$pos}); + + // Check what the first character is - it will tell us how many bytes the + // Unicode value covers + + if ( ( $chval >= 0x00 ) && ( $chval <= 0x7F ) ) + { + // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character + $bytes = 1; + } + else if ( ( $chval >= 0xC0 ) && ( $chval <= 0xDF ) ) + { + // 2 Byte UTF-8 Unicode Character + $bytes = 2; + } + else if ( ( $chval >= 0xE0 ) && ( $chval <= 0xEF ) ) + { + // 3 Byte UTF-8 Unicode Character + $bytes = 3; + } + else if ( ( $chval >= 0xF0 ) && ( $chval <= 0xF7 ) ) + { + // 4 Byte UTF-8 Unicode Character + $bytes = 4; + } + else if ( ( $chval >= 0xF8 ) && ( $chval <= 0xFB ) ) + { + // 5 Byte UTF-8 Unicode Character + $bytes = 5; + } + else if ( ( $chval >= 0xFC ) && ( $chval <= 0xFD ) ) + { + // 6 Byte UTF-8 Unicode Character + $bytes = 6; + } + else + { + // Invalid Code - skip character and do nothing + $bytes = 0; + $pos++; + } + + + // check that there is enough data remaining to read + if (($pos + $bytes - 1) < strlen( $utf8_text ) ) + { + // Cycle through the number of bytes specified, + // copying them to the output string + while ( $bytes > 0 ) + { + $output .= $utf8_text{$pos}; + $pos++; + $bytes--; + } + } + else + { + break; + } + } + + // Return the result + return $output; +} + +/****************************************************************************** +* End of Function: UTF8_fix +******************************************************************************/ + + + + + + + + + +/****************************************************************************** +* +* Function: UTF16_fix +* +* Description: Checks a string for badly formed Unicode UTF-16 coding and +* returns the same string containing only the parts which +* were properly formed UTF-16 data. +* +* Parameters: utf16_text - a string with possibly badly formed UTF-16 data +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: output - the well formed UTF-16 version of the string +* +******************************************************************************/ + +function UTF16_fix( $utf16_text, $MSB_first ) +{ + // Initialise the current position in the string + $pos = 0; + + // Create a string to accept the well formed output + $output = "" ; + + // Cycle through each group of bytes, ensuring the coding is correct + while ( $pos < strlen( $utf16_text ) ) + { + // Retreive the current numerical character value + $chval1 = ord($utf16_text{$pos}); + + // Skip over character just read + $pos++; + + // Check if there is another character available + if ( $pos < strlen( $utf16_text ) ) + { + // Another character is available - get it for the second half of the UTF-16 value + $chval2 = ord( $utf16_text{$pos} ); + } + else + { + // Error - no second byte to this UTF-16 value - end processing + continue 1; + } + + // Skip over character just read + $pos++; + + // Calculate the 16 bit unicode value + if ( $MSB_first ) + { + // Big Endian + $UTF16_val = $chval1 * 0x100 + $chval2; + } + else + { + // Little Endian + $UTF16_val = $chval2 * 0x100 + $chval1; + } + + + + if ( ( ( $UTF16_val >= 0x0000 ) && ( $UTF16_val <= 0xD7FF ) ) || + ( ( $UTF16_val >= 0xE000 ) && ( $UTF16_val <= 0xFFFF ) ) ) + { + // Normal Character (Non Surrogate pair) + // Add it to the output + $output .= chr( $chval1 ) . chr ( $chval2 ); + } + else if ( ( $UTF16_val >= 0xD800 ) && ( $UTF16_val <= 0xDBFF ) ) + { + // High surrogate of a surrogate pair + // Now we need to read the low surrogate + // Check if there is another 2 characters available + if ( ( $pos + 3 ) < strlen( $utf16_text ) ) + { + // Another 2 characters are available - get them + $chval3 = ord( $utf16_text{$pos} ); + $chval4 = ord( $utf16_text{$pos+1} ); + + // Calculate the second 16 bit unicode value + if ( $MSB_first ) + { + // Big Endian + $UTF16_val2 = $chval3 * 0x100 + $chval4; + } + else + { + // Little Endian + $UTF16_val2 = $chval4 * 0x100 + $chval3; + } + + // Check that this is a low surrogate + if ( ( $UTF16_val2 >= 0xDC00 ) && ( $UTF16_val2 <= 0xDFFF ) ) + { + // Low surrogate found following high surrogate + // Add both to the output + $output .= chr( $chval1 ) . chr ( $chval2 ) . chr( $chval3 ) . chr ( $chval4 ); + + // Skip over the low surrogate + $pos += 2; + } + else + { + // Low surrogate not found after high surrogate + // Don't add either to the output + // Only the High surrogate is skipped and processing continues after it + } + + } + else + { + // Error - not enough data for low surrogate - end processing + continue 1; + } + + } + else + { + // Low surrogate of a surrogate pair + // This should not happen - it means this is a lone low surrogate + // Dont add it to the output + } + + } + + // Return the result + return $output; +} + +/****************************************************************************** +* End of Function: UTF16_fix +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: UTF8_to_unicode_array +* +* Description: Converts a string encoded with Unicode UTF-8, to an array of +* numbers which represent unicode character numbers +* +* Parameters: utf8_text - a string containing the UTF-8 data +* +* Returns: output - the array containing the unicode character numbers +* +******************************************************************************/ + +function UTF8_to_unicode_array( $utf8_text ) +{ + // Create an array to receive the unicode character numbers output + $output = array( ); + + // Cycle through the characters in the UTF-8 string + for ( $pos = 0; $pos < strlen( $utf8_text ); $pos++ ) + { + // Retreive the current numerical character value + $chval = ord($utf8_text{$pos}); + + // Check what the first character is - it will tell us how many bytes the + // Unicode value covers + + if ( ( $chval >= 0x00 ) && ( $chval <= 0x7F ) ) + { + // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character + $bytes = 1; + $outputval = $chval; // Since 7-bit ASCII is unaffected, the output equals the input + } + else if ( ( $chval >= 0xC0 ) && ( $chval <= 0xDF ) ) + { + // 2 Byte UTF-8 Unicode + $bytes = 2; + $outputval = $chval & 0x1F; // The first byte is bitwise ANDed with 0x1F to remove the leading 110b + } + else if ( ( $chval >= 0xE0 ) && ( $chval <= 0xEF ) ) + { + // 3 Byte UTF-8 Unicode + $bytes = 3; + $outputval = $chval & 0x0F; // The first byte is bitwise ANDed with 0x0F to remove the leading 1110b + } + else if ( ( $chval >= 0xF0 ) && ( $chval <= 0xF7 ) ) + { + // 4 Byte UTF-8 Unicode + $bytes = 4; + $outputval = $chval & 0x07; // The first byte is bitwise ANDed with 0x07 to remove the leading 11110b + } + else if ( ( $chval >= 0xF8 ) && ( $chval <= 0xFB ) ) + { + // 5 Byte UTF-8 Unicode + $bytes = 5; + $outputval = $chval & 0x03; // The first byte is bitwise ANDed with 0x03 to remove the leading 111110b + } + else if ( ( $chval >= 0xFC ) && ( $chval <= 0xFD ) ) + { + // 6 Byte UTF-8 Unicode + $bytes = 6; + $outputval = $chval & 0x01; // The first byte is bitwise ANDed with 0x01 to remove the leading 1111110b + } + else + { + // Invalid Code - do nothing + $bytes = 0; + } + + // Check if the byte was valid + if ( $bytes !== 0 ) + { + // The byte was valid + + // Check if there is enough data left in the UTF-8 string to allow the + // retrieval of the remainder of this unicode character + if ( $pos + $bytes - 1 < strlen( $utf8_text ) ) + { + // The UTF-8 string is long enough + + // Cycle through the number of bytes required, + // minus the first one which has already been done + while ( $bytes > 1 ) + { + $pos++; + $bytes--; + + // Each remaining byte is coded with 6 bits of data and 10b on the high + // order bits. Hence we need to shift left by 6 bits (0x40) then add the + // current characer after it has been bitwise ANDed with 0x3F to remove the + // highest two bits. + $outputval = $outputval*0x40 + ( (ord($utf8_text{$pos})) & 0x3F ); + } + + // Add the calculated Unicode number to the output array + $output[] = $outputval; + } + } + + } + + // Return the resulting array + return $output; +} + +/****************************************************************************** +* End of Function: UTF8_to_unicode_array +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: UTF16_to_unicode_array +* +* Description: Converts a string encoded with Unicode UTF-16, to an array of +* numbers which represent unicode character numbers +* +* Parameters: utf16_text - a string containing the UTF-16 data +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: output - the array containing the unicode character numbers +* +******************************************************************************/ + +function UTF16_to_unicode_array( $utf16_text, $MSB_first ) +{ + // Create an array to receive the unicode character numbers output + $output = array( ); + + + // Initialise the current position in the string + $pos = 0; + + // Cycle through each group of bytes, ensuring the coding is correct + while ( $pos < strlen( $utf16_text ) ) + { + // Retreive the current numerical character value + $chval1 = ord($utf16_text{$pos}); + + // Skip over character just read + $pos++; + + // Check if there is another character available + if ( $pos < strlen( $utf16_text ) ) + { + // Another character is available - get it for the second half of the UTF-16 value + $chval2 = ord( $utf16_text{$pos} ); + } + else + { + // Error - no second byte to this UTF-16 value - end processing + continue 1; + } + + // Skip over character just read + $pos++; + + // Calculate the 16 bit unicode value + if ( $MSB_first ) + { + // Big Endian + $UTF16_val = $chval1 * 0x100 + $chval2; + } + else + { + // Little Endian + $UTF16_val = $chval2 * 0x100 + $chval1; + } + + + if ( ( ( $UTF16_val >= 0x0000 ) && ( $UTF16_val <= 0xD7FF ) ) || + ( ( $UTF16_val >= 0xE000 ) && ( $UTF16_val <= 0xFFFF ) ) ) + { + // Normal Character (Non Surrogate pair) + // Add it to the output + $output[] = $UTF16_val; + } + else if ( ( $UTF16_val >= 0xD800 ) && ( $UTF16_val <= 0xDBFF ) ) + { + // High surrogate of a surrogate pair + // Now we need to read the low surrogate + // Check if there is another 2 characters available + if ( ( $pos + 3 ) < strlen( $utf16_text ) ) + { + // Another 2 characters are available - get them + $chval3 = ord( $utf16_text{$pos} ); + $chval4 = ord( $utf16_text{$pos+1} ); + + // Calculate the second 16 bit unicode value + if ( $MSB_first ) + { + // Big Endian + $UTF16_val2 = $chval3 * 0x100 + $chval4; + } + else + { + // Little Endian + $UTF16_val2 = $chval4 * 0x100 + $chval3; + } + + // Check that this is a low surrogate + if ( ( $UTF16_val2 >= 0xDC00 ) && ( $UTF16_val2 <= 0xDFFF ) ) + { + // Low surrogate found following high surrogate + // Add both to the output + $output[] = 0x10000 + ( ( $UTF16_val - 0xD800 ) * 0x400 ) + ( $UTF16_val2 - 0xDC00 ); + + // Skip over the low surrogate + $pos += 2; + } + else + { + // Low surrogate not found after high surrogate + // Don't add either to the output + // The high surrogate is skipped and processing continued + } + + } + else + { + // Error - not enough data for low surrogate - end processing + continue 1; + } + + } + else + { + // Low surrogate of a surrogate pair + // This should not happen - it means this is a lone low surrogate + // Don't add it to the output + } + + } + + // Return the result + return $output; + + +} + +/****************************************************************************** +* End of Function: UTF16_to_unicode_array +******************************************************************************/ + + + + + + + +/****************************************************************************** +* +* Function: unicode_array_to_UTF8 +* +* Description: Converts an array of unicode character numbers to a string +* encoded by UTF-8 +* +* Parameters: unicode_array - the array containing unicode character numbers +* +* Returns: output - the UTF-8 encoded string representing the data +* +******************************************************************************/ + +function unicode_array_to_UTF8( $unicode_array ) +{ + + // Create a string to receive the UTF-8 output + $output = ""; + + // Cycle through each Unicode character number + foreach( $unicode_array as $unicode_char ) + { + // Check which range the current unicode character lies in + if ( ( $unicode_char >= 0x00 ) && ( $unicode_char <= 0x7F ) ) + { + // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character + + $output .= chr($unicode_char); // Output is equal to input for 7-bit ASCII + } + else if ( ( $unicode_char >= 0x80 ) && ( $unicode_char <= 0x7FF ) ) + { + // 2 Byte UTF-8 Unicode - binary encode data as : 110xxxxx 10xxxxxx + + $output .= chr(0xC0 + ($unicode_char/0x40)); + $output .= chr(0x80 + ($unicode_char & 0x3F)); + } + else if ( ( $unicode_char >= 0x800 ) && ( $unicode_char <= 0xFFFF ) ) + { + // 3 Byte UTF-8 Unicode - binary encode data as : 1110xxxx 10xxxxxx 10xxxxxx + + $output .= chr(0xE0 + ($unicode_char/0x1000)); + $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); + $output .= chr(0x80 + ($unicode_char & 0x3F)); + } + else if ( ( $unicode_char >= 0x10000 ) && ( $unicode_char <= 0x1FFFFF ) ) + { + // 4 Byte UTF-8 Unicode - binary encode data as : 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + + $output .= chr(0xF0 + ($unicode_char/0x40000)); + $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); + $output .= chr(0x80 + ($unicode_char & 0x3F)); + } + else if ( ( $unicode_char >= 0x200000 ) && ( $unicode_char <= 0x3FFFFFF ) ) + { + // 5 Byte UTF-8 Unicode - binary encode data as : 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + + $output .= chr(0xF8 + ($unicode_char/0x1000000)); + $output .= chr(0x80 + (($unicode_char/0x40000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); + $output .= chr(0x80 + ($unicode_char & 0x3F)); + } + else if ( ( $unicode_char >= 0x4000000 ) && ( $unicode_char <= 0x7FFFFFFF ) ) + { + // 6 Byte UTF-8 Unicode - binary encode data as : 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + + $output .= chr(0xFC + ($unicode_char/0x40000000)); + $output .= chr(0x80 + (($unicode_char/0x1000000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x40000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F)); + $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F)); + $output .= chr(0x80 + ($unicode_char & 0x3F)); + } + else + { + // Invalid Code - do nothing + } + + } + + // Return resulting UTF-8 String + return $output; +} + +/****************************************************************************** +* End of Function: unicode_array_to_UTF8 +******************************************************************************/ + + + + + + + + + +/****************************************************************************** +* +* Function: unicode_array_to_UTF16 +* +* Description: Converts an array of unicode character numbers to a string +* encoded by UTF-16 +* +* Parameters: unicode_array - the array containing unicode character numbers +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: output - the UTF-16 encoded string representing the data +* +******************************************************************************/ + +function unicode_array_to_UTF16( $unicode_array, $MSB_first ) +{ + + // Create a string to receive the UTF-16 output + $output = ""; + + // Cycle through each Unicode character number + foreach( $unicode_array as $unicode_char ) + { + // Check which range the current unicode character lies in + if ( ( ( $unicode_char >= 0x0000 ) && ( $unicode_char <= 0xD7FF ) ) || + ( ( $unicode_char >= 0xE000 ) && ( $unicode_char <= 0xFFFF ) ) ) + { + // Normal 16 Bit Character (Not a Surrogate Pair) + + // Check what byte order should be used + if ( $MSB_first ) + { + // Big Endian + $output .= chr( $unicode_char / 0x100 ) . chr( $unicode_char % 0x100 ) ; + } + else + { + // Little Endian + $output .= chr( $unicode_char % 0x100 ) . chr( $unicode_char / 0x100 ) ; + } + + } + else if ( ( $unicode_char >= 0x10000 ) && ( $unicode_char <= 0x10FFFF ) ) + { + // Surrogate Pair required + + // Calculate Surrogates + $High_Surrogate = ( ( $unicode_char - 0x10000 ) / 0x400 ) + 0xD800; + $Low_Surrogate = ( ( $unicode_char - 0x10000 ) % 0x400 ) + 0xDC00; + + // Check what byte order should be used + if ( $MSB_first ) + { + // Big Endian + $output .= chr( $High_Surrogate / 0x100 ) . chr( $High_Surrogate % 0x100 ); + $output .= chr( $Low_Surrogate / 0x100 ) . chr( $Low_Surrogate % 0x100 ); + } + else + { + // Little Endian + $output .= chr( $High_Surrogate % 0x100 ) . chr( $High_Surrogate / 0x100 ); + $output .= chr( $Low_Surrogate % 0x100 ) . chr( $Low_Surrogate / 0x100 ); + } + } + else + { + // Invalid UTF-16 codepoint + // Unicode value should never be between 0xD800 and 0xDFFF + // Do not output this point - there is no way to encode it in UTF-16 + } + + } + + // Return resulting UTF-16 String + return $output; +} + +/****************************************************************************** +* End of Function: unicode_array_to_UTF16 +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: xml_UTF8_clean +* +* Description: XML has specific requirements about the characters that are +* allowed, and characters that must be escaped. +* This function ensures that all characters in the given string +* are valid, and that characters such as Quotes, Greater than, +* Less than and Ampersand are properly escaped. Newlines and Tabs +* are also escaped. +* Note - Do not use this on constructed XML which includes tags, +* as it will escape the tags. It is designed to be used +* on the tag and attribute names, attribute values, and text. +* +* Parameters: utf8_text - a string containing the UTF-8 data +* +* Returns: output - the array containing the unicode character numbers +* +******************************************************************************/ + +function xml_UTF8_clean( $UTF8_text ) +{ + // Ensure that the Unicode UTF8 encoding is valid. + + $UTF8_text = UTF8_fix( $UTF8_text ); + + + // XML only allows characters in the following unicode ranges + // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + // Hence we need to delete any characters that dont fit this + + // Convert the UTF-8 string to an array of unicode character numbers + $unicode_array = UTF8_to_unicode_array( $UTF8_text ); + + // Create a new array to receive the valid unicode character numbers + $new_unicode_array = array( ); + + // Cycle through the unicode character numbers + foreach( $unicode_array as $unichar ) + { + // Check if the unicode character number is valid for XML + if ( ( $unichar == 0x09 ) || + ( $unichar == 0x0A ) || + ( $unichar == 0x0D ) || + ( ( $unichar >= 0x20 ) && ( $unichar <= 0xD7FF ) ) || + ( ( $unichar >= 0xE000 ) && ( $unichar <= 0xFFFD ) ) || + ( ( $unichar >= 0x10000 ) && ( $unichar <= 0x10FFFF ) ) ) + { + // Unicode character is valid for XML - add it to the valid characters array + $new_unicode_array[] = $unichar; + } + + } + + // Convert the array of valid unicode character numbers back to UTF-8 encoded text + $UTF8_text = unicode_array_to_UTF8( $new_unicode_array ); + + // Escape any special HTML characters present + $UTF8_text = htmlspecialchars ( $UTF8_text, ENT_QUOTES ); + + // Escape CR, LF and TAB characters, so that they are kept and not treated as expendable white space + $trans = array( "\x09" => "	", "\x0A" => "
", "\x0D" => "
" ); + $UTF8_text = strtr( $UTF8_text, $trans ); + + // Return the resulting XML valid string + return $UTF8_text; +} + +/****************************************************************************** +* End of Function: xml_UTF8_clean +******************************************************************************/ + + + + + + + + + +/****************************************************************************** +* +* Function: xml_UTF16_clean +* +* Description: XML has specific requirements about the characters that are +* allowed, and characters that must be escaped. +* This function ensures that all characters in the given string +* are valid, and that characters such as Quotes, Greater than, +* Less than and Ampersand are properly escaped. Newlines and Tabs +* are also escaped. +* Note - Do not use this on constructed XML which includes tags, +* as it will escape the tags. It is designed to be used +* on the tag and attribute names, attribute values, and text. +* +* Parameters: utf16_text - a string containing the UTF-16 data +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: output - the array containing the unicode character numbers +* +******************************************************************************/ + +function xml_UTF16_clean( $UTF16_text, $MSB_first ) +{ + // Ensure that the Unicode UTF16 encoding is valid. + + $UTF16_text = UTF16_fix( $UTF16_text, $MSB_first ); + + + // XML only allows characters in the following unicode ranges + // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + // Hence we need to delete any characters that dont fit this + + // Convert the UTF-16 string to an array of unicode character numbers + $unicode_array = UTF16_to_unicode_array( $UTF16_text, $MSB_first ); + + // Create a new array to receive the valid unicode character numbers + $new_unicode_array = array( ); + + // Cycle through the unicode character numbers + foreach( $unicode_array as $unichar ) + { + // Check if the unicode character number is valid for XML + if ( ( $unichar == 0x09 ) || + ( $unichar == 0x0A ) || + ( $unichar == 0x0D ) || + ( ( $unichar >= 0x20 ) && ( $unichar <= 0xD7FF ) ) || + ( ( $unichar >= 0xE000 ) && ( $unichar <= 0xFFFD ) ) || + ( ( $unichar >= 0x10000 ) && ( $unichar <= 0x10FFFF ) ) ) + { + // Unicode character is valid for XML - add it to the valid characters array + $new_unicode_array[] = $unichar; + } + + } + + // Convert the array of valid unicode character numbers back to UTF-16 encoded text + $UTF16_text = unicode_array_to_UTF16( $new_unicode_array, $MSB_first ); + + // Escape any special HTML characters present + $UTF16_text = htmlspecialchars ( $UTF16_text, ENT_QUOTES ); + + // Escape CR, LF and TAB characters, so that they are kept and not treated as expendable white space + $trans = array( "\x09" => "	", "\x0A" => "
", "\x0D" => "
" ); + $UTF16_text = strtr( $UTF16_text, $trans ); + + // Return the resulting XML valid string + return $UTF16_text; +} + +/****************************************************************************** +* End of Function: xml_UTF16_clean +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: HTML_UTF8_Escape +* +* Description: A HTML page can display UTF-8 data properly if it has a +* META http-equiv="Content-Type" tag with the content attribute +* including the value: "charset=utf-8". +* Otherwise the ISO-8859-1 character set is usually assumed, and +* Unicode values above 0x7F must be escaped. +* This function takes a UTF-8 encoded string and escapes the +* characters above 0x7F as well as reserved HTML characters such +* as Quotes, Greater than, Less than and Ampersand. +* +* Parameters: utf8_text - a string containing the UTF-8 data +* +* Returns: htmloutput - a string containing the HTML equivalent +* +******************************************************************************/ + +function HTML_UTF8_Escape( $UTF8_text ) +{ + + // Ensure that the Unicode UTF8 encoding is valid. + $UTF8_text = UTF8_fix( $UTF8_text ); + + // Change: changed to use smart_htmlspecialchars, so that characters which were already escaped would remain intact, as of revision 1.10 + // Escape any special HTML characters present + $UTF8_text = smart_htmlspecialchars( $UTF8_text, ENT_QUOTES ); + + // Convert the UTF-8 string to an array of unicode character numbers + $unicode_array = UTF8_to_unicode_array( $UTF8_text ); + + // Create a string to receive the escaped HTML + $htmloutput = ""; + + // Cycle through the unicode character numbers + foreach( $unicode_array as $unichar ) + { + // Check if the character needs to be escaped + if ( ( $unichar >= 0x00 ) && ( $unichar <= 0x7F ) ) + { + // Character is less than 0x7F - add it to the html as is + $htmloutput .= chr( $unichar ); + } + else + { + // Character is greater than 0x7F - escape it and add it to the html + $htmloutput .= "&#x" . dechex($unichar) . ";"; + } + } + + // Return the resulting escaped HTML + return $htmloutput; +} + +/****************************************************************************** +* End of Function: HTML_UTF8_Escape +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: HTML_UTF8_UnEscape +* +* Description: Converts HTML which contains escaped decimal or hex characters +* into UTF-8 text +* +* Parameters: HTML_text - a string containing the HTML text to convert +* +* Returns: utfoutput - a string containing the UTF-8 equivalent +* +******************************************************************************/ + +function HTML_UTF8_UnEscape( $HTML_text ) +{ + preg_match_all( "/\&\#(\d+);/", $HTML_text, $matches); + preg_match_all( "/\&\#[x|X]([A|B|C|D|E|F|a|b|c|d|e|f|0-9]+);/", $HTML_text, $hexmatches); + foreach( $hexmatches[1] as $index => $match ) + { + $matches[0][] = $hexmatches[0][$index]; + $matches[1][] = hexdec( $match ); + } + + for ( $i = 0; $i < count( $matches[ 0 ] ); $i++ ) + { + $trans = array( $matches[0][$i] => unicode_array_to_UTF8( array( $matches[1][$i] ) ) ); + + $HTML_text = strtr( $HTML_text , $trans ); + } + return $HTML_text; +} + +/****************************************************************************** +* End of Function: HTML_UTF8_UnEscape +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: HTML_UTF16_Escape +* +* Description: A HTML page can display UTF-16 data properly if it has a +* META http-equiv="Content-Type" tag with the content attribute +* including the value: "charset=utf-16". +* Otherwise the ISO-8859-1 character set is usually assumed, and +* Unicode values above 0x7F must be escaped. +* This function takes a UTF-16 encoded string and escapes the +* characters above 0x7F as well as reserved HTML characters such +* as Quotes, Greater than, Less than and Ampersand. +* +* Parameters: utf16_text - a string containing the UTF-16 data +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: htmloutput - a string containing the HTML equivalent +* +******************************************************************************/ + +function HTML_UTF16_Escape( $UTF16_text, $MSB_first ) +{ + + // Ensure that the Unicode UTF16 encoding is valid. + $UTF16_text = UTF16_fix( $UTF16_text, $MSB_first ); + + // Change: changed to use smart_htmlspecialchars, so that characters which were already escaped would remain intact, as of revision 1.10 + // Escape any special HTML characters present + $UTF16_text = smart_htmlspecialchars( $UTF16_text ); + + // Convert the UTF-16 string to an array of unicode character numbers + $unicode_array = UTF16_to_unicode_array( $UTF16_text, $MSB_first ); + + // Create a string to receive the escaped HTML + $htmloutput = ""; + + // Cycle through the unicode character numbers + foreach( $unicode_array as $unichar ) + { + // Check if the character needs to be escaped + if ( ( $unichar >= 0x00 ) && ( $unichar <= 0x7F ) ) + { + // Character is less than 0x7F - add it to the html as is + $htmloutput .= chr( $unichar ); + } + else + { + // Character is greater than 0x7F - escape it and add it to the html + $htmloutput .= "&#x" . dechex($unichar) . ";"; + } + } + + // Return the resulting escaped HTML + return $htmloutput; +} + +/****************************************************************************** +* End of Function: HTML_UTF16_Escape +******************************************************************************/ + + +/****************************************************************************** +* +* Function: HTML_UTF16_UnEscape +* +* Description: Converts HTML which contains escaped decimal or hex characters +* into UTF-16 text +* +* Parameters: HTML_text - a string containing the HTML text to be converted +* MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first) +* False will cause processing as Little Endian UTF-16 (Intel, LSB first) +* +* Returns: utfoutput - a string containing the UTF-16 equivalent +* +******************************************************************************/ + +function HTML_UTF16_UnEscape( $HTML_text, $MSB_first ) +{ + $utf8_text = HTML_UTF8_UnEscape( $HTML_text ); + + return unicode_array_to_UTF16( UTF8_to_unicode_array( $utf8_text ), $MSB_first ); +} + +/****************************************************************************** +* End of Function: HTML_UTF16_UnEscape +******************************************************************************/ + + + + +/****************************************************************************** +* +* Function: smart_HTML_Entities +* +* Description: Performs the same function as HTML_Entities, but leaves entities +* that are already escaped intact. +* +* Parameters: HTML_text - a string containing the HTML text to be escaped +* +* Returns: HTML_text_out - a string containing the escaped HTML text +* +******************************************************************************/ + +function smart_HTML_Entities( $HTML_text ) +{ + // Get a table containing the HTML entities translations + $translation_table = get_html_translation_table( HTML_ENTITIES ); + + // Change the ampersand to translate to itself, to avoid getting & + $translation_table[ chr(38) ] = '&'; + + // Perform replacements + // Regular expression says: find an ampersand, check the text after it, + // if the text after it is not one of the following, then replace the ampersand + // with & + // a) any combination of up to 4 letters (upper or lower case) with at least 2 or 3 non whitespace characters, then a semicolon + // b) a hash symbol, then between 2 and 7 digits + // c) a hash symbol, an 'x' character, then between 2 and 7 digits + // d) a hash symbol, an 'X' character, then between 2 and 7 digits + return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,7}|#x[0-9]{2,7}|#X[0-9]{2,7};)/","&" , strtr( $HTML_text, $translation_table ) ); +} + +/****************************************************************************** +* End of Function: smart_HTML_Entities +******************************************************************************/ + + + +/****************************************************************************** +* +* Function: smart_htmlspecialchars +* +* Description: Performs the same function as htmlspecialchars, but leaves characters +* that are already escaped intact. +* +* Parameters: HTML_text - a string containing the HTML text to be escaped +* +* Returns: HTML_text_out - a string containing the escaped HTML text +* +******************************************************************************/ + +function smart_htmlspecialchars( $HTML_text ) +{ + // Get a table containing the HTML special characters translations + $translation_table=get_html_translation_table (HTML_SPECIALCHARS); + + // Change the ampersand to translate to itself, to avoid getting & + $translation_table[ chr(38) ] = '&'; + + // Perform replacements + // Regular expression says: find an ampersand, check the text after it, + // if the text after it is not one of the following, then replace the ampersand + // with & + // a) any combination of up to 4 letters (upper or lower case) with at least 2 or 3 non whitespace characters, then a semicolon + // b) a hash symbol, then between 2 and 7 digits + // c) a hash symbol, an 'x' character, then between 2 and 7 digits + // d) a hash symbol, an 'X' character, then between 2 and 7 digits + return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,7}|#x[0-9]{2,7}|#X[0-9]{2,7};)/","&" , strtr( $HTML_text, $translation_table ) ); +} + +/****************************************************************************** +* End of Function: smart_htmlspecialchars +******************************************************************************/ + + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/Write_File_Info.php b/includes/jpeg_metadata_tk/Write_File_Info.php new file mode 100644 index 0000000..efa402a --- /dev/null +++ b/includes/jpeg_metadata_tk/Write_File_Info.php @@ -0,0 +1,196 @@ +<html> + +<!--*************************************************************************** +* +* Filename: Write_File_Info.php +* +* Description: An example file showing how a user can write the metadata of an +* image over the internet in the same way that Photoshop +* edits 'File Info' data. +* This script pairs with Edit_File_Info_Example.php, receiving +* and processing the data from the HTML form in that script +* +* Author: Evan Hunter +* +* Date: 17/11/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* Changes: 1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php +* Changed error reporting to no errors +* Removed limitation on file being in current directory +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +***************************************************************************--> + + + <head> + <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css"> + <STYLE TYPE="text/css" MEDIA="screen, print, projection"> + <!-- + + BODY { background-color:#505050; color:#F0F0F0 } + a { color:orange } + .EXIF_Main_Heading { color:red } + .EXIF_Secondary_Heading{ color: orange} + .EXIF_Table { border-collapse: collapse ; border: 1px solid #909000} + .EXIF_Table tbody td{border-width: 1px; border-style:solid; border-color: #909000;} + + --> + </STYLE> + + <title>Writing Photoshop File Info Metadata</title> + </head> + + <body> + <?php require_once 'Toolkit_Version.php'; ?> + <p>Powered by: <a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p> <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 --> + <br> + <br> + + <?php + // Turn off Error Reporting + error_reporting ( 0 ); // Change: changed to no reporting - as of version 1.11 + + require_once 'Toolkit_Version.php'; // Change: added as of version 1.11 + + // Include the required files for reading and writing Photoshop File Info + require_once 'JPEG.php'; + require_once 'XMP.php'; + require_once 'Photoshop_IRB.php'; + require_once 'EXIF.php'; + require_once 'Photoshop_File_Info.php'; + + + // Copy all of the HTML Posted variables into an array + $new_ps_file_info_array = $GLOBALS['HTTP_POST_VARS']; + + // Some characters are escaped with backslashes in HTML Posted variable + // Cycle through each of the HTML Posted variables, and strip out the slashes + foreach( $new_ps_file_info_array as $var_key => $var_val ) + { + $new_ps_file_info_array[ $var_key ] = stripslashes( $var_val ); + } + + // Keywords should be an array - explode it on newline boundarys + $new_ps_file_info_array[ 'keywords' ] = explode( "\n", trim( $new_ps_file_info_array[ 'keywords' ] ) ); + + // Supplemental Categories should be an array - explode it on newline boundarys + $new_ps_file_info_array[ 'supplementalcategories' ] = explode( "\n", trim( $new_ps_file_info_array[ 'supplementalcategories' ] ) ); + + // Make the filename easier to access + $filename = $new_ps_file_info_array[ 'filename' ]; + + // Protect against hackers editing other files + $path_parts = pathinfo( $filename ); + if ( strcasecmp( $path_parts["extension"], "jpg" ) != 0 ) + { + echo "Incorrect File Type - JPEG Only\n"; + exit( ); + } + // Change: removed limitation on file being in current directory - as of version 1.11 + + // Retrieve the header information + $jpeg_header_data = get_jpeg_header_data( $filename ); + + // Retreive the EXIF, XMP and Photoshop IRB information from + // the existing file, so that it can be updated + $Exif_array = get_EXIF_JPEG( $filename ); + $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) ); + $IRB_array = get_Photoshop_IRB( $jpeg_header_data ); + + // Update the JPEG header information with the new Photoshop File Info + $jpeg_header_data = put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Exif_array, $XMP_array, $IRB_array ); + + // Check if the Update worked + if ( $jpeg_header_data == FALSE ) + { + // Update of file info didn't work - output error message + echo "Error - Failure update Photoshop File Info : $filename <br>\n"; + + // Output HTML with the form and data which was + // sent, to allow the user to fix it + + $outputfilename = $filename; + require_once "Edit_File_info.php"; + echo "</body>\n"; + echo "</html>\n"; + + // Abort processing + exit( ); + } + + // Attempt to write the new JPEG file + if ( FALSE == put_jpeg_header_data( $filename, $filename, $jpeg_header_data ) ) + { + // Writing of the new file didn't work - output error message + echo "Error - Failure to write new JPEG : $filename <br>\n"; + + // Output HTML with the form and data which was + // sent, to allow the user to fix it + + $outputfilename = $filename; + require_once "Edit_File_info.php"; + echo "</body>\n"; + echo "</html>\n"; + + // Abort processing + exit( ); + } + + + // Writing of new JPEG succeeded + + // Output information about new file + + echo "<h1>DONE! - $filename updated</h1>\n"; + echo "<p><a href=\"Example.php?jpeg_fname=$filename\" >View Full Metatdata Information</a></p>\n"; + echo "<p><a href=\"Edit_File_Info_Example.php?jpeg_fname=$filename\" >Re-Edit Photoshop File Info</a></p>\n"; + echo "<br><br>\n"; + echo "<p>Below is the updated image, you can save it and look at the changed metadata in your favorite image editor</p>\n"; + echo "<p><img src=\"$filename\" ></p>\n"; + + + ?> + + <br> + <br> + <br> + <br> + + + <p>Powered by: <a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p> <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 --> + + <br> + <br> + + </body> + +</html> diff --git a/includes/jpeg_metadata_tk/XML.php b/includes/jpeg_metadata_tk/XML.php new file mode 100644 index 0000000..2ce4668 --- /dev/null +++ b/includes/jpeg_metadata_tk/XML.php @@ -0,0 +1,396 @@ +<?php + +/****************************************************************************** +* +* Filename: XML.php +* +* Description: Provides functions for parsing and constructing XML information +* +* Author: Evan Hunter +* +* Date: 27/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.10 +* +* Changes: 1.00 -> 1.10 : Changed read_xml_array_from_text to fix problem that +* caused the whitespace (especially newlines) to be +* destroyed when converting xml text to an xml array +* +* 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 +* +******************************************************************************/ + +include_once 'Unicode.php'; // Unicode is required as XML is always Unicode encoded + + +/****************************************************************************** +* +* Function: read_xml_array_from_text +* +* Description: Parses a string containing XML, and returns the resulting +* tree structure array, which contains all the XML information. +* Note: White space and comments in the XML are ignored +* Note: All text information contained in the tree structure +* is encoded as Unicode UTF-8. Hence text will appear as +* normal ASCII except where there is an extended character. +* +* Parameters: xmltext - a string containing the XML to be parsed +* +* Returns: output - the tree structure array containing the XML information +* FALSE - if an error occured +* +******************************************************************************/ + +function read_xml_array_from_text( $xmltext ) +{ + // Check if there actually is any text to parse + if ( trim( $xmltext ) == "" ) + { + return FALSE; + } + + // Create an instance of a xml parser to parse the XML text + $xml_parser = xml_parser_create( "UTF-8" ); + + + // Change: Fixed problem that caused the whitespace (especially newlines) to be destroyed when converting xml text to an xml array, as of revision 1.10 + + // We would like to remove unneccessary white space, but this will also + // remove things like newlines (
) in the XML values, so white space + // will have to be removed later + if ( xml_parser_set_option($xml_parser,XML_OPTION_SKIP_WHITE,0) == FALSE ) + { + // Error setting case folding - destroy the parser and return + xml_parser_free($xml_parser); + return FALSE; + } + + // to use XML code correctly we have to turn case folding + // (uppercasing) off. XML is case sensitive and upper + // casing is in reality XML standards violation + if ( xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0) == FALSE ) + { + // Error setting case folding - destroy the parser and return + xml_parser_free($xml_parser); + return FALSE; + } + + // Parse the XML text into a array structure + if ( xml_parse_into_struct($xml_parser, $xmltext, $vals, $index) == 0 ) + { + // Error Parsing XML - destroy the parser and return + xml_parser_free($xml_parser); + return FALSE; + } + + // Destroy the xml parser + xml_parser_free($xml_parser); + + + // Change: Fixed problem that caused the whitespace (especially newlines) to be destroyed when converting xml text to an xml array, as of revision 1.10 + + // Since the xml was processed with whitespace enabled, it will have many values which are + // only whitespace. These need to be removed to make a sensible array. + + $newvals = array( ); + + // Cycle through each of the items + foreach( $vals as $valno => $val ) + { + // If the item has a whitespace only value, remove it + if ( ( array_key_exists( 'value', $val ) ) && (trim( $val[ 'value' ] ) == "" ) ) + { + unset( $val[ 'value' ] ); + } + // If the item has a value (which will be non blank now) or is of type other than cdata, add it to the new array + if ( ( $val[ 'type' ] != 'cdata' ) || ( array_key_exists( 'value', $val ) ) ) + { + $newvals[] = $val; + } + + } + + // The xml_parse_into_struct function returns a flat version + // of the XML data, where each tag has a level number attached. + // This is very difficult to work with, so it needs to be + // converted to a tree structure before being returned + return xml_get_children($newvals, $i=0); + +} + +/****************************************************************************** +* End of Function: read_xml_array_from_text +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: write_xml_array_to_text +* +* Description: Takes a tree structure array (in the same format as returned +* by read_xml_array_from_text, and constructs a string containing +* the equivalent XML. This function is recursive, and produces +* XML which has correct indents. +* Note: All text information contained in the tree structure +* can be either 7-bit ASCII or encoded as Unicode UTF-8, +* since UTF-8 passes 7-bit ASCII text unchanged. +* +* Parameters: xmlarray - the tree structure array containing the information to +* be converted to XML +* indentlevel - the indent level of the top level tags (usually zero) +* +* Returns: output - the string containing the equivalent XML +* FALSE - if an error occured +* +******************************************************************************/ + +function write_xml_array_to_text( $xmlarray, $indentlevel ) +{ + // Create a string to receive the XML + $output_xml_text = ""; + + + // Cycle through each xml element at this level + foreach ($xmlarray as $xml_elem) + { + + // Add the indent, then the cleaned tag name to the output + $output_xml_text .= str_repeat ( " ", $indentlevel ) . "<" . xml_UTF8_clean( $xml_elem['tag'] ); + + // Check if there are any attributes for this tag + if (array_key_exists('attributes',$xml_elem)) + { + // There are attributes + // Cycle through each attribute for this tag + foreach ($xml_elem['attributes'] as $xml_attr_name => $xml_attr_val) + { + // Add the cleaned attribute name, and cleaned attribute value to the output + $output_xml_text .= " ". xml_UTF8_clean( $xml_attr_name ) ." ='" . xml_UTF8_clean( $xml_attr_val ) ."'"; + } + } + + // Add the 'greater-than' to close this tag to the output + $output_xml_text .= ">"; + + // Check if this element has any text inside it. + if (array_key_exists('value',$xml_elem) ) + { + // There is text for this element - clean it and add it to the output + $output_xml_text .= xml_UTF8_clean( $xml_elem['value'] ); + } + + // Check if there are any lower levels contained by this element + if (array_key_exists('children',$xml_elem) ) + { + // There are sub-elements for this element + + // Add a newline to the output, so the sub-elements start on a fresh line + $output_xml_text .= "\n"; + + // Recursively call this function to output the sub-elements, and add the result to the output + $output_xml_text .= write_xml_array_to_text( $xml_elem['children'], $indentlevel + 1 ); + + // Add an indent to the output for the closing tag, since we are on a new line due to the sub-elements + $output_xml_text .= str_repeat ( " ", $indentlevel ); + } + + // Add the cleaned closing tag to the output + $output_xml_text .= "</" .xml_UTF8_clean($xml_elem['tag']) . ">\n"; + } + + // Return the XML text + return $output_xml_text; +} + +/****************************************************************************** +* End of Function: write_xml_array_to_text +******************************************************************************/ + + + + + + + + + + + + + + + + + + + + + + + +/****************************************************************************** +* +* INTERNAL FUNCTIONS +* +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Internal Function: xml_get_children +* +* Description: Used by the read_xml_array_from_text function. +* This function recursively converts the values retrieved from +* the xml_parse_into_struct function into a tree structure array, +* which is much more useful and easier to use. +* +* Parameters: input_xml_array - the flat array of XML elements retrieved +* from xml_parse_into_struct +* $item_num - the number of the element at which the conversion +* should start (usually zero when called from another +* function, this is used for recursion) +* +* Returns: children - the tree structure array containing XML elements +* FALSE - if an error occured +* +******************************************************************************/ + +function xml_get_children( &$input_xml_array, &$item_num ) +{ + + // Make an array to receive the output XML tree structure + $children = array(); + + + // Cycle through all the elements of the input XML array + while ( $item_num < count( $input_xml_array ) ) + { + // Retrieve the current array element + $v = &$input_xml_array[ $item_num++ ]; + + // Check what type of XML array element this is, and process accordingly + + switch ( $v['type'] ) + { + case 'cdata': // This is a non parsed Character Data tag + case 'complete': // This is a pair of XML matching tags possibly with text (but no tags) inside + $children[] = xml_get_child( $v ); + break; + + case 'open': // This is a single opening tag + // Recursively get the children for this opening tag + $children[] = xml_get_child( $v, xml_get_children( $input_xml_array, $item_num ) ); + break; // This is a single opening tag + + case 'close': // This is a single closing tag + break 2; // leave "while" loop (and the function) + } + } + + // Return the results + return $children; +} + +/****************************************************************************** +* End of Function: xml_get_children +******************************************************************************/ + + +/****************************************************************************** +* +* Internal Function: xml_get_child +* +* Description: Used by the xml_get_children function. +* Takes an element from an array provided by xml_parse_into_struct +* and returns an element for a tree structure array +* +* Parameters: input_xml_item - the item from the array provided by xml_parse_into_struct +* children - an array of sub-elements to be added to the tree +* structure array. Null or missing value indicate no +* sub-elements are to be added. +* +* Returns: child - the element for a tree structure array +* FALSE - if an error occured +* +******************************************************************************/ + +function xml_get_child( &$input_xml_item, $children = NULL ) +{ + // Create an array to receive the child structure + $child = array(); + + // If the input item has the 'tag' element set, copy it to the child + if ( isset( $input_xml_item['tag'] ) ) + { + $child['tag'] = $input_xml_item['tag'] ; + } + + // If the input item has the 'value' element set, copy it to the child + if ( isset( $input_xml_item['value'] ) ) + { + $child['value'] = $input_xml_item['value'] ; + } + + // If the input item has the 'attributes' element set, copy it to the child + if ( isset( $input_xml_item['attributes'] ) ) + { + $child['attributes'] = $input_xml_item['attributes']; + } + + // If children have been specified, add them to the child + if ( is_array( $children ) ) + { + $child['children'] = $children; + } + + // Return the child structure + return $child; +} + +/****************************************************************************** +* End of Function: xml_get_children +******************************************************************************/ + + + + + + + + + + + + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/XMP.php b/includes/jpeg_metadata_tk/XMP.php new file mode 100644 index 0000000..0948be1 --- /dev/null +++ b/includes/jpeg_metadata_tk/XMP.php @@ -0,0 +1,1063 @@ +<?php + +/****************************************************************************** +* +* Filename: XMP.php +* +* Description: Provides functions for reading and writing information to/from +* the 'App 1' Extensible Metadata Platform (XMP) segment of JPEG +* format files. This XMP segment is XML based and contains the +* Resource Description Framework (RDF) data, which itself can +* contain the Dublin Core Metadata Initiative (DCMI) information. +* +* Author: Evan Hunter +* +* Date: 27/7/2004 +* +* Project: JPEG Metadata +* +* Revision: 1.10 +* +* Changes: 1.00 -> 1.04 : changed put_IPTC to fix a bug preventing the correct +* insertion of a XMP block where none existed previously +* +* 1.04 -> 1.10 : changed put_XMP_text to fix some array indexes which were missing qoutes +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + +include_once 'XML.php'; + + + + + +/****************************************************************************** +* +* Function: get_XMP_text +* +* Description: Retrieves the Extensible Metadata Platform (XMP) information +* from an App1 JPEG segment and returns the raw XML text as a +* string. This includes the Resource Description Framework (RDF) +* information and may include Dublin Core Metadata Initiative (DCMI) +* information. Uses information supplied by the get_jpeg_header_data +* function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* +* Returns: xmp_data - the string of raw XML text +* FALSE - if an APP 1 XMP segment could not be found, +* or if an error occured +* +******************************************************************************/ + +function get_XMP_text( $jpeg_header_data ) +{ + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP1 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) + { + // And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) , + if( strncmp ( $jpeg_header_data[$i]['SegData'], "http://ns.adobe.com/xap/1.0/\x00", 29) == 0 ) + { + // Found a XMP/RDF block + // Return the XMP text + $xmp_data = substr ( $jpeg_header_data[$i]['SegData'], 29 ); + + return $xmp_data; + } + } + } + return FALSE; +} + +/****************************************************************************** +* End of Function: get_XMP_text +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: put_XMP_text +* +* Description: Adds or modifies the Extensible Metadata Platform (XMP) information +* in an App1 JPEG segment. If a XMP segment already exists, it is +* replaced, otherwise a new one is inserted, using the supplied data. +* Uses information supplied by the get_jpeg_header_data function +* +* Parameters: jpeg_header_data - a JPEG header data array in the same format +* as from get_jpeg_header_data +* newXMP - a string containing the XMP text to be stored in the XMP +* segment. Should be constructed using the write_XMP_array_to_text +* function +* +* Returns: jpeg_header_data - the JPEG header data array with the +* XMP segment added. +* FALSE - if an error occured +* +******************************************************************************/ + +function put_XMP_text( $jpeg_header_data, $newXMP ) +{ + //Cycle through the header segments + for( $i = 0; $i < count( $jpeg_header_data ); $i++ ) + { + // If we find an APP1 header, + if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 ) + { + // And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) , + if( strncmp ( $jpeg_header_data[$i]['SegData'], "http://ns.adobe.com/xap/1.0/\x00", 29) == 0 ) + { + // Found a preexisting XMP/RDF block - Replace it with the new one and return. + $jpeg_header_data[$i]['SegData'] = "http://ns.adobe.com/xap/1.0/\x00" . $newXMP; + return $jpeg_header_data; + } + } + } + + // No pre-existing XMP/RDF found - insert a new one after any pre-existing APP0 or APP1 blocks + // Change: changed to initialise $i properly as of revision 1.04 + $i = 0; + // Loop until a block is found that isn't an APP0 or APP1 + while ( ( $jpeg_header_data[$i]['SegName'] == "APP0" ) || ( $jpeg_header_data[$i]['SegName'] == "APP1" ) ) + { + $i++; + } + + + + // Insert a new XMP/RDF APP1 segment at the specified point. + // Change: changed to properly construct array element as of revision 1.04 - requires two array statements not one, requires insertion at $i, not $i - 1 + array_splice($jpeg_header_data, $i, 0, array( array( "SegType" => 0xE1, + "SegName" => "APP1", + "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ], + "SegData" => "http://ns.adobe.com/xap/1.0/\x00" . $newXMP ) ) ); + + // Return the headers with the new segment inserted + return $jpeg_header_data; +} + +/****************************************************************************** +* End of Function: put_XMP_text +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: read_XMP_array_from_text +* +* Description: An alias for read_xml_array_from_text. +* Parses a string containing XMP data (XML), and returns the resulting +* tree structure array, which contains all the XMP (XML) information. +* Note: White space and comments in the XMP data (XML) are ignored +* Note: All text information contained in the tree structure +* is encoded as Unicode UTF-8. Hence text will appear as +* normal ASCII except where there is an extended character. +* +* Parameters: xmptext - a string containing the XMP data (XML) to be parsed +* +* Returns: output - the tree structure array containing the XMP (XML) information +* FALSE - if an error occured +* +******************************************************************************/ + +function read_XMP_array_from_text( $xmptext ) +{ + return read_xml_array_from_text( $xmptext ); +} + +/****************************************************************************** +* End of Function: read_XMP_array_from_text +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Function: write_XMP_array_to_text +* +* Description: Takes a tree structure array containing XMP (in the same format +* as returned by read_XMP_array_from_text, and constructs a string +* containing the equivalent XMP, including the XMP Packet header +* and trailer. Produces XMP text which has correct indents, encoded +* using UTF-8. +* Note: All text information contained in the tree structure +* can be either 7-bit ASCII or encoded as Unicode UTF-8, +* since UTF-8 passes 7-bit ASCII text unchanged. +* +* Parameters: xmparray - the tree structure array containing the information to +* be converted to XMP text +* +* Returns: output_XMP_text - the string containing the equivalent XMP text +* +******************************************************************************/ + +function write_XMP_array_to_text( $xmparray ) +{ + // Add the XMP packet header + // The sequence 0xEFBBBF is the UTF-8 encoded version of the Unicode “zero + // width non-breaking space character” (U+FEFF), which is used for detecting + // whether UTF-16 or UTF-8 is being used. + $output_XMP_text = "<?xpacket begin='\xef\xbb\xbf' id='W5M0MpCehiHzreSzNTczkc9d'?>\n"; + + // Photoshop Seems to add this, but there doesn't appear to be + // any information on what it means + // TODO : XMP, Find out what the adobe-xap-filters tag means + $output_XMP_text .= "<?adobe-xap-filters esc=\"CR\"?>\n"; + + // Add the XML text + $output_XMP_text .= write_xml_array_to_text( $xmparray, 0 ); + + + // The XMP standard recommends adding 2-4k of white space at the + // end for in place editing, so we will add it to the XML now + $output_XMP_text .= str_repeat(" \n", 30); + + // Add the XMP packet trailer + $output_XMP_text .= "<?xpacket end='w'?>"; + + // Return the resulting XMP text + return $output_XMP_text; +} + +/****************************************************************************** +* End of Function: write_XMP_array_to_text +******************************************************************************/ + + + + + + +/****************************************************************************** +* +* Function: Interpret_XMP_to_HTML +* +* Description: Generates html showing the information contained in an Extensible +* Metadata Platform (XMP) tree structure array, as retrieved +* with read_XMP_array_from_text +* +* Parameters: XMP_array - a XMP tree structure array as from read_XMP_array_from_text +* +* Returns: output - the HTML string +* +******************************************************************************/ + +function Interpret_XMP_to_HTML( $XMP_array ) +{ + // Create a string to receive the output html + $output =""; + + // Check if the XMP tree structure array is valid + if ( $XMP_array !== FALSE ) + { + // Check if there is a rdf:RDF tag at either the first or second level + if ( ( $XMP_array[0]['tag'] == "x:xapmeta" ) && ( $XMP_array[0]['children'][0]['tag'] == "rdf:RDF" ) ) + { + // RDF found at second level - Save it's position + $RDF_Contents = &$XMP_array[0]['children'][0]['children']; + } + else if ( ( $XMP_array[0]['tag'] == "x:xmpmeta" ) && ( $XMP_array[0]['children'][0]['tag'] == "rdf:RDF" ) ) + { + // RDF found at second level - Save it's position + $RDF_Contents = &$XMP_array[0]['children'][0]['children']; + } + else if ( $XMP_array[0]['tag'] == "rdf:RDF" ) + { + // RDF found at first level - Save it's position + $RDF_Contents = &$XMP_array[0]['children']; + } + else + { + // RDF section not found - abort + return ""; + } + + // Add heading to html output + $output .= "<h2 class=\"XMP_Main_Heading\">Contains Extensible Metadata Platform (XMP) / Resource Description Framework (RDF) Information</h2>\n"; + + // Cycle through each of the items in the RDF tree array, and process them + foreach ($RDF_Contents as $RDF_Item) + { + // Check if the item is a rdf:Description tag - these are the only ones that can be processed + + if ( ( $RDF_Item['tag'] == "rdf:Description" ) && ( array_key_exists( 'children', $RDF_Item ) ) ) + { + // Item is a rdf:Description tag. + + // Cycle through each of the attributes for this tag, looking + // for a xmlns: attribute, which tells us what Namespace the + // sub-items will be in. + foreach( $RDF_Item['attributes'] as $key => $val ) + { + // Check for the xmlns: namespace attribute + if ( substr( $key,0,6) == "xmlns:" ) + { + // Found a xmlns: attribute + // Extract the namespace string + // Add heading to the HTML according to which Namespace the RDF items have + switch ( substr( $key,6) ) + { + case "photoshop": + $output .= "<h3 class=\"XMP_Secondary_Heading\">Photoshop RDF Segment</h3>\n"; + break; + case "xapBJ": + $output .= "<h3 class=\"XMP_Secondary_Heading\">Basic Job Ticket RDF Segment</h3>\n"; + break; + case "xapMM": + $output .= "<h3 class=\"XMP_Secondary_Heading\">Media Management RDF Segment</h3>\n"; + break; + case "xapRights": + $output .= "<h3 class=\"XMP_Secondary_Heading\">Rights Management RDF Segment</h3>\n"; + break; + case "dc": + $output .= "<h3 class=\"XMP_Secondary_Heading\">Dublin Core Metadata Initiative RDF Segment</h3>\n"; + break; + case "xmp": + case "xap": + $output .= "<h3 class=\"XMP_Secondary_Heading\">XMP Basic Segment</h3>\n"; + break; + case "xmpTPg": + $output .= "<h3 class=\"XMP_Secondary_Heading\">XMP Paged-Text Segment</h3>\n"; + break; + case "xmpTPg": + $output .= "<h3 class=\"XMP_Secondary_Heading\">Adobe PDF Segment</h3>\n"; + break; + case "tiff": + $output .= "<h3 class=\"XMP_Secondary_Heading\">XMP - embedded TIFF Segment</h3>\n"; + break; + case "exif": + $output .= "<h3 class=\"XMP_Secondary_Heading\">XMP - embedded EXIF Segment</h3>\n"; + break; + case "xapGImg": // Sub Category - Do nothing + break; + case "stDim": // Sub Category - Do nothing + break; + case "stEvt": // Sub Category - Do nothing + break; + case "stRef": // Sub Category - Do nothing + break; + case "stVer": // Sub Category - Do nothing + break; + case "stJob": // Sub Category - Do nothing + break; + + default: + $output .= "<h3 class=\"XMP_Secondary_Heading\">Unknown RDF Segment '" . substr( $key,6) . "'</h3>\n"; + break; + } + + + } + + } + + // Add the start of the table to the HTML output + $output .= "\n<table class=\"XMP_Table\" border=1>\n"; + + + // Check if this element has sub-items + + if ( array_key_exists( 'children', $RDF_Item ) ) + { + + // Cycle through each of the sub-items + foreach( $RDF_Item['children'] as $child_item ) + { + // Get an interpretation of the sub-item's caption and value + list($tag_caption, $value_str) = Interpret_RDF_Item( $child_item ); + + // Escape the text of the caption for html + $tag_caption = HTML_UTF8_Escape( $tag_caption ); + // Escape the text of the value for html and turn newlines to <br> + $value_str = nl2br( HTML_UTF8_Escape( $value_str ) ); + + // Check if the value is empty - if it is, put a no-break-space in + // to ensure the table cell gets drawn + if ( $value_str == "" ) + { + $value_str = " "; + } + // Add the table row to the output html + $output .= "<tr class=\"XMP_Table_Row\"><td class=\"XMP_Caption_Cell\">" . $tag_caption . ":</td><td class=\"XMP_Value_Cell\">" . $value_str . "</td></tr>\n"; + } + } + + // Add the end of the table to the html + $output .= "\n</table>\n"; + + + } + else + { + // Don't know how to process tags other than rdf:Description - do nothing + } + } + + + + } + // Return the resulting HTML + return $output; +} + +/****************************************************************************** +* End of Function: Interpret_XMP_to_HTML +******************************************************************************/ + + + + + + + + + + + + + + + + + + +/****************************************************************************** +* +* INTERNAL FUNCTIONS +* +******************************************************************************/ + + + + + + + + + + + + +/****************************************************************************** +* +* Internal Function: Interpret_RDF_Item +* +* Description: Used by Interpret_XMP_to_HTML +* Used by get_RDF_field_html_value +* Used by interpret_RDF_collection +* Generates a caption and text representation of the value of a +* particular RDF item. +* +* Parameters: Item - The RDF item to evaluate +* +* Returns: tag_caption - the caption of the tag +* value_str - the text representation of the value +* +******************************************************************************/ + +function Interpret_RDF_Item( $Item ) +{ + + // TODO: Many RDF items have not been tested - only photoshop 7.0 and CS items + + // Create a string to receive the HTML output + $value_str = ""; + + // Check if the item has is in the lookup table of tag captions + if ( array_key_exists( $Item['tag'], $GLOBALS[ 'XMP_tag_captions' ] ) ) + { + // Item is in list of captions, get the caption + $tag_caption = $GLOBALS[ 'XMP_tag_captions' ][ $Item['tag'] ]; + } + else + { + // Item has no caption - make one + $tag_caption = "Unknown field " . $Item['tag']; + } + + + // Process specially the item according to it's tag + switch ( $Item['tag'] ) + { + + case "photoshop:DateCreated": // This is in year month day order + // Extract the year,month and day + list( $year, $month, $day ) = sscanf( $Item['value'], "%d-%d-%d" ); + // Make a new date string with Day, Month, Year + $value_str = "$day/$month/$year"; + break; + + default : + $value_str = get_RDF_field_html_value( $Item ); + break; + } + + + + + // Return the captiona and value + return array($tag_caption, $value_str); +} + + +/****************************************************************************** +* End of Function: Interpret_RDF_Item +******************************************************************************/ + + + + + +/****************************************************************************** +* +* Internal Function: get_RDF_field_html_value +* +* Description: Attempts to build a text representation of the value of an RDF +* item. This includes handling any collections or sub-resources. +* +* Parameters: rdf_item - The RDF item to evaluate +* +* Returns: output_str - the text representation of the field value +* +******************************************************************************/ + +function get_RDF_field_html_value( $rdf_item ) +{ + // Create a string to receive the output text + $output_str = ""; + + // Check if the item has a value + if ( array_key_exists( 'value', $rdf_item ) ) + { + // The item does have a value - add it to the text + $output_str .= $rdf_item['value']; + } + + // Check if the item has any attributes + if ( array_key_exists( 'attributes', $rdf_item ) ) + { + // Cycle through each of the attributes + foreach( $rdf_item['attributes'] as $key => $val ) + { + // Check if this attribute is rdf:parseType = 'Resource' i.e. a sub-resource indicator + if ( ( $key == "rdf:parseType" ) && ( $val == "Resource" ) ) + { + // This item has a attribute indicating sub-resources + // Check that the item has sub items + if ( array_key_exists( 'children', $rdf_item ) ) + { + // The item does have sub-items, + // Cycle through each, Interpreting them and adding the result to the output text + foreach( $rdf_item['children'] as $child ) + { + list($tag_caption, $value_str) = Interpret_RDF_Item( $child ); + $output_str .= "$tag_caption = $value_str\n"; + } + // The output text will have an extra \n on it - remove it + $output_str = rtrim( $output_str ); + } + } + } + } + // If the item did not have sub-resources, it may still have sub-items - check for this + else if ( array_key_exists( 'children', $rdf_item ) ) + { + // Non-resource Sub-items found, Cycle through each + foreach( $rdf_item['children'] as $child_item ) + { + // Check if this sub-item has a tag + if ( array_key_exists( 'tag', $child_item ) ) + { + // Sub item has a tag, Process it according to the tag + switch ( $child_item[ 'tag' ] ) + { + // Collections + case "rdf:Alt": + $output_str .= "List of Alternates:\n"; + $output_str .= interpret_RDF_collection( $child_item ); + break; + + case "rdf:Bag": + $output_str .= "Unordered List:\n"; + $output_str .= interpret_RDF_collection( $child_item ); + break; + + case "rdf:Seq": + $output_str .= "Ordered List:\n"; + $output_str .= interpret_RDF_collection( $child_item ); + break; + + // Sub-Resource + case "rdf:Description": + // Check that the item has sub items + if ( array_key_exists( 'children', $child_item ) ) + { + // The item does have sub-items, + // Cycle through each, Interpreting them and adding the result to the output text + foreach( $child_item['children'] as $child ) + { + list($tag_caption, $value_str) = Interpret_RDF_Item( $child ); + $output_str .= "$tag_caption = $value_str\n"; + } + // The output text will have an extra \n on it - remove it + $output_str = rtrim( $output_str ); + } + break; + + // Other + default: + $output_str .= "Unknown Sub Item type:". $child_item[ 'tag' ]. "\n"; + break; + } + } // sub-item Has no tags, look for a value + else if ( array_key_exists( 'value', $child_item ) ) + { + $output_str .= $rdf_item['value'] . "\n"; + } + else + { + // no info - do nothing + } + + } + } + + // return the resulting value string + return $output_str; +} + +/****************************************************************************** +* End of Function: get_RDF_field_html_value +******************************************************************************/ + + + + + + + + +/****************************************************************************** +* +* Internal Function: interpret_RDF_collection +* +* Description: Attempts to build a text representation of the value of an RDF +* collection item. This includes handling any sub-collections or +* sub-resources. +* +* Parameters: rdf_item - The RDF collection item to evaluate +* +* Returns: output_str - the text representation of the collection value +* +******************************************************************************/ + +function interpret_RDF_collection( $item ) +{ + // Create a string to receive the output + $output_str = ""; + + // Check if the collection item has sub-items + if ( array_key_exists( 'children', $item ) ) + { + + // Cycle through each of the sub-items + foreach( $item['children'] as $list_item ) + { + // Check that the sub item has a tag, and don't process it if it doesn't + if ( ! array_key_exists( 'tag', $list_item ) ) + { + continue 1; + } + + // Check that the sub-item tag is either rdf:li or rdf:_1 .... + // This signifies it is a list item of the collection + if ( ( $list_item['tag'] == "rdf:li" ) || + ( preg_match ( "rdf:_\d+", $list_item['tag'] ) == 1 ) ) + { + // A List item has been found + // Check if there are sub-resources, + // starting by checking if there are attributes + if ( array_key_exists( 'attributes', $list_item ) ) + { + // Cycle through each of the attributes + foreach( $list_item['attributes'] as $key => $val ) + { + // Check if this attribute is rdf:parseType = 'Resource' i.e. a sub-resource indicator + if ( ( $key == "rdf:parseType" ) && ( $val == "Resource" ) ) + { + // This item has a attribute indicating sub-resources + // Check that the item has sub items + if ( array_key_exists( 'children', $list_item ) ) + { + // The item does have sub-items, + // Cycle through each, Interpreting them and adding the result to the output text + foreach( $list_item['children'] as $child ) + { + list($tag_caption, $value_str) = Interpret_RDF_Item( $child ); + $output_str .= "$tag_caption = $value_str\n"; + } + // The output text will have an extra \n on it - remove it + $output_str = rtrim( $output_str ); + } + } + } + } + + // Check if the list item has a value + if ( array_key_exists( 'value', $list_item ) ) + { + // Value found, add it to the output + $output_str .= get_RDF_field_html_value( $list_item ) . "\n"; + } + + } + } + // The list of sub-items formed will have a trailing \n, remove it. + $output_str = rtrim( $output_str ); + + } + else + { + // No sub-items in collection - can't do anything + } + + // Return the output value + return $output_str; + +} + +/****************************************************************************** +* End of Function: interpret_RDF_collection +******************************************************************************/ + + + + + + + + + + + + + + + +/****************************************************************************** +* Global Variable: XMP_tag_captions +* +* Contents: The Captions of the known XMP fields, indexed by their field name +* +******************************************************************************/ + +$GLOBALS[ 'XMP_tag_captions' ] = array ( + +"dc:contributor" => "Other Contributor(s)", +"dc:coverage" => "Coverage (scope)", +"dc:creator" => "Creator(s) (Authors)", +"dc:date" => "Date", +"dc:description" => "Description (Caption)", +"dc:format" => "MIME Data Format", +"dc:identifier" => "Unique Resource Identifer", +"dc:language" => "Language(s)", +"dc:publisher" => "Publisher(s)", +"dc:relation" => "Relations to other documents", +"dc:rights" => "Rights Statement", +"dc:source" => "Source (from which this Resource is derived)", +"dc:subject" => "Subject and Keywords", +"dc:title" => "Title", +"dc:type" => "Resource Type", + +"xmp:Advisory" => "Externally Editied Properties", +"xmp:BaseURL" => "Base URL for relative URL's", +"xmp:CreateDate" => "Original Creation Date", +"xmp:CreatorTool" => "Creator Tool", +"xmp:Identifier" => "Identifier(s)", +"xmp:MetadataDate" => "Metadata Last Modify Date", +"xmp:ModifyDate" => "Resource Last Modify Date", +"xmp:Nickname" => "Nickname", +"xmp:Thumbnails" => "Thumbnails", + +"xmpidq:Scheme" => "Identification Scheme", + +// These are not in spec but Photoshop CS seems to use them +"xap:Advisory" => "Externally Editied Properties", +"xap:BaseURL" => "Base URL for relative URL's", +"xap:CreateDate" => "Original Creation Date", +"xap:CreatorTool" => "Creator Tool", +"xap:Identifier" => "Identifier(s)", +"xap:MetadataDate" => "Metadata Last Modify Date", +"xap:ModifyDate" => "Resource Last Modify Date", +"xap:Nickname" => "Nickname", +"xap:Thumbnails" => "Thumbnails", +"xapidq:Scheme" => "Identification Scheme", + + +"xapRights:Certificate" => "Certificate", +"xapRights:Copyright" => "Copyright", +"xapRights:Marked" => "Marked", +"xapRights:Owner" => "Owner", +"xapRights:UsageTerms" => "Legal Terms of Usage", +"xapRights:WebStatement" => "Web Page describing rights statement (Owner URL)", + +"xapMM:ContainedResources" => "Contained Resources", +"xapMM:ContributorResources" => "Contributor Resources", +"xapMM:DerivedFrom" => "Derived From", +"xapMM:DocumentID" => "Document ID", +"xapMM:History" => "History", +"xapMM:LastURL" => "Last Written URL", +"xapMM:ManagedFrom" => "Managed From", +"xapMM:Manager" => "Asset Management System", +"xapMM:ManageTo" => "Manage To", +"xapMM:xmpMM:ManageUI" => "Managed Resource URI", +"xapMM:ManagerVariant" => "Particular Variant of Asset Management System", +"xapMM:RenditionClass" => "Rendition Class", +"xapMM:RenditionParams" => "Rendition Parameters", +"xapMM:RenditionOf" => "Rendition Of", +"xapMM:SaveID" => "Save ID", +"xapMM:VersionID" => "Version ID", +"xapMM:Versions" => "Versions", + +"xapBJ:JobRef" => "Job Reference", + +"xmpTPg:MaxPageSize" => "Largest Page Size", +"xmpTPg:NPages" => "Number of pages", + +"pdf:Keywords" => "Keywords", +"pdf:PDFVersion" => "PDF file version", +"pdf:Producer" => "PDF Creation Tool", + +"photoshop:AuthorsPosition" => "Authors Position", +"photoshop:CaptionWriter" => "Caption Writer", +"photoshop:Category" => "Category", +"photoshop:City" => "City", +"photoshop:Country" => "Country", +"photoshop:Credit" => "Credit", +"photoshop:DateCreated" => "Creation Date", +"photoshop:Headline" => "Headline", +"photoshop:History" => "History", // Not in XMP spec +"photoshop:Instructions" => "Instructions", +"photoshop:Source" => "Source", +"photoshop:State" => "State", +"photoshop:SupplementalCategories" => "Supplemental Categories", +"photoshop:TransmissionReference" => "Technical (Transmission) Reference", +"photoshop:Urgency" => "Urgency", + + +"tiff:ImageWidth" => "Image Width", +"tiff:ImageLength" => "Image Length", +"tiff:BitsPerSample" => "Bits Per Sample", +"tiff:Compression" => "Compression", +"tiff:PhotometricInterpretation" => "Photometric Interpretation", +"tiff:Orientation" => "Orientation", +"tiff:SamplesPerPixel" => "Samples Per Pixel", +"tiff:PlanarConfiguration" => "Planar Configuration", +"tiff:YCbCrSubSampling" => "YCbCr Sub-Sampling", +"tiff:YCbCrPositioning" => "YCbCr Positioning", +"tiff:XResolution" => "X Resolution", +"tiff:YResolution" => "Y Resolution", +"tiff:ResolutionUnit" => "Resolution Unit", +"tiff:TransferFunction" => "Transfer Function", +"tiff:WhitePoint" => "White Point", +"tiff:PrimaryChromaticities" => "Primary Chromaticities", +"tiff:YCbCrCoefficients" => "YCbCr Coefficients", +"tiff:ReferenceBlackWhite" => "Black & White Reference", +"tiff:DateTime" => "Date & Time", +"tiff:ImageDescription" => "Image Description", +"tiff:Make" => "Make", +"tiff:Model" => "Model", +"tiff:Software" => "Software", +"tiff:Artist" => "Artist", +"tiff:Copyright" => "Copyright", + + +"exif:ExifVersion" => "Exif Version", +"exif:FlashpixVersion" => "Flash pix Version", +"exif:ColorSpace" => "Color Space", +"exif:ComponentsConfiguration" => "Components Configuration", +"exif:CompressedBitsPerPixel" => "Compressed Bits Per Pixel", +"exif:PixelXDimension" => "Pixel X Dimension", +"exif:PixelYDimension" => "Pixel Y Dimension", +"exif:MakerNote" => "Maker Note", +"exif:UserComment" => "User Comment", +"exif:RelatedSoundFile" => "Related Sound File", +"exif:DateTimeOriginal" => "Date & Time of Original", +"exif:DateTimeDigitized" => "Date & Time Digitized", +"exif:ExposureTime" => "Exposure Time", +"exif:FNumber" => "F Number", +"exif:ExposureProgram" => "Exposure Program", +"exif:SpectralSensitivity" => "Spectral Sensitivity", +"exif:ISOSpeedRatings" => "ISO Speed Ratings", +"exif:OECF" => "Opto-Electronic Conversion Function", +"exif:ShutterSpeedValue" => "Shutter Speed Value", +"exif:ApertureValue" => "Aperture Value", +"exif:BrightnessValue" => "Brightness Value", +"exif:ExposureBiasValue" => "Exposure Bias Value", +"exif:MaxApertureValue" => "Max Aperture Value", +"exif:SubjectDistance" => "Subject Distance", +"exif:MeteringMode" => "Metering Mode", +"exif:LightSource" => "Light Source", +"exif:Flash" => "Flash", +"exif:FocalLength" => "Focal Length", +"exif:SubjectArea" => "Subject Area", +"exif:FlashEnergy" => "Flash Energy", +"exif:SpatialFrequencyResponse" => "Spatial Frequency Response", +"exif:FocalPlaneXResolution" => "Focal Plane X Resolution", +"exif:FocalPlaneYResolution" => "Focal Plane Y Resolution", +"exif:FocalPlaneResolutionUnit" => "Focal Plane Resolution Unit", +"exif:SubjectLocation" => "Subject Location", +"exif:SensingMethod" => "Sensing Method", +"exif:FileSource" => "File Source", +"exif:SceneType" => "Scene Type", +"exif:CFAPattern" => "Colour Filter Array Pattern", +"exif:CustomRendered" => "Custom Rendered", +"exif:ExposureMode" => "Exposure Mode", +"exif:WhiteBalance" => "White Balance", +"exif:DigitalZoomRatio" => "Digital Zoom Ratio", +"exif:FocalLengthIn35mmFilm" => "Focal Length In 35mm Film", +"exif:SceneCaptureType" => "Scene Capture Type", +"exif:GainControl" => "Gain Control", +"exif:Contrast" => "Contrast", +"exif:Saturation" => "Saturation", +"exif:Sharpness" => "Sharpness", +"exif:DeviceSettingDescription" => "Device Setting Description", +"exif:SubjectDistanceRange" => "Subject Distance Range", +"exif:ImageUniqueID" => "Image Unique ID", +"exif:GPSVersionID" => "GPS Version ID", +"exif:GPSLatitude" => "GPS Latitude", +"exif:GPSLongitude" => "GPS Longitude", +"exif:GPSAltitudeRef" => "GPS Altitude Reference", +"exif:GPSAltitude" => "GPS Altitude", +"exif:GPSTimeStamp" => "GPS Time Stamp", +"exif:GPSSatellites" => "GPS Satellites", +"exif:GPSStatus" => "GPS Status", +"exif:GPSMeasureMode" => "GPS Measure Mode", +"exif:GPSDOP" => "GPS Degree Of Precision", +"exif:GPSSpeedRef" => "GPS Speed Reference", +"exif:GPSSpeed" => "GPS Speed", +"exif:GPSTrackRef" => "GPS Track Reference", +"exif:GPSTrack" => "GPS Track", +"exif:GPSImgDirectionRef" => "GPS Image Direction Reference", +"exif:GPSImgDirection" => "GPS Image Direction", +"exif:GPSMapDatum" => "GPS Map Datum", +"exif:GPSDestLatitude" => "GPS Destination Latitude", +"exif:GPSDestLongitude" => "GPS Destnation Longitude", +"exif:GPSDestBearingRef" => "GPS Destination Bearing Reference", +"exif:GPSDestBearing" => "GPS Destination Bearing", +"exif:GPSDestDistanceRef" => "GPS Destination Distance Reference", +"exif:GPSDestDistance" => "GPS Destination Distance", +"exif:GPSProcessingMethod" => "GPS Processing Method", +"exif:GPSAreaInformation" => "GPS Area Information", +"exif:GPSDifferential" => "GPS Differential", + +"stDim:w" => "Width", +"stDim:h" => "Height", +"stDim:unit" => "Units", + +"xapGImg:height" => "Height", +"xapGImg:width" => "Width", +"xapGImg:format" => "Format", +"xapGImg:image" => "Image", + +"stEvt:action" => "Action", +"stEvt:instanceID" => "Instance ID", +"stEvt:parameters" => "Parameters", +"stEvt:softwareAgent" => "Software Agent", +"stEvt:when" => "When", + +"stRef:instanceID" => "Instance ID", +"stRef:documentID" => "Document ID", +"stRef:versionID" => "Version ID", +"stRef:renditionClass" => "Rendition Class", +"stRef:renditionParams" => "Rendition Parameters", +"stRef:manager" => "Asset Management System", +"stRef:managerVariant" => "Particular Variant of Asset Management System", +"stRef:manageTo" => "Manage To", +"stRef:manageUI" => "Managed Resource URI", + +"stVer:comments" => "", +"stVer:event" => "", +"stVer:modifyDate" => "", +"stVer:modifier" => "", +"stVer:version" => "", + + + +"stJob:name" => "Job Name", +"stJob:id" => "Unique Job ID", +"stJob:url" => "URL for External Job Management File", + +// Exif Flash +"exif:Fired" => "Fired", +"exif:Return" => "Return", +"exif:Mode" => "Mode", +"exif:Function" => "Function", +"exif:RedEyeMode" => "Red Eye Mode", + +// Exif OECF/SFR +"exif:Columns" => "Columns", +"exif:Rows" => "Rows", +"exif:Names" => "Names", +"exif:Values" => "Values", + +// Exif CFAPattern +"exif:Columns" => "Columns", +"exif:Rows" => "Rows", +"exif:Values" => "Values", + + +// Exif DeviceSettings +"exif:Columns" => "Columns", +"exif:Rows" => "Rows", +"exif:Settings" => "Settings", + + + +); + +/****************************************************************************** +* End of Global Variable: XMP_tag_captions +******************************************************************************/ + + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf b/includes/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf Binary files differnew file mode 100644 index 0000000..711e2a7 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf diff --git a/includes/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php b/includes/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php new file mode 100644 index 0000000..b8a9f24 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php @@ -0,0 +1,248 @@ +<html> + +<!--*************************************************************************** +* +* Filename: Edit_File_Info_Example.php +* +* Description: An example file showing how edit_file_info allows the user to edit +* the metadata of an image over the internet in the same way +* that Photoshop edits 'File Info' data +* +* Author: Evan Hunter +* +* Date: 17/11/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* Changes: 1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +***************************************************************************--> + + <head> + + <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css"> + <STYLE TYPE="text/css" MEDIA="screen, print, projection"> + <!-- + + BODY { background-color:#505050; color:#F0F0F0 } + a { color:orange } + .EXIF_Main_Heading { color:red } + .EXIF_Secondary_Heading{ color: orange} + .EXIF_Table { border-collapse: collapse ; border: 1px solid #909000} + .EXIF_Table tbody td{border-width: 1px; border-style:solid; border-color: #909000;} + + --> + </STYLE> + + + <?php + // Turn off Error Reporting + error_reporting ( 0 ); + + include 'Toolkit_Version.php'; // Change: added as of version 1.11 + + // Retrieve the JPEG image filename from the http url request + if ( ( !array_key_exists( 'jpeg_fname', $GLOBALS['HTTP_GET_VARS'] ) ) || + ( $GLOBALS['HTTP_GET_VARS']['jpeg_fname'] == "" ) ) + { + echo "<title>No image filename defined</title>\n"; + echo "</head>\n"; + echo "<body>\n"; + echo "<p>No image filename defined - use GET method with field: jpeg_fname</p>\n"; + echo "<p><a href=\"http://www.ozhiker.com/electronics/pjmt/\" >PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter</a></p>\n"; // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 + echo "</body>\n"; + exit( ); + + } + else + { + $filename = $GLOBALS['HTTP_GET_VARS']['jpeg_fname']; + } + ?> + + + <title>Edit Photoshop File Info details for <?php $filename ?></title> + </head> + + <body > + <p>Powered by: <a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p> <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 --> + <br> + <br> + + + <?php + // Output a heading + echo "<H1>Edit Photoshop File Info details for $filename</H1>"; + + // Output a link to display the full metadata + echo "<p><a href=\"Example.php?jpeg_fname=" . $filename . "\" >View Full Metatdata Information</a></p>\n"; + + + // Display a small copy of the image + echo "<p><img src=\"$filename\" height=\"50%\"></p>"; + + // Define defaults for the fields - These are only used where the image has blank fields + $default_ps_file_info_array = array ( + 'title' => "", + 'author' => "Evan Hunter", + 'authorsposition' => "", + 'caption' => "", + 'captionwriter' => "Evan Hunter", + 'jobname' => "", + 'copyrightstatus' => "Copyrighted Work", + 'copyrightnotice' => "Copyright (c) Evan Hunter 2004", + 'ownerurl' => "http://www.ozhiker.com", + 'keywords' => array(), + 'category' => "", + 'supplementalcategories'=> array(), + 'date' => "", + 'city' => "", + 'state' => "Tasmania", + 'country' => "Australia", + 'credit' => "Evan Hunter", + 'source' => "Evan Hunter", + 'headline' => "", + 'instructions' => "", + 'transmissionreference' => "", + 'urgency' => "" + ); + + // outputfilename must always be defined, as it specifies the + // file which will be changed + + // These two lines create a temporary copy of the file + // which will be the one that is edited, keeping + // the original intact. - This would not be required if you wanted + // to change the original - in that case just set $outputfilename = $filename + $outputfilename = get_next_filename( ); + copy( $filename, $outputfilename ); + + + + + // Include the File Info Editor. + + include "Edit_File_Info.php"; + + ?> + + + </body> + +</html> + + + + + + + + + + + + + + + + + + +<?php + +/****************************************************************************** +* +* Function: get_next_filename +* +* Description: Simple function to cycle through temporary filenames ( a to z ) +* This means that there will only be a maximum of 26 temporary files, +* hence avoiding filling up the server or having a cron job to remove them. +* +* NOTE: This function would not normally be required, and is just +* to protect my website (and others) from filling up with +* temporary files whilst demonstrating the toolkit +* +* Parameters: none +* +* Returns: TRUE - on Success +* FALSE - on Failure +* +******************************************************************************/ + +function get_next_filename( ) +{ + // Read the letter of the next temp file from disk + $filename = file( "next_temp_file.dat" ); + // If it wasn't read - start at 'a' + if ( $filename == FALSE ) + { + $filename = 'a'; + } + else + { + $filename = $filename{0}; + } + + // Ensure the filename letter is valid + if ( ( $filename < 'a' ) || ( $filename > 'z' ) ) + { + $filename = 'a'; + } + + + // Check if the names are up to 'z' + if( $filename == 'z' ) + { + // Name is at z - the next one should be 'a' + $new_filename = 'a'; + } + else + { + // The name is not 'z' add one to it to get the next value + $new_filename = chr( ord( $filename ) + 1 ); + } + + // Write the next temp file letter back into the file + $Fhnd = fopen ("next_temp_file.dat", "w"); + fwrite ($Fhnd, $new_filename); + fclose ($Fhnd); + + // return the filename + return "temp_$filename.jpg"; +} + +/****************************************************************************** +* End of Function: get_next_filename +******************************************************************************/ + + + +?>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/Example.php b/includes/jpeg_metadata_tk/documentation/Example.php new file mode 100644 index 0000000..86ac497 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/Example.php @@ -0,0 +1,218 @@ +<html> + +<!--*************************************************************************** +* +* Filename: Example.php +* +* Description: An example of how the PHP JPEG Metadata Toolkit can be used to +* display JPEG Metadata. +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* Changes: 1.00 -> 1.10 : Changed name of GET parameter from 'filename' to 'jpeg_fname' +* to stop script-kiddies using the google command 'allinurl:*.php?filename=*' +* to find servers to attack +* Changed behavior when no filename is given, to be cleaner +* 1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php +* Changed this example file to be easily relocatable +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +***************************************************************************--> + + <head> + + <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css"> + <STYLE TYPE="text/css" MEDIA="screen, print, projection"> + <!-- + + BODY { background-color:#505050; color:#F0F0F0 } + a { color:orange } + .EXIF_Main_Heading { color:red } + .EXIF_Secondary_Heading{ color: orange} + .EXIF_Table { border-collapse: collapse ; border: 1px solid #909000} + .EXIF_Table tbody td{border-width: 1px; border-style:solid; border-color: #909000;} + + --> + </STYLE> + + + <?php + // Turn off Error Reporting + error_reporting ( 0 ); + + // Change: Allow this example file to be easily relocatable - as of version 1.11 + $Toolkit_Dir = "./"; // Ensure dir name includes trailing slash + + // Hide any unknown EXIF tags + $GLOBALS['HIDE_UNKNOWN_TAGS'] = TRUE; + + include $Toolkit_Dir . 'Toolkit_Version.php'; // Change: added as of version 1.11 + include $Toolkit_Dir . 'JPEG.php'; // Change: Allow this example file to be easily relocatable - as of version 1.11 + include $Toolkit_Dir . 'JFIF.php'; + include $Toolkit_Dir . 'PictureInfo.php'; + include $Toolkit_Dir . 'XMP.php'; + include $Toolkit_Dir . 'Photoshop_IRB.php'; + include $Toolkit_Dir . 'EXIF.php'; + + // Retrieve the JPEG image filename from the http url request + if ( ( !array_key_exists( 'jpeg_fname', $GLOBALS['HTTP_GET_VARS'] ) ) || + ( $GLOBALS['HTTP_GET_VARS']['jpeg_fname'] == "" ) ) + { + echo "<title>No image filename defined</title>\n"; + echo "</head>\n"; + echo "<body>\n"; + echo "<p>No image filename defined - use GET method with field: jpeg_fname</p>\n"; + echo "<p><a href=\"http://www.ozhiker.com/electronics/pjmt/\" >PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter</a></p>\n"; // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 + echo "</body>\n"; + exit( ); + } + else + { + $filename = $GLOBALS['HTTP_GET_VARS']['jpeg_fname']; + } + + + // Output the title + echo "<title>Metadata details for $filename</title>"; + + // Retrieve the header information + $jpeg_header_data = get_jpeg_header_data( $filename ); + + ?> + + </head> + + <body> + + <p>Interpreted using: <a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p> <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 --> + <br> + <br> + + <h1><B><U>Metadata for "<?php echo $filename; ?>"</U></B></h1> + <br> + + <!-- Output a link allowing user to edit the Photoshop File Info + Change: Allow this example file to be easily relocatable - as of version 1.11 + --> + <?php $relative_filename = get_relative_path( $filename, $Toolkit_Dir ); ?> + <h4><a href="<?php echo $Toolkit_Dir."Edit_File_Info_Example.php?jpeg_fname=$relative_filename"; ?>" >Click here to edit the Photoshop File Info for this file</a></h4> + <br> + + + + <!-- Output the information about the APP segments --> + <?php echo Generate_JPEG_APP_Segment_HTML( $jpeg_header_data ); ?> + + <BR> + <HR> + <BR> + + <!-- Output the Intrinsic JPEG Information --> + <?php echo Interpret_intrinsic_values_to_HTML( get_jpeg_intrinsic_values( $jpeg_header_data ) ); ?> + + <BR> + <HR> + <BR> + + <!-- Output the JPEG Comment --> + <?php echo Interpret_Comment_to_HTML( $jpeg_header_data ); ?> + + <BR> + <HR> + <BR> + + <!-- Output the JPEG File Interchange Format Information --> + <?php echo Interpret_JFIF_to_HTML( get_JFIF( $jpeg_header_data ), $filename ); ?> + + <BR> + <HR> + <BR> + + <!-- Output the JFIF Extension Information --> + <?php echo Interpret_JFXX_to_HTML( get_JFXX( $jpeg_header_data ), $filename ); ?> + + <BR> + <HR> + <BR> + + <!-- Output the Picture Info Text --> + <?php echo Interpret_App12_Pic_Info_to_HTML( $jpeg_header_data ); ?> + + <BR> + <HR> + <BR> + + <!-- Output the EXIF Information --> + <?php echo Interpret_EXIF_to_HTML( get_EXIF_JPEG( $filename ), $filename ); ?> + + <BR> + <HR> + <BR> + + <!-- Output the XMP Information --> + <?php echo Interpret_XMP_to_HTML( read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) ) ); ?> + + <BR> + <HR> + <BR> + + <!-- Output the Photoshop IRB (including the IPTC-NAA info --> + <?php echo Interpret_IRB_to_HTML( get_Photoshop_IRB( $jpeg_header_data ), $filename ); ?> + + <BR> + <HR> + <BR> + + <!-- Output the Meta Information --> + <?php echo Interpret_EXIF_to_HTML( get_Meta_JPEG( $filename ), $filename ); ?> + + <BR> + <HR> + <BR> + + <!-- Display the original image --> + + <h2>Original Image</h2> + <?php echo "<img src=\"$filename\">"; ?> + + + <BR> + <BR> + <BR> + <p>Interpreted using:</p> + <p><a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p> <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 --> + + </body> + +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/TIFFExample.php b/includes/jpeg_metadata_tk/documentation/TIFFExample.php new file mode 100644 index 0000000..873d18f --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/TIFFExample.php @@ -0,0 +1,121 @@ +<html> + +<!--*************************************************************************** +* +* Filename: TIFFExample.php +* +* Description: An example of how the PHP JPEG Metadata Toolkit can be used to +* display TIFF Metadata. +* +* Author: Evan Hunter +* +* Date: 30/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* Changes: 1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +***************************************************************************--> + + <head> + + <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css"> + <STYLE TYPE="text/css" MEDIA="screen, print, projection"> + <!-- + + BODY { background-color:#505050; color:#F0F0F0 } + a { color:orange } + .EXIF_Main_Heading { color:red } + .EXIF_Secondary_Heading{ color: orange} + .EXIF_Table { border-collapse: collapse ; border: 1px solid #909000} + .EXIF_Table tbody td{border-width: 1px; border-style:solid; border-color: #909000;} + + --> + </STYLE> + + + <?php + // Turn off Error Reporting + error_reporting ( 0 ); + + // Hide any unknown EXIF tags + $GLOBALS['HIDE_UNKNOWN_TAGS'] = TRUE; + + include 'Toolkit_Version.php'; + include 'EXIF.php'; + + // Retrieve the TIFF image filename from the http url request + if ( ( !array_key_exists( 'tiff_fname', $GLOBALS['HTTP_GET_VARS'] ) ) || + ( $GLOBALS['HTTP_GET_VARS']['tiff_fname'] == "" ) ) + { + echo "<title>No image filename defined</title>\n"; + echo "</head>\n"; + echo "<body>\n"; + echo "<p>No image filename defined - use GET method with field: tiff_fname</p>\n"; + echo "<p><a href=\"http://www.ozhiker.com/electronics/pjmt/\" >PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter</a></p>\n"; // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 + echo "</body>\n"; + exit( ); + } + else + { + $filename = $GLOBALS['HTTP_GET_VARS']['tiff_fname']; + } + + + // Output the title + echo "<title>Metadata details for $filename</title>"; + + + + ?> + + </head> + + <body> + + <p >Interpreted using: <a href="http://www.ozhiker.com/electronics/pjmt/">PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p> <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 --> + <br> + + <h2><B><U>Metadata for "<?php echo $filename; ?>"</U></B></h2> + + + <!-- Output the EXIF Information --> + <?php echo Interpret_EXIF_to_HTML( get_EXIF_TIFF( $filename ), $filename ); ?> + + <BR> + <BR> + <BR> + <p>Interpreted using:</p> + <p><a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p> <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 --> + + </body> + +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/changes.html b/includes/jpeg_metadata_tk/documentation/changes.html new file mode 100644 index 0000000..e85f5b7 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/changes.html @@ -0,0 +1,257 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>Changes List</h2> + <table border cellpadding=8 cellspacing=0> + <tr> + <th>Versions</th> + <th>File</th> + <th>Changes</th> + </tr> + <tr> + <td rowspan=1 >1.00 -> 1.01</td> + <td>IPTC.php</td> + <td>Changed <i>get_IPTC</i> to return partial data when error occurs</td> + </tr> + <tr> + <td rowspan=1 >1.00 -> 1.02</td> + <td>Photoshop_IRB.php</td> + <td>Changed <i>get_Photoshop_IRB</i> to work with corrupted resource names which Photoshop can still read</td> + </tr> + <tr> + <td>1.02 -> 1.03</td> + <td>Photoshop_IRB.php</td> + <td>Fixed <i>put_Photoshop_IRB</i> to output "Photoshop 3.0\x00" string with every APP13 segment, not just the first one</td> + </tr> + <tr> + <td>1.03 -> 1.04</td> + <td>XMP.php</td> + <td>Changed <i>put_IPTC</i> to fix a bug preventing the correct insertion of a XMP block where none existed previously</td> + </tr> + + + <tr> + <td rowspan=12 >1.04 -> 1.10</td> + <td>XMP.php</td> + <td>Changed <i>put_XMP_text</i> to fix some array indexes which were missing qoutes</td> + </tr> + <tr> + <td>IPTC.php</td> + <td> + Changed <i>put_IPTC</i> to check if the incoming IPTC block is valid<br /> + Changed Interpret_IPTC_to_HTML, adding nl2br functions for each text field, + so that multiline text displays properly + </td> + </tr> + <tr> + <td>XML.php</td> + <td> + Changed <i>read_xml_array_from_text</i> to fix problem that caused the whitespace (especially newlines) to be + destroyed when converting xml text to an xml array + </td> + </tr> + <tr> + <td>Photoshop_IRB.php</td> + <td> + Changed <i>get_Photoshop_IRB</i> to fix processing of embedded resource names, + after discovering that Photoshop does not process + resource names according to the standard :<br> + <a href="http://www.ozhiker.com/electronics/pjmt/jpeg_info/standards.html#IRB" >"Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000"</a><br> + This is an update to the change 1.00 -> 1.02, which was not fully correct<br><br> + Changed <i>put_Photoshop_IRB</i> to fix the writing of embedded resource name, + to avoid creating blank resources, and to fix a problem + causing the IRB block to be incorrectly positioned if no APP segments existed.<br><br> + Changed <i>get_Photoshop_IPTC</i> to initialise the output array correctly. + </td> + </tr> + <tr> + <td>Unicode.php</td> + <td> + Added the following functions: <br> + <i>smart_HTML_Entities</i><br> + <i>smart_htmlspecialchars</i><br> + <i>HTML_UTF16_UnEscape</i><br> + <i>HTML_UTF8_UnEscape</i><br><br> + Changed <i>HTML_UTF8_Escape</i> and <i>HTML_UTF16_Escape</i> to + use smart_htmlspecialchars, so that characters which + were already escaped would remain intact + </td> + </tr> + <tr> + <td>JPEG.php</td> + <td> + changed <i>put_jpeg_header_data</i> to check if the data being written exists + </td> + </tr> + <tr> + <td>Example.php</td> + <td> + Changed name of GET parameter from "filename" to "jpeg_fname" + to stop script-kiddies using the google command "allinurl:*.php?filename=*" + to find servers to attack + </td> + </tr> + <tr> + <td>EXIF.php</td> + <td> + added function get_EXIF_TIFF to allow extracting EXIF from a TIFF file + </td> + </tr> + + <tr> + <td>Photoshop_File_Info.php</td> + <td> + New File - see documentation for more information + </td> + </tr> + <tr> + <td>Edit_File_Info.php</td> + <td> + New File - see documentation for more information + </td> + </tr> + <tr> + <td>Edit_File_Info_Example.php</td> + <td> + New File - see documentation for more information + </td> + </tr> + <tr> + <td>Write_File_Info.php</td> + <td> + New File - see documentation for more information + </td> + </tr> + + + <tr> + <td rowspan=16 >1.10 -> 1.11</td> + <td>EXIF.php</td> + <td> + Added functionality to allow decoding of XMP and Photoshop IRB information embedded within the EXIF data<br> + Added checks for http and ftp wrappers, as these are not supported<br> + Changed <i>interpret_IFD</i> to allow thumbnail links to work when toolkit is portable across directories + </td> + </tr> + <tr> + <td>Makernotes/casio.php</td> + <td>Changed <i>get_Casio_Makernote_Html</i> to allow thumbnail links to work when toolkit is portable across directories</td> + </tr> + + <tr> + <td>Makernotes/olympus.php</td> + <td>Changed <i>get_Olympus_Makernote_Html</i> to allow thumbnail links to work when toolkit is portable across directories</td> + </tr> + + <tr> + <td>JFIF.php</td> + <td>Changed <i>Interpret_JFXX_to_HTML</i> to allow thumbnail links to work when toolkit is portable across directories</td> + </tr> + <tr> + <td>Photoshop_IRB.php</td> + <td> + Moved code out of <i>get_Photoshop_IRB</i> into new function <i>unpack_Photoshop_IRB_Data</i> to allow reading of IRB blocks embedded within EXIF (for TIFF Files)<br> + Moved code out of <i>put_Photoshop_IRB</i> into new function <i>pack_Photoshop_IRB_Data</i> to allow writing of IRB blocks embedded within EXIF (for TIFF Files)<br> + Enabled the usage of $GLOBALS['HIDE_UNKNOWN_TAGS'] to hide unknown resources<br> + Changed <i>Interpret_IRB_to_HTML</i> to allow thumbnail links to work when toolkit is portable across directories + </td> + </tr> + <tr> + <td>EXIF_Makernote.php</td> + <td> + Changed makernotes directory definition to allow the toolkit to be portable across directories + </td> + </tr> + <tr> + <td>EXIF_Tags.php</td> + <td> + Added TIFF compression types ZIP, LZW and JPEG<br> + Added embedded XMP tag<br> + Added embedded Photoshop IRB tag<br> + Fixed GPS tags after testing + </td> + </tr> + + <tr> + <td>Example.php</td> + <td> + Changed displayed toolkit version numbers to reference Toolkit_Version.php + Changed this example file to be easily relocatable + </td> + </tr> + <tr> + <td>TIFFExample.php</td> + <td> + Changed displayed toolkit version numbers to reference Toolkit_Version.php + </td> + </tr> + <tr> + <td>Write_File_Info.php</td> + <td> + Changed displayed toolkit version numbers to reference Toolkit_Version.php<br> + Changed error reporting to no errors<br> + Removed limitation on file being in current directory + </td> + </tr> + <tr> + <td>Edit_File_Info_Example.php</td> + <td> + Changed displayed toolkit version numbers to reference Toolkit_Version.php + </td> + </tr> + <tr> + <td>Photoshop_File_Info.php</td> + <td> + Changed displayed toolkit version numbers to reference Toolkit_Version.php + </td> + </tr> + <tr> + <td>Edit_File_Info.php</td> + <td> + Changed displayed toolkit version numbers to reference Toolkit_Version.php + </td> + </tr> + <tr> + <td>get_ps_thumb.php</td> + <td> + Added support for Photoshop IRB thumbnails which are embedded within EXIF information (used in TIFF files) + </td> + </tr> + <tr> + <td>Toolkit_Version.php</td> + <td> + New File - Added file to provide a single storage place for current version number + </td> + </tr> + <tr> + <td>pjmt_utils.php</td> + <td> + New File - Added file to provide utility functions for the toolkit + </td> + </tr> + + </table> + + <br> + + <br> + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/css_terms.html b/includes/jpeg_metadata_tk/documentation/css_terms.html new file mode 100644 index 0000000..648f6cd --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/css_terms.html @@ -0,0 +1,171 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>Customising the HTML look via style sheets</h2> + <p> + <a href="css_terms.html#class_list">Below</a> is a list of style classes which have been used in the HTML for the PHP JPEG Metadata Toolkit. + These allow the look of the output HTML to be changed via the use of cascading style sheets. + + </p> + + <p>There are two ways to do this:</p> + <ol> + <li>Embedded Style sheets</li> + <li>External Style sheets</li> + </ol> + + <h3>Examples</h3> + + <p> Both of the following two sections would cause the following visual effects:<p> + <ul> + <li>The page will have a dark grey background colour (#505050)</li> + <li>The default text colour for the page will be off-white (#F0F0F0)</li> + <li>Links (Anchors) will be orange</li> + <li>Main headings for the EXIF section will be red</li> + <li>Secondary headings for the EXIF section will be orange</li> + <li>The outer border of the Tables for the EXIF section will be a thin dark yellow (#909000) solid line</li> + </ul> + <br> + <br> + <br> + + <h4>Embedded Style Sheets</h4> + + <p>Include the following in the <HEAD> section of the HTML page:</p> + <pre style="color:brown"> + <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css"> + <STYLE TYPE="text/css" MEDIA="screen, print, projection"> + <!-- + BODY { background-color:#505050; color:#F0F0F0 } + a { color:orange } + .EXIF_Main_Heading { color:red } + .EXIF_Secondary_Heading{ color: orange} + .EXIF_Table { border-collapse: collapse ; border: 1px solid #909000} + --> + </STYLE> + </pre> + + + <h4>External Style Sheets</h4> + <p>Include the following in the <HEAD> section of the HTML page:</p> + <pre style="color:brown"> + <link rel=StyleSheet href="style.css" type="text/css"> + </pre> + <p>Create an external file called "style.css" containing the following:</p> + <pre style="color:brown"> + BODY { background-color:#505050; color:#F0F0F0 } + a { color:orange } + .EXIF_Main_Heading { color:red } + .EXIF_Secondary_Heading{ color: orange} + .EXIF_Table { border-collapse: collapse ; border: 1px solid #909000} + </pre> + + + + <br> + <br> + + <a name="class_list"><h3>Cascading Style Sheets Class List</h3></a> + <p>Any of the following classes may be used to customise the look of the html output from the PHP JPEG Metadata Toolkit</p> + <ul> + <li>JPEG_Intrinsic_Main_Heading</li> + <li>JPEG_Intrinsic_Table</li> + <li>JPEG_Intrinsic_Table_Row</li> + <li>JPEG_Intrinsic_Caption_Cell</li> + <li>JPEG_Intrinsic_Value_Cell</li> + </ul> + <ul> + <li>JPEG_APP_Segments_Main_Heading</li> + <li>JPEG_APP_Segments_Table</li> + <li>JPEG_APP_Segments_Table_Row</li> + <li>JPEG_APP_Segments_Caption_Cell</li> + <li>JPEG_APP_Segments_Type_Cell</li> + <li>JPEG_APP_Segments_Size_Cell</li> + </ul> + <ul> + <li>XMP_Main_Heading</li> + <li>XMP_Secondary_Heading</li> + <li>XMP_Table</li> + <li>XMP_Table_Row</li> + <li>XMP_Caption_Cell</li> + <li>XMP_Value_Cell</li> + </ul> + <ul> + <li>JFIF_Main_Heading</li> + <li>JFIF_Table</li> + <li>JFIF_Table_Row</li> + <li>JFIF_Caption_Cell</li> + <li>JFIF_Value_Cell</li> + <li>JFIF_Thumbnail</li> + </ul> + <ul> + <li>JFXX_Main_Heading</li> + <li>JFXX_Text</li> + <li>JFXX_Thumbnail</li> + <li>JFXX_Thumbnail_Link</li> + </ul> + <ul> + <li>JPEG_Comment_Main_Heading</li> + <li>JPEG_Comment_Text</li> + </ul> + <ul> + <li>Picture_Info_Main_Heading</li> + <li>Picture_Info_Caption_Text</li> + <li>Picture_Info_Value_Text</li> + </ul> + <ul> + <li>Photoshop_Main_Heading</li> + <li>Photoshop_Table</li> + <li>Photoshop_Table_Row</li> + <li>Photoshop_Caption_Cell</li> + <li>Photoshop_Value_Cell</li> + <li>Photoshop_Thumbnail</li> + <li>Photoshop_Thumbnail_Link</li> + </ul> + <ul> + <li>IPTC_Main_Heading</li> + <li>IPTC_Table</li> + <li>IPTC_Table_Row</li> + <li>IPTC_Caption_Cell</li> + <li>IPTC_Value_Cell</li> + </ul> + <ul> + <li>EXIF_Main_Heading</li> + <li>EXIF_Secondary_Heading</li> + <li>EXIF_Table</li> + <li>EXIF_Table_Row</li> + <li>EXIF_Caption_Cell</li> + <li>EXIF_Value_Cell</li> + <li>EXIF_First_IFD_Thumb</li> + <li>EXIF_First_IFD_Thumb_Link</li> + <li>EXIF_Minolta_Thumb</li> + <li>EXIF_Minolta_Thumb_Link</li> + <li>EXIF_Casio_Thumb</li> + <li>EXIF_Casio_Thumb_Link</li> + <li>EXIF_Makernote_Small_Heading</li> + <li>EXIF_Makernote_Text</li> + </ul> + + + <br> + <br> + + </div> + + </body> +</html> diff --git a/includes/jpeg_metadata_tk/documentation/edit_write_file_info.html b/includes/jpeg_metadata_tk/documentation/edit_write_file_info.html new file mode 100644 index 0000000..d9ab787 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/edit_write_file_info.html @@ -0,0 +1,272 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="examples.html">Go to Documentation - Examples</a> + + <div class="maintext"> + + <h2>Example Photoshop "File Info" Editor Scripts</h2> + + <p> + These two example scripts areincluded with the PHP JPEG Metadata Toolkit. + They show how the library can be used to edit metadata over the internet + in exactly the same format as Photoshop"s "File Info" dialog box. + This is an explanation of some of the commands used in the script. + </p> + + <a href="http://www.ozhiker.com/electronics/pjmt/library/list_contents.php4?filename=Edit_File_Info_Example.php"> + Click here to see the Edit_File_Info_Example.php</a> + <br><br> + <a href="http://www.ozhiker.com/electronics/pjmt/library/list_contents.php4?filename=Write_File_Info.php"> + Click here to see the Write_File_Info.php</a> + <br> + + <br> + <br> + <br> + + <h2>Edit_File_Info_Example.php</h2> + + <h4>Overview</h4> + <p> + This script utilises another script called Edit_File_Info.php.<br /> + Edit_File_Info.php outputs the HTML required to display a HTML form which emulates + the Photoshop "File Info" dialog box. + </p> + <p> + Edit_File_Info.php has four modes of operation: + </p> + <ol> + <li> + If $new_ps_file_info_array is defined then it's data will be used + to fill the fields. + </li> + <li> + If $new_ps_file_info_array is not defined but $filename is + defined, then the file info fields will be filled from the + metadata in the file specified + </li> + <li> + If $new_ps_file_info_array is not defined but $filename + and $default_ps_file_info_array are defined, then the + file info fields will be filled from the metadata in the + file specified, but where fields are blank, they will be + filled from $default_ps_file_info_array + </li> + <li> + Otherwise the fields will be blank + </li> + </ol> + + + + + <br> + <br> + + <h4>Forced Field Values</h4> + + <p> + If the variable $new_ps_file_info_array before Edit_File_Info.php is included, then it + forces the values of the fields to be loaded from $new_ps_file_info_array + </p> + + <p> + <b>NOTE:</b> + <ul> + <li>date must be in YYYY-MM-DD format</li> + <li>coyrightstatus must be either "Copyrighted Work", "Public Domain", or "Unknown"</li> + <li>urgency must be a single digit between 1 and 8 inclusive</li> + <li>category must be 3 characters or less</li> + <li>authors position is not used in Photoshop 7 or higher</li> + <li>jobname is not used in Photoshop CS or higher</li> + </ul> + </p> + + <p>Here is some example code for defining $new_ps_file_info_array</p> + <pre style="color:brown"> + $new_ps_file_info_array = array ( + 'title' => "Frenchmans Cap", + 'author' => "Evan Hunter", + 'authorsposition' => "", + 'caption' => "A view of Frenchmans cap from a bus on the Lyell Highway", + 'captionwriter' => "Evan Hunter", + 'jobname' => "", + 'copyrightstatus' => "Copyrighted Work", + 'copyrightnotice' => "Copyright (c) Evan Hunter 2004", + 'ownerurl' => "http://www.ozhiker.com", + 'keywords' => array( "Frenchmans Cap", "Franklin Gordon National Park", "Tasmania" ), + 'category' => "abc", + 'supplementalcategories'=> array( "Supp Category1", "Supp Category2" ), + 'date' => "2004-05-11", + 'city' => "Franklin Gordon National Park", + 'state' => "Tasmania", + 'country' => "Australia", + 'credit' => "Evan Hunter", + 'source' => "Evan Hunter", + 'headline' => "Frenchmans Cap from Lyell Highway", + 'instructions' => "No Instructions", + 'transmissionreference' => "12345", + 'urgency' => "3" + ); + </pre> + + <br> + <br> + + + <h4>Default Field Values</h4> + + <p> + The variable $default_ps_file_info_array sets the default values of the File Info fields. + These are only loaded into the fields where File Info has been loaded from a file, but + the fields in question are still blank. It has similar definition requirements to $new_ps_file_info_array. + </p> + + <pre style="color:brown"> + // Define defaults for the fields - These are only used where the image has blank fields + $default_ps_file_info_array = array ( + 'title' => "", + 'author' => "Evan Hunter", + 'authorsposition' => "", + 'caption' => "", + 'captionwriter' => "Evan Hunter", + 'jobname' => "", + 'copyrightstatus' => "Copyrighted Work", + 'copyrightnotice' => "Copyright (c) Evan Hunter 2004", + 'ownerurl' => "http://www.ozhiker.com", + 'keywords' => array(), + 'category' => "", + 'supplementalcategories'=> array(), + 'date' => "", + 'city' => "", + 'state' => "Tasmania", + 'country' => "Australia", + 'credit' => "Evan Hunter", + 'source' => "Evan Hunter", + 'headline' => "", + 'instructions' => "", + 'transmissionreference' => "", + 'urgency' => "" + ); + </pre> + + <br> + <br> + + <h4>Input Filename</h4> + <p> + When the $filename variable is defined before the inclusion of Edit_File_Info.php, it defines + which JPEG file should be searched to fill the File Info fields + </p> + <pre style="color:brown"> + $filename = "test.jpg"; + </pre> + + <br> + <br> + + <h4>Output Filename</h4> + <p> + The $outputfilename variable must always be defined before the inclusion of Edit_File_Info.php. + It defines the target image which will be modified with the information that the user enters. + It can be the same as $filename. + </p> + <pre style="color:brown"> + $outputfilename = "test.jpg"; + </pre> + + <h4>Including Edit_File_Info.php</h4> + <p> + After $outputfilename has been defined, and any of the other variables have been defined as required, + Edit_File_Info.php should be included. It outputs the HTML for the File Info Editor. + </p> + <pre style="color:brown"> + include "Edit_File_Info.php"; + </pre> + + <br> + <br> + + + <h2>Write_File_Info.php</h2> + <h4>Overview</h4> + <p> + This script receives the File Info data from the user via the HTML POST method.<br /> + </p> + + + <h4>Retrieving the POSTed variables</h4> + <p> + The field data is retrieved as follows: + </p> + <pre style="color:brown"> + $new_ps_file_info_array = $GLOBALS['HTTP_POST_VARS']; + </pre> + + <br> + <br> + + <h4>Preparing the field data</h4> + <p> + The POSTed field data has some characters escaped with backslashes, which need to be removed. + Two of the fields should also be arrays which and need to be split. + </p> + <pre style="color:brown"> + // Some characters are escaped with backslashes in HTML Posted variable + // Cycle through each of the HTML Posted variables, and strip out the slashes + foreach( $new_ps_file_info_array as $var_key => $var_val ) + { + $new_ps_file_info_array[ $var_key ] = stripslashes( $var_val ); + } + + // Keywords should be an array - explode it on newline boundarys + $new_ps_file_info_array[ 'keywords' ] = explode( "\n", trim( $new_ps_file_info_array[ 'keywords' ] ) ); + + // Supplemental Categories should be an array - explode it on newline boundarys + $new_ps_file_info_array[ 'supplementalcategories' ] = explode( "\n", trim( $new_ps_file_info_array[ 'supplementalcategories' ] ) ); + </pre> + + <br> + <br> + + <h4>Writing the File Info</h4> + <p> + Now the File Info data can be written to a file. The following code results in + the $jpeg_header_data being updated with the new Photoshop File Info, and it can + then be written to a JPEG file as normal using put_jpeg_header_data: + </p> + <pre style="color:brown"> + // Retrieve the header information + $jpeg_header_data = get_jpeg_header_data( $filename ); + + // Retreive the EXIF, XMP and Photoshop IRB information from + // the existing file, so that it can be updated + $Exif_array = get_EXIF_JPEG( $filename ); + $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) ); + $IRB_array = get_Photoshop_IRB( $jpeg_header_data ); + + // Update the JPEG header information with the new Photoshop File Info + $jpeg_header_data = put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Exif_array, $XMP_array, $IRB_array ); + </pre> + + <br> + <br> + + + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/example.html b/includes/jpeg_metadata_tk/documentation/example.html new file mode 100644 index 0000000..7f922de --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/example.html @@ -0,0 +1,131 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="examples.html">Go to Documentation - Examples</a> + + <div class="maintext"> + + <h2>Example Metadata Display Script</h2> + + <p> + This example script is included with the PHP JPEG Metadata Toolkit. It shows how the library + can be used to display detailed metadata from a JPEG image file. This is an explanation of some of the + commands used in the script. + </p> + + <a href="http://www.ozhiker.com/electronics/pjmt/library/list_contents.php4?filename=Example.php"> + Click here to see the script</a> + + <br> + + <br> + <br> + <br> + + <h4>Error Reporting</h4> + <p> + Although this library has been tested to be error free on a large number of images, + it is good programming to turn error reporting off, to ensure that users don't see + any error messages. This is not just cosmetic, it helps hide the details of the program + from hackers. + </p> + <pre style="color:brown"> + // Turn off Error Reporting + error_reporting ( 0 ); + </pre> + + <h4>Includes</h4> + <p>Ensure that the correct files are included:</p> + <table border=1 cellspacing=0 cellpadding=4> + <tr> + <td>JPEG.php</td> + <td>For accessing basic JPEG functions - most programs will need this</td> + </tr> + <tr> + <td>EXIF.php</td> + <td>For accessing Exchangeable Image File Format (EXIF) APP1 segments</td> + </tr> + <tr> + <td>JFIF.php</td> + <td>For accessing JPEG File Interchange Format and JFIF Extension segments</td> + </tr> + <tr> + <td>Photoshop_IRB.php</td> + <td>For accessing Photoshop IRB & IPTC-NAA IIM segments</td> + </tr> + <tr> + <td>PictureInfo.php</td> + <td>For accessing APP12 Picture Info segments</td> + </tr> + <tr> + <td>XMP.php</td> + <td>For accessing APP1 XMP / RDF / Dublin Core segments</td> + </tr> + + </table> + + <br> + <br> + + <h4>Displaying Metadata Information</h4> + + <p> + Displaying metadata in HTML format from a PHP script is very simple. For example, + the following will display any EXIF information in a JPEG image: + </p> + + <pre style="color:brown"> + echo Interpret_EXIF_to_HTML( get_EXIF_JPEG( $filename ), $filename ); + </pre> + + <br> + <br> + + <p>To display the Photoshop IRB and IPTC-NAA information, use:</p> + <pre style="color:brown"> + echo Interpret_IRB_to_HTML( get_Photoshop_IRB( get_jpeg_header_data( $filename ) ), $filename ); + </pre> + + <br> + <br> + + <p> + Note: The HTML returned from these functions is just a fragment, and requires + <HTML>, <HEAD> and <BODY> tags to be provided. + </p> + + <br> + <br> + + <h4>Writing Metadata Information</h4> + <p> + Although this is not shown in the example script, writing metadata is also very simple. For example, the following will allow + the EXIF data in an image to be modified, and written to a file called "output.jpg" : + </p> + <pre style="color:brown"> + $exif_data = get_EXIF_JPEG( $filename ); + + // Modify the $exif_data array here + + $jpeg_header_data = put_EXIF_JPEG( $exif_data, $jpeg_header_data ); + put_jpeg_header_data( $filename, "output.jpg", $jpeg_header_data ); + </pre> + + <br> + <br> + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/examples.html b/includes/jpeg_metadata_tk/documentation/examples.html new file mode 100644 index 0000000..172b123 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/examples.html @@ -0,0 +1,62 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>Example Scripts</h2> + + <p> + Included with the PHP JPEG Metadata Toolkit are several example scripts, which show how the library + can be used to retrieve and store the metadata in a JPEG image file. Below are explanations of some of the + commands used in the scripts. + </p> + + + <table border=1 cellpadding=10 cellspacing=0> + <tr> + <td> + <a href="example.html" >Example.php</a> + </td> + <td> + Displays detailed information about the metadata in a JPEG file. + </td> + <tr> + <tr> + <td> + <a href="tiffexample.html" >TIFFExample.php</a> + </td> + <td> + Displays detailed information about the metadata in a JPEG file. + </td> + <tr> + <tr> + <td> + <a href="edit_write_file_info.html" >Edit_File_Info_Example.php,<br>Write_File_Info.php</a> + </td> + <td> + Allows editing of metadata over the internet in exactly the same format as + Photoshop"s "File Info" dialog box. + </td> + <tr> + + + <br> + <br> + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/exif.html b/includes/jpeg_metadata_tk/documentation/exif.html new file mode 100644 index 0000000..f67c1fd --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/exif.html @@ -0,0 +1,284 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>EXIF and Kodak "Meta" Function Reference</h2> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>get_EXIF_JPEG</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retrieves information from a Exchangeable Image File Format (EXIF) + APP1 segment and returns it in an array. Uses information + supplied by the get_jpeg_header_data function + </td> + <tr> + <tr> + <td>Parameters:</td> + <td width=1%>filename</td> + <td>the filename of the JPEG image to process</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>OutputArray</td> + <td>Array of EXIF records</td> + </tr> + <tr> + <td>FALSE</td> + <td>If an error occured in decoding</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>put_EXIF_JPEG</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Stores information into a Exchangeable Image File Format (EXIF) + APP1 segment from an EXIF array.<br> + <br> + <b>WARNING</b>: Because the EXIF standard allows pointers to data + outside the APP1 segment, if there are any such pointers in + a makernote, this function will DAMAGE them since it will not + be aware that there is an external pointer. This will often + happen with Makernotes that include an embedded thumbnail. + This damage could be prevented where makernotes can be decoded, + but currently this is not implemented. + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>exif_data</td> + <td>The array of EXIF data to insert into the JPEG header</td> + </tr> + <tr> + <td width=1%>jpeg_header_data</td> + <td>The JPEG header into which the EXIF data should be stored, as from get_jpeg_header_data</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>jpeg_header_data</td> + <td>JPEG header array with the EXIF segment inserted</td> + </tr> + <tr> + <td>FALSE</td> + <td>If an error occured</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>get_Meta_JPEG</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + <p> + Retrieves information from a Meta APP3 segment and returns it + in an array. Uses information supplied by the + get_jpeg_header_data function. + </p> + <p> + The Meta segment has the same format as an EXIF segment, but + uses different tags + </p> + </td> + <tr> + <tr> + <td>Parameters:</td> + <td width=1%>filename</td> + <td>the filename of the JPEG image to process</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>OutputArray</td> + <td>Array of Meta records</td> + </tr> + <tr> + <td>FALSE</td> + <td>If an error occured in decoding</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>put_Meta_JPEG</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Stores information into a Meta APP3 segment from a Meta array.<br> + <br> + <b>WARNING</b>: Because the Meta (EXIF) standard allows pointers to data + outside the APP1 segment, if there are any such pointers in + a makernote, this function will DAMAGE them since it will not + be aware that there is an external pointer. + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>meta_data</td> + <td>The array of Meta data to insert into the JPEG header</td> + </tr> + <tr> + <td width=1%>jpeg_header_data</td> + <td>The JPEG header into which the Meta data should be stored, as from get_jpeg_header_data</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>jpeg_header_data</td> + <td>JPEG header array with the Meta segment inserted</td> + </tr> + <tr> + <td>FALSE</td> + <td>If an error occured</td> + </tr> + </table> + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>get_EXIF_TIFF</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + <p> + Retrieves information from a Exchangeable Image File Format (EXIF) + within a TIFF file and returns it in an array. + </p> + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>filename</td> + <td>the filename of the TIFF image to process </td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>OutputArray</td> + <td>Array of EXIF records</td> + </tr> + <tr> + <td>FALSE</td> + <td>If an error occured in decoding</td> + </tr> + </table> + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>Interpret_EXIF_to_HTML</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + <p> + Generates html detailing the contents an APP1 EXIF array + which was retrieved with a get_EXIF_JPEG function. + Can also be used for APP3 Meta arrays. + </p> + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>Exif_array</td> + <td>the EXIF array,as read from get_EXIF_JPEG</td> + </tr> + <tr> + <td width=1%>filename</td> + <td>the name of the Image file being processed (used by scripts which displays EXIF thumbnails)</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>output_str</td> + <td> A string containing the HTML</td> + </tr> + </table> + + + <h3>Notes:</h3> + <p> + Unfortunately, because EXIF data may be distributed anywhere + throughout an image file, rather than just being in one block, + it is impossible to pass just a string containing only the EXIF + information. Hence it is neccessary to be able to seek to + any point in the file. This causes the HTTP and FTP wrappers + not to work - i.e. the EXIF functions will only work with local + files. + </p> + To work on an internet file, copy it locally to start with: + </p> + <blockquote> + $newfilename = tempnam ( $dir, "tmpexif" );<br> + copy ( "http://whatever.com", $newfilename ); + </blockquote> + + <p> + There are three global variables which can be set to alter the look of any HTML + output from Interpret_EXIF_to_HTML: + </p> + <blockquote> + $GLOBALS['HIDE_UNKNOWN_TAGS'];<br> + $GLOBALS['SHOW_BINARY_DATA_HEX'];<br> + $GLOBALS['SHOW_BINARY_DATA_TEXT']; + </blockquote> + <p>These variables are all boolean and all default to FALSE</p> + + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/index.html b/includes/jpeg_metadata_tk/documentation/index.html new file mode 100644 index 0000000..8d21c64 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/index.html @@ -0,0 +1,60 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + <div class="maintext"> + + + <h2><a href="intro.html">Introduction</a></h2> + + <h2><a href="examples.html">Example Scripts</a></h2> + + + <h2>Function Reference</h2> + <table> + <tr><td><a href="jpeg.html">JPEG & Intrinsic JPEG Values</a></td></tr> + <tr><td><a href="exif.html">EXIF, TIFF/EP, DCF, and Kodak "Meta"</a></td></tr> + <tr><td><a href="xmp.html">XMP, RDF, Dublin Core</a></td></tr> + <tr><td><a href="photoshop.html">Photoshop IRB & IPTC-NAA IIM</a></td></tr> + <tr><td><a href="picture_info.html">Picture Info</a></td></tr> + <tr><td><a href="jfif.html">JFIF & JFIF Extension</a></td></tr> + <tr><td><a href="photoshop_file_info.html">Photoshop File Info</a></td></tr> + </table> + + + <H2>Other Information</H2> + <table> + <tr><td><a href="css_terms.html">Customising the look of the HTML output via style sheets</a></td></tr> + <tr><td><a href="Camera_List_1.0.pdf">Digital Camera Support List</a></li></tr> + <tr><td><a href="todo.html">List of Further Work Required</a></li></tr> + <tr><td><a href="changes.html">Changes List</a></li></tr> + <tr><td><a href="../COPYING.txt">License</a></li></tr> + </table> + + + <br> + <br> + <br> + + <br> + <p>PHP JPEG Metadata Toolkit Version 1.11</p> + <hr> + <br> + <a href="http://www.ozhiker.com/electronics/pjmt/">Visit the PHP JPEG Metadata Toolkit Homepage (online)</a><br> + <a href="http://www.ozhiker.com/electronics/pjmt/library/list_contents.php4">Browse the source (online)</a><br> + + <a href="http://www.ozhiker.com/electronics/pjmt/jpeg_info/index.html">JPEG Metadata Reference Information (online)</a><br> + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/intro.html b/includes/jpeg_metadata_tk/documentation/intro.html new file mode 100644 index 0000000..b40cfe0 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/intro.html @@ -0,0 +1,75 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>Introduction</h2> + + <p> + The PHP JPEG Metadata Toolkit is a library of functions which allows manipulation of many types of metadata + that reside in a JPEG image file. + </p> + <br> + + <p>The metadata that can be accessed by this library include:</p> + + <table align="center" border=1 cellspacing=0 cellpadding=7> + <tr> + <td>EXIF, DCF & TIFF/EP - including makernotes</td> + <td>Contains Digital Camera Settings. Can contain a thumbnail.</td> + </tr> + <tr> + <td>XMP, RDF & Dublin Core, including multiple language support</td> + <td>Can contain Digital camera settings and text headings/captions.</td> + </tr> + <tr> + <td>Photoshop IRB & IPTC-NAA IIM</td> + <td>Contains Photoshop settings and can contain text headings/captions. Can contain a thumbnail.</td> + </tr> + <tr> + <td>Picture Info</td> + <td>Contains Digital Camera Settings for older cameras</td> + </tr> + <tr> + <td>JFIF & JFIF Extension</td> + <td>Contains limited info about the image and can contain a thumbnail</td> + </tr> + <tr> + <td>Intrinsic JPEG Values</td> + <td>Contains limited info configuration of the image</td> + </tr> + </table> + <br> + <br> + + <p>Other features<p> + <ul> + <li>Has been tested with over 450 popular digital cameras</li> + <li>Provides access to lots of metadata for which php has no built in support</li> + <li>Works with many files that have corrupted metadata.</li> + <li>Customisable look of the HTML output via style sheets</li> + </ul> + + <br> + <h3>Requirements</h3> + <p>The toolkit requires PHP 4 or higher - Has been tested with PHP 4.3.7 and 4.3.8</p> + <br> + <br> + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/jfif.html b/includes/jpeg_metadata_tk/documentation/jfif.html new file mode 100644 index 0000000..f00cb45 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/jfif.html @@ -0,0 +1,237 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>JFIF & JFIF Extension Function Reference</h2> + <p> + The "App 0" segment is often used by the JPEG File Interchange Format (JFIF) information. + Sometimes, there is an additional "App 0" segment for the JFIF Extension (JFXX) information. + </p> + + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>get_JFIF</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retrieves information from a JPEG File Interchange Format (JFIF) + segment and returns it in an array. Uses information supplied by + the get_jpeg_header_data function + </td> + <tr> + <tr> + <td>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>JFIF_data</td> + <td>an array of JFIF data</td> + </tr> + <tr> + <td>FALSE</td> + <td>if a JFIF segment could not be found</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>put_JFIF</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Creates a new JFIF segment from an array of JFIF data in the + same format as would be retrieved from get_JFIF, and inserts + this segment into the supplied JPEG header array + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data, into which the new JFIF segment will be put</td> + </tr> + <tr> + <td width=1%>new_JFIF_array</td> + <td>a JFIF information array in the same format as from get_JFIF, to create the new segment</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>jpeg_header_data</td> + <td>the JPEG header data array with the new JFIF segment added</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>Interpret_JFIF_to_HTML</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Generates html showing the JFIF information contained in a JFIF data array, as retrieved with get_JFIF + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>JFIF_array</td> + <td>a JFIF data array, as from get_JFIF</td> + </tr> + <tr> + <td width=1%>filename</td> + <td>the name of the JPEG file being processed (used by the script which displays the JFIF thumbnail)</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>output</td> + <td>the HTML string</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>get_JFXX</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retrieves information from a JPEG File Interchange Format Extension (JFXX) + segment and returns it in an array. Uses information supplied by + the get_jpeg_header_data function + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>JFXX_data</td> + <td>an array of JFXX data</td> + </tr> + <tr> + <td width=1%>FALSE</td> + <td>if a JFXX segment could not be found</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>put_JFXX</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Creates a new JFXX segment from an array of JFXX data in the + same format as would be retrieved from get_JFXX, and inserts + this segment into the supplied JPEG header array + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data, into which the new JFXX segment will be put</td> + </tr> + <tr> + <td width=1%>new_JFXX_array</td> + <td>a JFXX information array in the same format as from get_JFXX, to create the new segment</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>jpeg_header_data</td> + <td>the JPEG header data array with the new JFXX segment added</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>Interpret_JFXX_to_HTML</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Generates html showing the JFXX thumbnail contained in a JFXX data array, as retrieved with get_JFXX + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>JFXX_array</td> + <td>a JFXX information array in the same format as from get_JFXX, to create the new segment</td> + </tr> + <tr> + <td width=1%>filename</td> + <td>the name of the JPEG file being processed (used by the script which displays the JFXX thumbnail)</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>output</td> + <td>the Html string</td> + </tr> + </table> + + + <br> + <br> + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/jpeg.html b/includes/jpeg_metadata_tk/documentation/jpeg.html new file mode 100644 index 0000000..36a59a7 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/jpeg.html @@ -0,0 +1,303 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>JPEG & Intrinsic JPEG Values Function Reference</h2> + <p> + Functions for performing basic operations on a JPEG file + </p> + + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td width=1%>Function:</td> + <td colspan=2><b>get_jpeg_header_data</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Reads all the JPEG header segments from an JPEG image file into an array + </td> + <tr> + <tr> + <td>Parameters:</td> + <td width=1%>filename</td> + <td>the filename of the file to JPEG file to read</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>headerdata</td> + <td>Array of JPEG header segments</td> + </tr> + <tr> + <td>FALSE</td> + <td>if headers could not be read</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td width=1%>Function:</td> + <td colspan=2><b>put_jpeg_header_data</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Writes JPEG header data into a JPEG file. Takes an array in the + same format as from get_jpeg_header_data, and combines it with + the image data of an existing JPEG file, to create a new JPEG file<br> + <br> + <b>WARNING</b>: As this function will replace all JPEG headers, + including SOF etc, it is best to read the jpeg headers + from a file, alter them, then put them back on the same + file. If a SOF segment wer to be transfered from one + file to another, the image could become unreadable unless + the images were idenical size and configuration + </td> + <tr> + <tr> + <td rowspan=3>Parameters:</td> + <td width=1%>old_filename</td> + <td>the JPEG file from which the image data will be retrieved</td> + </tr> + <tr> + <td width=1%>new_filename</td> + <td>the name of the new JPEG to create (can be same as old_filename)</td> + </tr> + <tr> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>TRUE</td> + <td>on Success</td> + </tr> + <tr> + <td>FALSE</td> + <td>on Failure</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td width=1%>Function:</td> + <td colspan=2><b>get_jpeg_Comment</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retreives the contents of the JPEG Comment (COM = 0xFFFE) segment if one exists + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>the JPEG header data, as retrieved from the get_jpeg_header_data function</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>string</td> + <td>Contents of the Comment segement</td> + </tr> + <tr> + <td>FALSE</td> + <td>if the comment segment couldnt be found</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td width=1%>Function:</td> + <td colspan=2><b>Interpret_Comment_to_HTML</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Generates html showing the contents of any JPEG Comment segment + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>the JPEG header data, as retrieved from the get_jpeg_header_data function</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>output</td> + <td>the HTML</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td width=1%>Function:</td> + <td colspan=2><b>get_jpeg_intrinsic_values</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retreives information about the intrinsic characteristics of the jpeg image, such as Bits per Component, Height and Width. + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>the JPEG header data, as retrieved from the get_jpeg_header_data function</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>array</td> + <td>An array containing the intrinsic JPEG values</td> + </tr> + <tr> + <td>FALSE</td> + <td>if the comment segment couldnt be found</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td width=1%>Function:</td> + <td colspan=2><b>Interpret_intrinsic_values_to_HTML</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Generates html showing some of the intrinsic JPEG values which were retrieved with the get_jpeg_intrinsic_values function + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>values</td> + <td>the JPEG intrinsic values, as read from get_jpeg_intrinsic_values</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>OutputStr</td> + <td>A string containing the HTML</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td width=1%>Function:</td> + <td colspan=2><b>get_jpeg_image_data</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retrieves the compressed image data part of the JPEG file + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>filename</td> + <td>the filename of the JPEG file to read</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>compressed_data</td> + <td>A string containing the compressed data</td> + </tr> + <tr> + <td>FALSE</td> + <td>if retrieval failed</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td width=1%>Function:</td> + <td colspan=2><b>Generate_JPEG_APP_Segment_HTML</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Generates html showing information about the Application (APP) segments which are present in the JPEG file + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>the JPEG header data, as retrieved from the get_jpeg_header_data function</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>output</td> + <td>A string containing the HTML</td> + </tr> + </table> + + + <br> + <br> + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/photoshop.html b/includes/jpeg_metadata_tk/documentation/photoshop.html new file mode 100644 index 0000000..c8c8009 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/photoshop.html @@ -0,0 +1,262 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>Photoshop IRB & IPTC-NAA IIM Function Reference</h2> + <p> + The Photoshop Image Resource Block (IRB) resides in + the "App 13" segment of a JPEG image. + It contains photoshop information but can also contain the + IPTC-NAA IIM record. (International Press Telecommunications Council, Newspaper Association of America, Information Interchange Model) + </p> + + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>get_Photoshop_IRB</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retrieves the Photoshop Information Resource Block (IRB) information + from an App13 JPEG segment and returns it as an array. This may + include IPTC-NAA IIM Information. Uses information + supplied by the get_jpeg_header_data function + </td> + <tr> + <tr> + <td>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>IRBdata</td> + <td>The array of Photoshop IRB records</td> + </tr> + <tr> + <td>FALSE</td> + <td>if an APP 13 Photoshop IRB segment could not be found, or if an error occured</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>put_Photoshop_IRB</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Adds or modifies the Photoshop Information Resource Block (IRB) + information from an App13 JPEG segment. If a Photoshop IRB already + exists, it is replaced, otherwise a new one is inserted, using the + supplied data. Uses information supplied by the get_jpeg_header_data + function + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data</td> + </tr> + <tr> + <td width=1%>new_IRB_data</td> + <td> + an array of the data to be stored in the Photoshop + IRB segment. Should be in the same format as received + from get_Photoshop_IRB + </td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>jpeg_header_data</td> + <td>the JPEG header data array with the Photoshop IRB added.</td> + </tr> + <tr> + <td>FALSE</td> + <td>if an error occured</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>get_Photoshop_IPTC</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retrieves IPTC-NAA IIM information from within a Photoshop + IRB (if it is present) and returns it in an array. Uses + information supplied by the get_jpeg_header_data function + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>Photoshop_IRB_data</td> + <td>an array of Photoshop IRB records, as returned from get_Photoshop_IRB</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>IPTC_Data_Out</td> + <td>The array of IPTC-NAA IIM records</td> + </tr> + <tr> + <td>FALSE</td> + <td>if an IPTC-NAA IIM record could not be found, or if an error occured</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>put_Photoshop_IPTC</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Inserts a new IPTC-NAA IIM resource into a Photoshop + IRB, or replaces an the existing resource if one is present. + Uses information supplied by the get_Photoshop_IRB function + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>Photoshop_IRB_data</td> + <td>an array of Photoshop IRB records, as returned from get_Photoshop_IRB, into which the IPTC-NAA IIM record will be inserted</td> + </tr> + <tr> + <td width=1%>new_IPTC_block</td> + <td>an array of IPTC-NAA records in the same format as those returned by get_Photoshop_IPTC</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>Photoshop_IRB_data</td> + <td>The Photoshop IRB array with the IPTC-NAA IIM resource inserted</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>Interpret_IRB_to_HTML</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Generates html showing the information contained in a Photoshop + IRB data array, as retrieved with get_Photoshop_IRB, including + any IPTC-NAA IIM records found.<br> + <br> + Note: The following resource numbers are not currently + decoded: ( Many of these do not apply to JPEG images)<br> + 0x03E9, 0x03EE, 0x03EF, 0x03F0, 0x03F1, 0x03F2, 0x03F6, 0x03F9, + 0x03FA, 0x03FB, 0x03FD, 0x03FE, 0x0400, 0x0401, 0x0402, 0x0405, + 0x040E, 0x040F, 0x0410, 0x0412, 0x0413, 0x0415, 0x0416, 0x0417, + 0x041B, 0x041C, 0x041D, 0x0BB7<br> + <br> + ( Also these Obsolete resource numbers)<br> + 0x03E8, 0x03EB, 0x03FC, 0x03FF, 0x0403 + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>IRB_array</td> + <td>a Photoshop IRB data array as from get_Photoshop_IRB</td> + </tr> + <tr> + <td width=1%>filename</td> + <td>the name of the JPEG file being processed (used by the script which displays the Photoshop thumbnail)</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>output_str</td> + <td>the HTML string</td> + </tr> + </table> + + + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>Interpret_IPTC_to_HTML</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Generates html detailing the contents a IPTC-NAA IIM array which was retrieved with the get_Photoshop_IPTC function + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>IPTC_info</td> + <td>the IPTC-NAA IIM array,as read from get_IPTC</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>OutputStr</td> + <td>A string containing the HTML</td> + </tr> + </table> + + + <br> + <br> + + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/photoshop_file_info.html b/includes/jpeg_metadata_tk/documentation/photoshop_file_info.html new file mode 100644 index 0000000..35e5f5a --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/photoshop_file_info.html @@ -0,0 +1,162 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>Photoshop File Info Function Reference</h2> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>get_photoshop_file_info</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retrieves Photoshop 'File Info' metadata in the same way that Photoshop + does. The results are returned in an array as below:<br> + <pre> + $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" => "" ); + </pre> + </td> + <tr> + <tr> + <td rowspan=3>Parameters:</td> + <td width=1%>Exif_array</td> + <td>an array containing the EXIF information to be searched, as retrieved by get_EXIF_JPEG.</td> + </tr> + <tr> + <td width=1%>XMP_array</td> + <td>an array containing the XMP information to be searched, as retrieved by read_XMP_array_from_text.</td> + </tr> + <tr> + <td width=1%>IRB_array</td> + <td>an array containing the Photoshop IRB information to be searched, as retrieved by get_Photoshop_IRB.</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>outputarray</td> + <td>an array as above, containing the Photoshop File Info data</td> + </tr> + + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>put_photoshop_file_info</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + 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: <br> + <pre> + $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" => "" ); + </pre> + </td> + <tr> + <tr> + <td rowspan=5>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>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.</td> + </tr> + <tr> + <td width=1%>new_ps_file_info_array</td> + <td>An array as above, which contains the "File Info" metadata information to be written.</td> + </tr> + <tr> + <td width=1%>Old_Exif_array</td> + <td>an array containing the EXIF information to be updated, as retrieved by get_EXIF_JPEG.</td> + </tr> + <tr> + <td width=1%>Old_XMP_array</td> + <td>an array containing the XMP information to be updated, as retrieved by read_XMP_array_from_text.</td> + </tr> + <tr> + <td width=1%>Old_IRB_array</td> + <td>an array containing the Photoshop IRB information to be updated, as retrieved by get_Photoshop_IRB.</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>jpeg_header_data</td> + <td>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.</td> + </tr> + <tr> + <td>FALSE</td> + <td>If an error occured</td> + </tr> + </table> + + <br> + <br> + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/picture_info.html b/includes/jpeg_metadata_tk/documentation/picture_info.html new file mode 100644 index 0000000..44ab6aa --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/picture_info.html @@ -0,0 +1,133 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>Picture Info Function Reference</h2> + <p> + The "App 12" segment is used by many older digital cameras, and contains + text called "Picture Info" + </p> + + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>get_jpeg_App12_Pic_Info</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retrieves the Picture Info text information from an App12 + JPEG segment and returns it as a string. Uses information + supplied by the get_jpeg_header_data function + </td> + <tr> + <tr> + <td>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>App12_Head, App12_Text</td> + <td>The text preceeding the Picture Info (often the camera manufacturer's name), plus the Picture Info Text</td> + </tr> + <tr> + <td>FALSE, FALSE</td> + <td>if an APP 12 Picture Info segment could not be found</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>put_jpeg_App12_Pic_Info</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Writes Picture Info text into an App12 JPEG segment. Uses information + supplied by the get_jpeg_header_data function. If no App12 exists + already a new one is created, otherwise it replaces the old one + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data</td> + </tr> + <tr> + <td width=1%>new_Pic_Info_Text</td> + <td>The Picture Info Text, including any header that is required</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>jpeg_header_data</td> + <td>the JPEG header array with the new Picture info segment inserted</td> + </tr> + <tr> + <td>FALSE</td> + <td>if an error occured</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>Interpret_App12_Pic_Info_to_HTML</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Generates html showing the contents of any JPEG App12 Picture Info segment + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>the JPEG header data, as retrieved from the get_jpeg_header_data function</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>output</td> + <td>the HTML</td> + </tr> + </table> + + + <br> + <br> + + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/style.css b/includes/jpeg_metadata_tk/documentation/style.css new file mode 100644 index 0000000..ae5ccff --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/style.css @@ -0,0 +1,29 @@ +body { + margin:0 0 0 0; + background: #A4C7A0; +} + +.headerbar { background:#505060; + color:#C0C070; + } +.maintext { margin-left:10%; + margin-right:10%; + } + + +a:link { + color: #00f; + background: transparent; +} + +a:visited { + color: #800080; + background: transparent; +} + +a:active { + color: green; + background: #FFD700 +} + + diff --git a/includes/jpeg_metadata_tk/documentation/tiffexample.html b/includes/jpeg_metadata_tk/documentation/tiffexample.html new file mode 100644 index 0000000..5e47aaf --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/tiffexample.html @@ -0,0 +1,57 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="examples.html">Go to Documentation - Examples</a> + + <div class="maintext"> + + <h2>Example TIFF Metadata Display Script</h2> + + <p> + This example script is included with the PHP JPEG Metadata Toolkit. It shows how the library + can be used to display detailed metadata from a TIFF image file. This is an explanation of some of the + commands used in the script. + </p> + + <br> + <br> + <a href="http://www.ozhiker.com/electronics/pjmt/library/list_contents.php4?filename=TIFFExample.php"> + Click here to see this script</a> + + <br> + <br> + <br> + <p>This script is essentially a subset of <a href="example.html">Example.php</a></p> + <br> + <br> + <br> + + <h4>TIFF EXIF</h4> + <p> + The only metadata that can be stored in a TIFF file is TIFF/EXIF information. + Displaying this information metadata in HTML format from a PHP script is very simple: + </p> + <pre style="color:brown"> + + echo Interpret_EXIF_to_HTML( <b>get_EXIF_TIFF</b>( $filename ), $filename ); + + </pre> + + <br> + <br> + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/todo.html b/includes/jpeg_metadata_tk/documentation/todo.html new file mode 100644 index 0000000..8d4e5d7 --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/todo.html @@ -0,0 +1,65 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <H2>Further Work Required</H2> + + <p> + If you would like to contribute, and extend the toolkit, here are some + things that are yet to be done + </p> + + <ul> + <li><b>Obtain format specifications for more Makernotes</b></li> + <li>Find out definitions of Print Image Matching Info tags</li> + <li>Obtain a copy of the Photoshop CS File Format Specification</li> + <li>Find out what the adobe-xap-filters tag means in XMP</li> + <li>Fully Test Functions to write EXIF data</li> + <li>Test the use of EXIF functions on TIFF Files - only JPEG's tested so far</li> + <li>Figure out a way to allow EXIF to function normally with HTTP and FTP wrappers</li> + <li>Implement decoding of Adobe segment</li> + <li>Find definition of Ducky segment and implement decoding</li> + <li>Implement decoding of Apple plist segment</li> + <li>Implement decoding of ICC Profiles</li> + <li>Implement EXIF decoding of Device Setting Description field</li> + <li>Implement EXIF decoding of SpatialFrequencyResponse field</li> + <li>Implement EXIF decoding of User comment</li> + <li>Implement EXIF decoding of OECF field</li> + <li>Implement EXIF decoding of SubjectArea field</li> + <li>Implement EXIF decoding of IFD datatype Float</li> + <li>Implement EXIF decoding of IFD datatype Double</li> + <li>Test those IPTC fields which are not used by Photoshop</li> + <li>Implement support for IPTC Extended Dataset record</li> + <li>Implement decoding of IPTC record 1:90 ( Coded Character Set )</li> + <li>Implement Display of Rasterised Caption for IPTC record 2:125</li> + <li>Implement JFIF Thumbnail display</li> + <li>Implement JFXX one and three bytes per pixel thumbnail decoding</li> + <li>Implement decoding for the many Photoshop IRB resources which are currently not supported</li> + <li>Find out what Photoshop IRB resources 1061, 1062 & 1064 are</li> + <li>Test whether the URL List field in Photoshop IRB works - No sample files available</li> + <li>Test get_Photoshop_IRB and put_Photoshop_IRB with multiple APP13 segments</li> + <li>Test the Unicode UTF-16 functions that have not been tested fully</li> + <li>Test the many RDF items that have not yet been tested - only those used by photoshop 7.0 and CS have been tested</li> + <li>Implement decoding of One Byte Per Pixel encoded JFXX Thumbnail</li> + <li>Implement decoding of Three Bytes Per Pixel encoded JFXX Thumbnail</li> + </ul> + + <br> + <br> + </div> + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/documentation/xmp.html b/includes/jpeg_metadata_tk/documentation/xmp.html new file mode 100644 index 0000000..894546f --- /dev/null +++ b/includes/jpeg_metadata_tk/documentation/xmp.html @@ -0,0 +1,228 @@ +<html> + <head> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> + <link rel=StyleSheet href="style.css" type="text/css"> + <META NAME="keywords" CONTENT="JPEG Metadata Application Segments APP0 APP1 APP2 APP3 APP12 APP13 APP14 EXIF DCF XMP RDF Photoshop IRB IPTC DCMI JFIF"> + + <title>The PHP JPEG Metadata Toolkit - Documentation</title> + </head> + + <body> + <div class="headerbar"> + <H1 align="center" style="padding:15">The PHP JPEG Metadata Toolkit - Documentation</H1> + </div> + + <a href="index.html">Go to Documentation - Home</a> + + <div class="maintext"> + + <h2>XMP / RDF / Dublin Core Function Reference</h2> + <p> + The Extensible Metadata Platform (XMP) information resides in + the "App 1" segment of a JPEG image. + An XMP segment is XML based and contains + Resource Description Framework (RDF) data, which itself can + contain the Dublin Core Metadata Initiative (DCMI) information. + </p> + + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>get_XMP_text</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Retrieves the Extensible Metadata Platform (XMP) information + from an App1 JPEG segment and returns the raw XML text as a + string. This includes the Resource Description Framework (RDF) + information and may include Dublin Core Metadata Initiative (DCMI) + information. Uses information supplied by the get_jpeg_header_data + function + </td> + <tr> + <tr> + <td>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>xmp_data</td> + <td>the string of raw XML text</td> + </tr> + <tr> + <td>FALSE</td> + <td>if an APP 1 XMP segment could not be found, or if an error occured</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>put_XMP_text</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + Adds or modifies the Extensible Metadata Platform (XMP) information + in an App1 JPEG segment. If a XMP segment already exists, it is + replaced, otherwise a new one is inserted, using the supplied data. + Uses information supplied by the get_jpeg_header_data function + </td> + <tr> + <tr> + <td rowspan=2>Parameters:</td> + <td width=1%>jpeg_header_data</td> + <td>a JPEG header data array in the same format as from get_jpeg_header_data</td> + </tr> + <tr> + <td width=1%>newXMP</td> + <td>a string containing the XMP text to be stored in the XMP segment. Should be constructed using the write_XMP_array_to_text function</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>jpeg_header_data</td> + <td>the JPEG header data array with the XMP segment added.</td> + </tr> + <tr> + <td>FALSE</td> + <td>if an error occured</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>read_XMP_array_from_text</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + <p> + An alias for read_xml_array_from_text.<br> + Parses a string containing XMP data (XML), and returns the resulting + tree structure array, which contains all the XMP (XML) information.<br> + Note: White space and comments in the XMP data (XML) are ignored<br> + Note: All text information contained in the tree structure<br> + is encoded as Unicode UTF-8. Hence text will appear as + normal ASCII except where there is an extended character. + </p> + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>xmptext</td> + <td>a string containing the XMP data (XML) to be parsed</td> + </tr> + <tr> + <td rowspan=2>Returns:</td> + <td>output</td> + <td>the tree structure array containing the XMP (XML) information</td> + </tr> + <tr> + <td>FALSE</td> + <td>if an error occured</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>write_XMP_array_to_text</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + <p> + Takes a tree structure array containing XMP (in the same format + as returned by read_XMP_array_from_text, and constructs a string + containing the equivalent XMP, including the XMP Packet header + and trailer. Produces XMP text which has correct indents, encoded + using UTF-8.<br> + Note: All text information contained in the tree structure + can be either 7-bit ASCII or encoded as Unicode UTF-8, + since UTF-8 passes 7-bit ASCII text unchanged. + </p> + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>xmparray</td> + <td>the tree structure array containing the information to be converted to XMP text</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>output_XMP_text</td> + <td>the string containing the equivalent XMP text</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + <table border cellpadding=8 cellspacing=0> + <tr> + <td>Function:</td> + <td colspan=2><b>Interpret_XMP_to_HTML</b></td> + </tr> + <tr> + <td>Description:</td> + <td colspan=2> + <p> + Generates html showing the information contained in an Extensible + Metadata Platform (XMP) tree structure array, as retrieved + with read_XMP_array_from_text + </p> + </td> + <tr> + <tr> + <td rowspan=1>Parameters:</td> + <td width=1%>XMP_array</td> + <td>a XMP tree structure array as from read_XMP_array_from_text</td> + </tr> + <tr> + <td rowspan=1>Returns:</td> + <td>output</td> + <td>the HTML string</td> + </tr> + </table> + + + <br> + <br> + <br> + <br> + + + </div> + + </body> +</html>
\ No newline at end of file diff --git a/includes/jpeg_metadata_tk/get_JFXX_thumb.php b/includes/jpeg_metadata_tk/get_JFXX_thumb.php new file mode 100644 index 0000000..801417d --- /dev/null +++ b/includes/jpeg_metadata_tk/get_JFXX_thumb.php @@ -0,0 +1,109 @@ +<?php + +/****************************************************************************** +* +* Filename: get_JFXX_thumb.php +* +* Description: This script extracts a JFXX (JPEG File Interchange Format +* Extension) thumbnail from within a JPEG file and allows it +* to be displayed +* +* Usage: get_JFXX_thumb?filename=<filename> +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + + + // Ensure that nothing can write to the standard io, before we get the header out + ob_start( ); + + + require_once 'JPEG.php'; + require_once 'JFIF.php'; + + + // retrieve the filename from the URL parameters + + $filename = $GLOBALS['HTTP_GET_VARS']['filename']; + + // Retrieve the JPEG header Data + + $jpeg_header_data = get_jpeg_header_data( $filename ); + + // Retrieve any JFXX data in the file + + $JFXX_array = get_JFXX( $jpeg_header_data ); + + // Check if JFXX data was retrieved + + if ( $JFXX_array === FALSE ) + { + // No JFXX data could be retrieved - abort + ob_end_clean ( ); + echo "<p>JFXX Data could not be retrieved</p>\n"; + return; + } + + // Check the JFXX extension code which indicates what format + // the thumbnail is encoded with + + if ( $JFXX_array['Extension_Code'] == 0x10 ) // JPEG Encoding + { + // JPEG Encoding - Output JPEG Data + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $JFXX_array['ThumbData']; + return; + } + else if ( $JFXX_array['Extension_Code'] == 0x11 ) // One Byte Per Pixel Encoding + { + // TODO: Implement decoding of One Byte Per Pixel encoded JFXX Thumbnail + return; + } + else if ( $JFXX_array['Extension_Code'] == 0x13 ) // Three Bytes Per Pixel Encoding + { + // TODO: Implement decoding of Three Bytes Per Pixel encoded JFXX Thumbnail + return; + } + else + { + // Invalid Extension Value - abort + return; + } + + + +?> diff --git a/includes/jpeg_metadata_tk/get_casio_thumb.php b/includes/jpeg_metadata_tk/get_casio_thumb.php new file mode 100644 index 0000000..f75b558 --- /dev/null +++ b/includes/jpeg_metadata_tk/get_casio_thumb.php @@ -0,0 +1,126 @@ +<?php + +/****************************************************************************** +* +* Filename: get_ps_thumb.php +* +* Description: This script extracts a Casio Type 2 EXIF Makernote Thumbnail +* from within a JPEG file and allows it to be displayed +* +* Usage: get_casio_thumb?filename=<filename> +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + + // Ensure that nothing can write to the standard io, before we get the header out + ob_start( ); + + + require_once 'EXIF.php'; + + + // retrieve the filename from the URL parameters + + $filename = $GLOBALS['HTTP_GET_VARS']['filename']; + + + // Retrieve any EXIF data in the file + + $Exif_array = get_EXIF_JPEG( $filename ); + + // Check if any EXIF data was retrieved + if ( $Exif_array === FALSE ) + { + // No EXIF data was found - abort + ob_end_clean ( ); + echo "<p>Error getting EXIF Information</p>\n"; + return; + } + + + // Check that there is at least the Zeroth IFD in the array + if ( count( $Exif_array ) < 1 ) + { + // Couldn't find the zeroth IFD + return; + } + + + // Check that the EXIF IFD exists + if ( array_key_exists( 34665, $Exif_array[0] ) ) + { + // Found the EXIF IFD, + + // Check that the makernote tag exists + if ( array_key_exists( 37500, $Exif_array[0][34665]['Data'][0] ) ) + { + // Makernote Exists + + // Check that the Makernote is Casio Type 2 + if ( $Exif_array[0][34665]['Data'][0][37500]['Makernote Tags'] == "Casio Type 2" ) + { + // Makernote is Casio Type 2 + // Check if an IFD exists for the makernote + if ( array_key_exists( 0, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'] ) ) + { + // Check if the Thumbnail offset tag 8192 exists + if ( array_key_exists( 8192, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) + { + // Found Thumbnail - Display it + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][8192]['Data']; + } + // Check if the Thumbnail offset tag 4 exists + else if ( array_key_exists( 4, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) + { + // Found Thumbnail - Display it + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][4]['Data']; + } + } + + } + } + } + else + { + } + + return; + +?> diff --git a/includes/jpeg_metadata_tk/get_exif_thumb.php b/includes/jpeg_metadata_tk/get_exif_thumb.php new file mode 100644 index 0000000..0f40090 --- /dev/null +++ b/includes/jpeg_metadata_tk/get_exif_thumb.php @@ -0,0 +1,98 @@ +<?php + +/****************************************************************************** +* +* Filename: get_exif_thumb.php +* +* Description: This script extracts a EXIF thumbnail from the first IFD of a +* JPEG file and allows it to be displayed +* +* Usage: get_exif_thumb?filename=<filename> +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + + // Ensure that nothing can write to the standard io, before we get the header out + ob_start( ); + + + require_once 'JPEG.php'; + require_once 'EXIF.php'; + + + // retrieve the filename from the URL parameters + + $filename = $GLOBALS['HTTP_GET_VARS']['filename']; + + // Retrieve any EXIF data in the file + + $Exif_array = get_EXIF_JPEG( $filename ); + + // Check if EXIF data was retrieved + + if ( $Exif_array === FALSE ) + { + // No EXIF data could be retrieved - abort + ob_end_clean ( ); + echo "<p>EXIF segment could not be retrieved</p>\n"; + return; + } + + + // Check if the First IFD exists ( The First IFD is actually the second, since there is also the zeroth IFD) + if ( count( $Exif_array ) < 2 ) + { + ob_end_clean ( ); + echo "<p>Couldn't find Thumbnail IFD</p>\n"; + return; + } + + // Check if the First IFD contains the Thumbnail tag + if ( array_key_exists( 513, $Exif_array[1] ) ) + { + // Output the thumbnail + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $Exif_array[1][513]['Data']; + } + else + { + ob_end_clean ( ); + echo "<p>Couldn't find Thumbnail Tag</p>\n"; + return; + } + +?> diff --git a/includes/jpeg_metadata_tk/get_minolta_thumb.php b/includes/jpeg_metadata_tk/get_minolta_thumb.php new file mode 100644 index 0000000..eecea8a --- /dev/null +++ b/includes/jpeg_metadata_tk/get_minolta_thumb.php @@ -0,0 +1,195 @@ +<?php + +/****************************************************************************** +* +* Filename: get_minolta_thumb.php +* +* Description: This script extracts a Minolta EXIF Makernote Thumbnail +* from within a JPEG file and allows it to be displayed +* +* Usage: get_minolta_thumb?filename=<filename> +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.00 +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + + + // Ensure that nothing can write to the standard io, before we get the header out + ob_start( ); + + + require_once 'EXIF.php'; + + + // retrieve the filename from the URL parameters + + $filename = $GLOBALS['HTTP_GET_VARS']['filename']; + + // Retrieve any EXIF data in the file + + $Exif_array = get_EXIF_JPEG( $filename ); + + + // Check if any EXIF data was retrieved + if ( $Exif_array === FALSE ) + { + // No EXIF data was found - abort + ob_end_clean ( ); + echo "<p>Error getting EXIF Information</p>\n"; + return; + } + + + // Check that there is at least the Zeroth IFD in the array + if ( count( $Exif_array ) < 1 ) + { + ob_end_clean ( ); + echo "<p>Couldn't find TIFF IFD 0</p>\n"; + return; + } + + + + // Check that the EXIF IFD exists + if ( array_key_exists( 34665, $Exif_array[0] ) ) + { + // Found the EXIF IFD, + + // Check that the makernote tag exists + if ( array_key_exists( 37500, $Exif_array[0][34665]['Data'][0] ) ) + { + // Makernote Exists + + // Check that the Makernote is Olympus + if ( $Exif_array[0][34665]['Data'][0][37500]['Makernote Tags'] == "Olympus" ) + { + // Makernote is Olympus + // Check if an IFD exists for the makernote + if ( array_key_exists( 0, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'] ) ) + { + // Check if the Thumbnail tag 0x0088 exists + if ( array_key_exists( 0x0088, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) + { + // Found a Thumbnail + // Get the data + $data = $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][0x0088]['Data']; + + // Sometimes the Minolta thumbnails are corrupt as there is no data + // Check that the data is OK + if ( $data !== FALSE ) + { + // Minolta thumbnails seem to have the first byte incorrect - this could possibly be a counter in case the thumbnail needs to span more than one tag + // Restore the first byte of the jpeg thumbnail + $data{0} = "\xff"; + + // Display the thumbnail + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $data; + } + else + { + // Thumbnail data is missing - display message + ob_end_clean ( ); + echo "<p>Thumbnail missing</p>\n"; + } + } + // Check if the Thumbnail tag 0x0081 exists + else if ( array_key_exists( 0x0081, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) ) + { + // Found a Thumbnail + // Get the data + $data = $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][0x0081]['Data']; + + // Sometimes the Minolta thumbnails are corrupt as there is no data + // Check that the data is OK + if ( $data !== FALSE ) + { + // Minolta thumbnails seem to have the first byte incorrect - this could possibly be a counter in case the thumbnail needs to span more than one tag + // Restore the first byte of the jpeg thumbnail + $data{0} = "\xff"; + + // Display the thumbnail + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print $data ; + } + else + { + // Thumbnail data is missing - display message + ob_end_clean ( ); + echo "<p>Thumbnail missing</p>\n"; + } + } + else + { + // Couldn't find a Minolta thumbnail tag - display message + ob_end_clean ( ); + echo "<p>Couldn't find Minolta Thumbnail Tag</p>\n"; + } + } + else + { + // Couldn't find an IFD in the Makernote tag - display message + ob_end_clean ( ); + echo "<p>Makernote Doesn't contain IFD 0</p>\n"; + } + + } + else + { + // Makernote does not use Olympus tags - display message + ob_end_clean ( ); + echo "<p>Not an Olympus Makernote</p>\n"; + } + } + else + { + // Couldn't find Makernote tag - display message + ob_end_clean ( ); + echo "<p>Couldn't find Makernote</p>\n"; + } + } + else + { + // Couldn't find the EXIF IFD - display message + ob_end_clean ( ); + echo "<p>Couldn't find Exif IFD</p>\n"; + } + + + return; + +?> diff --git a/includes/jpeg_metadata_tk/get_ps_thumb.php b/includes/jpeg_metadata_tk/get_ps_thumb.php new file mode 100644 index 0000000..966c328 --- /dev/null +++ b/includes/jpeg_metadata_tk/get_ps_thumb.php @@ -0,0 +1,176 @@ +<?php + +/****************************************************************************** +* +* Filename: get_ps_thumb.php +* +* Description: This script extracts a Photoshop IRB (Image Resource Block) +* thumbnail from within a JPEG file and allows it to be displayed +* +* Usage: get_ps_thumb?filename=<filename> +* +* Author: Evan Hunter +* +* Date: 23/7/2004 +* +* Project: PHP JPEG Metadata Toolkit +* +* Revision: 1.11 +* +* 1.00 -> 1.11 : Added support for Photoshop IRB thumbnails which are +* embedded within EXIF information (used in TIFF files) +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + + // Ensure that nothing can write to the standard io, before we get the header out + ob_start( ); + + + // retrieve the filename from the URL parameters + + $filename = $GLOBALS['HTTP_GET_VARS']['filename']; + + // Change: Check for file extension rather than assuming JPEG as of 1.11 + // Retrieve the Filename Extension + $path_parts = pathinfo( $filename ); + + // Check if the Extension is JPEG + if ( ( strcasecmp( $path_parts["extension"], "jpg" ) == 0 ) || + ( strcasecmp( $path_parts["extension"], "jpeg" ) == 0 ) ) + { + // JPEG Extension + + require_once 'JPEG.php'; + require_once 'Photoshop_IRB.php'; + + // Retrieve the JPEG header Data + + $jpeg_header_data = get_jpeg_header_data( $filename ); + + // Retrieve any Photoshop IRB data in the file + + $IRB_array = get_Photoshop_IRB( $jpeg_header_data ); + + // Check if Photoshop IRB data was retrieved + + if ( $IRB_array === FALSE ) + { + // No Photoshop IRB data could be retrieved - abort + ob_end_clean ( ); + echo "<p>Photoshop IRB could not be retrieved from the JPEG file</p>\n"; + return; + } + + // Cycle through the resources in the Photoshop IRB + // Until either a thumbnail resource is found or + // there are no more resources + $i = 0; + while ( ( $i < count( $IRB_array ) ) && + ( $IRB_array[$i]['ResID'] != 0x0409 ) && + ( $IRB_array[$i]['ResID'] != 0x040C ) ) + { + $i++; + } + + + // Check if a thumbnail was found + if ( $i < count( $IRB_array ) ) + { + // A thumbnail was found, Display it + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print substr( $IRB_array[$i]['ResData'] , 28 ); + } + } + // Change: Add support for TIFF Photoshop IRB thumbnails as of 1.11 + // Check if file has TIFF extension + else if ( ( strcasecmp( $path_parts["extension"], "tif" ) == 0 ) || + ( strcasecmp( $path_parts["extension"], "tiff" ) == 0 ) ) + { + // TIFF Extension + + require_once 'EXIF.php'; + + // Retrieve the EXIF info + $exif_array = get_EXIF_TIFF( $filename ); + + // Retrieve any Photoshop IRB data in the EXIF + if ( ( array_key_exists( 0, $exif_array ) ) && + ( array_key_exists( 34377, $exif_array[0] ) ) && + ( array_key_exists( 'Data', $exif_array[0][34377] ) ) ) + { + $IRB_array = $exif_array[0][34377]['Data']; + + // Check if Photoshop IRB data was retrieved + + if ( $IRB_array === FALSE ) + { + // No Photoshop IRB data could be retrieved - abort + ob_end_clean ( ); + echo "<p>Photoshop IRB could not be retrieved from the TIFF file</p>\n"; + return; + } + + // Cycle through the resources in the Photoshop IRB + // Until either a thumbnail resource is found or + // there are no more resources + $i = 0; + while ( ( $i < count( $IRB_array ) ) && + ( $IRB_array[$i]['ResID'] != 0x0409 ) && + ( $IRB_array[$i]['ResID'] != 0x040C ) ) + { + $i++; + } + + + // Check if a thumbnail was found + if ( $i < count( $IRB_array ) ) + { + // A thumbnail was found, Display it + ob_end_clean ( ); + header("Content-type: image/jpeg"); + print substr( $IRB_array[$i]['ResData'] , 28 ); + } + } + else + { + // Embedded Photoshop IRB block not found + ob_end_clean ( ); + echo "No Photoshop IRB found within EXIF"; + } + } + else + { + // Unknown extension + ob_end_clean ( ); + echo "Unknown file Type"; + } + + +?> diff --git a/includes/jpeg_metadata_tk/pjmt_utils.php b/includes/jpeg_metadata_tk/pjmt_utils.php new file mode 100644 index 0000000..07579b3 --- /dev/null +++ b/includes/jpeg_metadata_tk/pjmt_utils.php @@ -0,0 +1,150 @@ +<?php +/****************************************************************************** +* +* Filename: pjmt_utils.php +* +* Description: Provides various utility functions for the toolkit +* +* Author: Evan Hunter +* +* Date: 20/1/2005 +* +* Project: JPEG Metadata +* +* Revision: 1.11 +* +* NOTE: This file will change with every revision update, hence will not +* be shown in the changes list +* +* URL: http://electronics.ozhiker.com +* +* Copyright: Copyright Evan Hunter 2004 +* +* 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 +* +******************************************************************************/ + + +/****************************************************************************** +* +* Function: get_relative_path +* +* Description: Creates a relative path version of a file or directiroy name, +* given a directory that it will be relative to. +* +* Parameters: target - a file or directory name which will be made relative +* fromdir - the directory which the returned path is relative to +* +* Returns: output - the relative path +* +******************************************************************************/ + +function get_relative_path( $target, $fromdir ) +{ + // Check that the fromdir has a trailing slash, otherwise realpath will + // strip the last directory name off + if ( ( $fromdir[ strlen( $fromdir ) - 1 ] != "\\" ) && + ( $fromdir[ strlen( $fromdir ) - 1 ] != "/" ) ) + { + $fromdir .= "/"; + } + + // get a real directory name for each of the target and from directory + $from = realpath( $fromdir ); + $target = realpath( $target ); + $to = dirname( $target ); + + // Can't get relative path with drive in path - remove it + if ( ( $colonpos = strpos( $target, ":" ) ) != FALSE ) + { + $target = substr( $target, $colonpos+1 ); + } + if ( ( $colonpos = strpos( $from, ":" ) ) != FALSE ) + { + $from = substr( $from, $colonpos+1 ); + } + if ( ( $colonpos = strpos( $to, ":" ) ) != FALSE ) + { + $to = substr( $to, $colonpos+1 ); + } + + + $path = "../"; + $posval = 0; + // Step through the paths until a difference is found (ignore slash, backslash differences + // or the end of one is found + while ( ( ( $from[$posval] == $to[$posval] ) || + ( ( $from[$posval] == "\\" ) && ( $to[$posval] == "/" ) ) || + ( ( $from[$posval] == "/" ) && ( $to[$posval] == "\\" ) ) ) && + ( $from[$posval] && $to[$posval] ) ) + { + $posval++; + } + // Save the position of the first difference + $diffpos = $posval; + + // Check if the directories are the same or + // the if target is in a subdirectory of the fromdir + if ( ( ! $from[$posval] ) && + ( $to[$posval] == "/" || $to[$posval] == "\\" || !$to[$posval] ) ) + { + // target is in fromdir or a subdirectory + // Build relative path starting with a ./ + return ( "./" . substr( $target, $posval+1, strlen( $target ) ) ); + } + else + { + // target is outside the fromdir branch + // find out how many "../"'s are necessary + // Step through the fromdir path, checking for slashes + // each slash encountered requires a "../" + while ( $from[++$posval] ) + { + // Check for slash + if ( ( $from[$posval] == "/" ) || ( $from[$posval] == "\\" ) ) + { + // Found a slash, add a "../" + $path .= "../"; + } + } + + // Search backwards to find where the first common directory + // as some letters in the first different directory names + // may have been the same + $diffpos--; + while ( ( $to[$diffpos] != "/" ) && ( $to[$diffpos] != "\\" ) && $to[$diffpos] ) + { + $diffpos--; + } + // Build relative path to return + + return ( $path . substr( $target, $diffpos+1, strlen( $target ) ) ); + } +} + +/****************************************************************************** +* End of Function: get_relative_path +******************************************************************************/ + + +?>
\ No newline at end of file |
