summaryrefslogtreecommitdiff
path: root/includes/jpeg_metadata_tk
diff options
context:
space:
mode:
Diffstat (limited to 'includes/jpeg_metadata_tk')
-rw-r--r--includes/jpeg_metadata_tk/COPYING.txt340
-rw-r--r--includes/jpeg_metadata_tk/EXIF.php2764
-rw-r--r--includes/jpeg_metadata_tk/EXIF_Makernote.php334
-rw-r--r--includes/jpeg_metadata_tk/EXIF_Tags.php915
-rw-r--r--includes/jpeg_metadata_tk/Edit_File_Info.php575
-rw-r--r--includes/jpeg_metadata_tk/IPTC.php691
-rw-r--r--includes/jpeg_metadata_tk/JFIF.php438
-rw-r--r--includes/jpeg_metadata_tk/JPEG.php973
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/Pentax.php353
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/agfa.php117
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/canon.php748
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/casio.php575
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/epson.php119
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/fujifilm.php344
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/konica_minolta.php745
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/kyocera.php241
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/nikon.php731
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/olympus.php486
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/panasonic.php292
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/ricoh.php415
-rw-r--r--includes/jpeg_metadata_tk/Makernotes/sony.php244
-rw-r--r--includes/jpeg_metadata_tk/PIM.php279
-rw-r--r--includes/jpeg_metadata_tk/Photoshop_File_Info.php2498
-rw-r--r--includes/jpeg_metadata_tk/Photoshop_IRB.php1514
-rw-r--r--includes/jpeg_metadata_tk/PictureInfo.php284
-rw-r--r--includes/jpeg_metadata_tk/Toolkit_Version.php50
-rw-r--r--includes/jpeg_metadata_tk/Unicode.php1227
-rw-r--r--includes/jpeg_metadata_tk/Write_File_Info.php196
-rw-r--r--includes/jpeg_metadata_tk/XML.php396
-rw-r--r--includes/jpeg_metadata_tk/XMP.php1063
-rw-r--r--includes/jpeg_metadata_tk/documentation/Camera_List_1.0.pdfbin0 -> 79294 bytes
-rw-r--r--includes/jpeg_metadata_tk/documentation/Edit_File_Info_Example.php248
-rw-r--r--includes/jpeg_metadata_tk/documentation/Example.php218
-rw-r--r--includes/jpeg_metadata_tk/documentation/TIFFExample.php121
-rw-r--r--includes/jpeg_metadata_tk/documentation/changes.html257
-rw-r--r--includes/jpeg_metadata_tk/documentation/css_terms.html171
-rw-r--r--includes/jpeg_metadata_tk/documentation/edit_write_file_info.html272
-rw-r--r--includes/jpeg_metadata_tk/documentation/example.html131
-rw-r--r--includes/jpeg_metadata_tk/documentation/examples.html62
-rw-r--r--includes/jpeg_metadata_tk/documentation/exif.html284
-rw-r--r--includes/jpeg_metadata_tk/documentation/index.html60
-rw-r--r--includes/jpeg_metadata_tk/documentation/intro.html75
-rw-r--r--includes/jpeg_metadata_tk/documentation/jfif.html237
-rw-r--r--includes/jpeg_metadata_tk/documentation/jpeg.html303
-rw-r--r--includes/jpeg_metadata_tk/documentation/photoshop.html262
-rw-r--r--includes/jpeg_metadata_tk/documentation/photoshop_file_info.html162
-rw-r--r--includes/jpeg_metadata_tk/documentation/picture_info.html133
-rw-r--r--includes/jpeg_metadata_tk/documentation/style.css29
-rw-r--r--includes/jpeg_metadata_tk/documentation/tiffexample.html57
-rw-r--r--includes/jpeg_metadata_tk/documentation/todo.html65
-rw-r--r--includes/jpeg_metadata_tk/documentation/xmp.html228
-rw-r--r--includes/jpeg_metadata_tk/get_JFXX_thumb.php109
-rw-r--r--includes/jpeg_metadata_tk/get_casio_thumb.php126
-rw-r--r--includes/jpeg_metadata_tk/get_exif_thumb.php98
-rw-r--r--includes/jpeg_metadata_tk/get_minolta_thumb.php195
-rw-r--r--includes/jpeg_metadata_tk/get_ps_thumb.php176
-rw-r--r--includes/jpeg_metadata_tk/pjmt_utils.php150
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'] = "&nbsp;";
+ }
+
+ // 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'] = "&nbsp;";
+ }
+
+ // 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&#xA;";
+ }
+ ?></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&#xA;";
+ }
+ ?>
+ </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" => "&#xA;") ) ;
+ }
+ }
+ }
+
+
+ /***************************************/
+
+ // 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" => "&#xA;") ) ;
+ }
+ }
+ }
+
+
+ /***************************************/
+
+ // 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" => "&#x09;", "\x0A" => "&#x0A;", "\x0D" => "&#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" => "&#x09;", "\x0A" => "&#x0A;", "\x0D" => "&#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 &amp;
+ $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 &amp;
+ // 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};)/","&amp;" , 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 &amp;
+ $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 &amp;
+ // 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};)/","&amp;" , 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 (&#xA;) 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 = "&nbsp;";
+ }
+ // 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
new file mode 100644
index 0000000..711e2a7
--- /dev/null
+++ b/includes/jpeg_metadata_tk/documentation/Camera_List_1.0.pdf
Binary files differ
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 &quot;<?php echo $filename; ?>&quot;</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 &quot;<?php echo $filename; ?>&quot;</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&nbsp;-&gt;&nbsp;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 -&gt; 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 -&gt; 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 -&gt; 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 -&gt; 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 &quot;filename&quot; to &quot;jpeg_fname&quot;
+ to stop script-kiddies using the google command &quot;allinurl:*.php?filename=*&quot;
+ 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 -&gt; 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 &lt;HEAD&gt; section of the HTML page:</p>
+ <pre style="color:brown">
+ &lt;META HTTP-EQUIV=&quot;Content-Style-Type&quot; CONTENT=&quot;text/css&quot;&gt;
+ &lt;STYLE TYPE=&quot;text/css&quot; MEDIA=&quot;screen, print, projection&quot;&gt;
+ &lt;!--
+ 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}
+ --&gt;
+ &lt;/STYLE&gt;
+ </pre>
+
+
+ <h4>External Style Sheets</h4>
+ <p>Include the following in the &lt;HEAD&gt; section of the HTML page:</p>
+ <pre style="color:brown">
+ &lt;link rel=StyleSheet href=&quot;style.css&quot; type=&quot;text/css&quot;&gt;
+ </pre>
+ <p>Create an external file called &quot;style.css&quot; 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 &quot;File Info&quot; 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&quot;s &quot;File Info&quot; 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 &quot;File Info&quot; 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
+ &lt;HTML&gt;, &lt;HEAD&gt; and &lt;BODY&gt; 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 &quot;output.jpg&quot; :
+ </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&quot;s &quot;File Info&quot; 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 &quot;Meta&quot; 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 &amp; Intrinsic JPEG Values</a></td></tr>
+ <tr><td><a href="exif.html">EXIF, TIFF/EP, DCF, and Kodak &quot;Meta&quot;</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 &amp; 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 &amp; JFIF Extension Function Reference</h2>
+ <p>
+ The &quot;App 0&quot; segment is often used by the JPEG File Interchange Format (JFIF) information.
+ Sometimes, there is an additional &quot;App 0&quot; 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 &amp; 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 &quot;App 13&quot; 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 &quot;File Info&quot; metadata in the same way that Photoshop
+ does. The &quot;File Info&quot; 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 &quot;File Info&quot; 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 &quot;File Info&quot; 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 &quot;App 12&quot; segment is used by many older digital cameras, and contains
+ text called &quot;Picture Info&quot;
+ </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 &quot;App 1&quot; 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