The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#------------------------------------------------------------------------------
# File:         Kodak.pm
#
# Description:  Kodak EXIF maker notes and APP3 "Meta" tags
#
# Revisions:    03/28/2005  - P. Harvey Created
#
# References:   1) http://search.cpan.org/dist/Image-MetaData-JPEG/
#               2) http://www.ozhiker.com/electronics/pjmt/jpeg_info/meta.html
#               3) http://www.cybercom.net/~dcoffin/dcraw/
#               4) Iliah Borg private communication (LibRaw)
#
# Notes:        There really isn't much public information about Kodak formats.
#               The only source I could find was Image::MetaData::JPEG, which
#               didn't provide information about decoding the tag values.  So
#               this module represents a lot of work downloading sample images
#               (about 100MB worth!), and testing with my daughter's CX4200.
#------------------------------------------------------------------------------

package Image::ExifTool::Kodak;

use strict;
use vars qw($VERSION);
use Image::ExifTool qw(:DataAccess :Utils);
use Image::ExifTool::Exif;

$VERSION = '1.41';

sub ProcessKodakIFD($$$);
sub ProcessKodakText($$$);
sub WriteKodakIFD($$$);

# Kodak type 1 maker notes (ref 1)
%Image::ExifTool::Kodak::Main = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    NOTES => q{
        The table below contains the most common set of Kodak tags.  The following
        Kodak camera models have been tested and found to use these tags: C360,
        C663, C875, CX6330, CX6445, CX7330, CX7430, CX7525, CX7530, DC4800, DC4900,
        DX3500, DX3600, DX3900, DX4330, DX4530, DX4900, DX6340, DX6440, DX6490,
        DX7440, DX7590, DX7630, EasyShare-One, LS420, LS443, LS633, LS743, LS753,
        V530, V550, V570, V603, V610, V705, Z650, Z700, Z710, Z730, Z740, Z760 and
        Z7590.
    },
    WRITABLE => 1,
    FIRST_ENTRY => 8,
    0x00 => {
        Name => 'KodakModel',
        Format => 'string[8]',
    },
    0x09 => {
        Name => 'Quality',
        PrintConv => { #PH
            1 => 'Fine',
            2 => 'Normal',
        },
    },
    0x0a => {
        Name => 'BurstMode',
        PrintConv => { 0 => 'Off', 1 => 'On' },
    },
    0x0c => {
        Name => 'KodakImageWidth',
        Format => 'int16u',
    },
    0x0e => {
        Name => 'KodakImageHeight',
        Format => 'int16u',
    },
    0x10 => {
        Name => 'YearCreated',
        Groups => { 2 => 'Time' },
        Format => 'int16u',
    },
    0x12 => {
        Name => 'MonthDayCreated',
        Groups => { 2 => 'Time' },
        Format => 'int8u[2]',
        ValueConv => 'sprintf("%.2d:%.2d",split(" ", $val))',
        ValueConvInv => '$val=~tr/:./ /;$val',
    },
    0x14 => {
        Name => 'TimeCreated',
        Groups => { 2 => 'Time' },
        Format => 'int8u[4]',
        Shift => 'Time',
        ValueConv => 'sprintf("%.2d:%.2d:%.2d.%.2d",split(" ", $val))',
        ValueConvInv => '$val=~tr/:./ /;$val',
    },
    0x18 => {
        Name => 'BurstMode2',
        Format => 'int16u',
        Unknown => 1, # not sure about this tag (or other 'Unknown' tags)
    },
    0x1b => {
        Name => 'ShutterMode',
        PrintConv => { #PH
            0 => 'Auto',
            8 => 'Aperture Priority',
            32 => 'Manual?',
        },
    },
    0x1c => {
        Name => 'MeteringMode',
        PrintConv => { #PH
            0 => 'Multi-segment',
            1 => 'Center-weighted average',
            2 => 'Spot',
        },
    },
    0x1d => 'SequenceNumber',
    0x1e => {
        Name => 'FNumber',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => 'int($val * 100 + 0.5)',
    },
    0x20 => {
        Name => 'ExposureTime',
        Format => 'int32u',
        ValueConv => '$val / 1e5',
        ValueConvInv => '$val * 1e5',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    0x24 => {
        Name => 'ExposureCompensation',
        Format => 'int16s',
        ValueConv => '$val / 1000',
        ValueConvInv => '$val * 1000',
        PrintConv => '$val > 0 ? "+$val" : $val',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    0x26 => {
        Name => 'VariousModes',
        Format => 'int16u',
        Unknown => 1,
    },
    0x28 => {
        Name => 'Distance1',
        Format => 'int32u',
        Unknown => 1,
    },
    0x2c => {
        Name => 'Distance2',
        Format => 'int32u',
        Unknown => 1,
    },
    0x30 => {
        Name => 'Distance3',
        Format => 'int32u',
        Unknown => 1,
    },
    0x34 => {
        Name => 'Distance4',
        Format => 'int32u',
        Unknown => 1,
    },
    0x38 => {
        Name => 'FocusMode',
        PrintConv => {
            0 => 'Normal',
            2 => 'Macro',
        },
    },
    0x3a => {
        Name => 'VariousModes2',
        Format => 'int16u',
        Unknown => 1,
    },
    0x3c => {
        Name => 'PanoramaMode',
        Format => 'int16u',
        Unknown => 1,
    },
    0x3e => {
        Name => 'SubjectDistance',
        Format => 'int16u',
        Unknown => 1,
    },
    0x40 => {
        Name => 'WhiteBalance',
        Priority => 0,
        PrintConv => { #PH
            0 => 'Auto',
            1 => 'Flash?',
            2 => 'Tungsten',
            3 => 'Daylight',
        },
    },
    0x5c => {
        Name => 'FlashMode',
        Flags => 'PrintHex',
        # various models express this number differently
        PrintConv => { #PH
            0x00 => 'Auto',
            0x01 => 'Fill Flash',
            0x02 => 'Off',
            0x03 => 'Red-Eye',
            0x10 => 'Fill Flash',
            0x20 => 'Off',
            0x40 => 'Red-Eye?',
        },
    },
    0x5d => {
        Name => 'FlashFired',
        PrintConv => { 0 => 'No', 1 => 'Yes' },
    },
    0x5e => {
        Name => 'ISOSetting',
        Format => 'int16u',
        PrintConv => '$val ? $val : "Auto"',
        PrintConvInv => '$val=~/^\d+$/ ? $val : 0',
    },
    0x60 => {
        Name => 'ISO',
        Format => 'int16u',
    },
    0x62 => {
        Name => 'TotalZoom',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0x64 => {
        Name => 'DateTimeStamp',
        Format => 'int16u',
        PrintConv => '$val ? "Mode $val" : "Off"',
        PrintConvInv => '$val=~tr/0-9//dc; $val ? $val : 0',
    },
    0x66 => {
        Name => 'ColorMode',
        Format => 'int16u',
        Flags => 'PrintHex',
        # various models express this number differently
        PrintConv => { #PH
            0x01 => 'B&W',
            0x02 => 'Sepia',
            0x03 => 'B&W Yellow Filter',
            0x04 => 'B&W Red Filter',
            0x20 => 'Saturated Color',
            0x40 => 'Neutral Color',
            0x100 => 'Saturated Color',
            0x200 => 'Neutral Color',
            0x2000 => 'B&W',
            0x4000 => 'Sepia',
        },
    },
    0x68 => {
        Name => 'DigitalZoom',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0x6b => {
        Name => 'Sharpness',
        Format => 'int8s',
        %Image::ExifTool::Exif::printParameter,
    },
);

# Kodak type 2 maker notes (ref PH)
%Image::ExifTool::Kodak::Type2 = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    NOTES => q{
        These tags are used by the Kodak DC220, DC260, DC265 and DC290,
        Hewlett-Packard PhotoSmart 618, C500 and C912, Pentax EI-200 and EI-2000,
        and Minolta EX1500Z.
    },
    WRITABLE => 1,
    FIRST_ENTRY => 0,
    0x08 => {
        Name => 'KodakMaker',
        Format => 'string[32]',
    },
    0x28 => {
        Name => 'KodakModel',
        Format => 'string[32]',
    },
    0x6c => {
        Name => 'KodakImageWidth',
        Format => 'int32u',
    },
    0x70 => {
        Name => 'KodakImageHeight',
        Format => 'int32u',
    },
);

# Kodak type 3 maker notes (ref PH)
%Image::ExifTool::Kodak::Type3 = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    NOTES => 'These tags are used by the DC240, DC280, DC3400 and DC5000.',
    WRITABLE => 1,
    FIRST_ENTRY => 0,
    0x0c => {
        Name => 'YearCreated',
        Groups => { 2 => 'Time' },
        Format => 'int16u',
    },
    0x0e => {
        Name => 'MonthDayCreated',
        Groups => { 2 => 'Time' },
        Format => 'int8u[2]',
        ValueConv => 'sprintf("%.2d:%.2d",split(" ", $val))',
        ValueConvInv => '$val=~tr/:./ /;$val',
    },
    0x10 => {
        Name => 'TimeCreated',
        Groups => { 2 => 'Time' },
        Format => 'int8u[4]',
        Shift => 'Time',
        ValueConv => 'sprintf("%2d:%.2d:%.2d.%.2d",split(" ", $val))',
        ValueConvInv => '$val=~tr/:./ /;$val',
    },
    0x1e => {
        Name => 'OpticalZoom',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0x37 => {
        Name => 'Sharpness',
        Format => 'int8s',
        %Image::ExifTool::Exif::printParameter,
    },
    0x38 => {
        Name => 'ExposureTime',
        Format => 'int32u',
        ValueConv => '$val / 1e5',
        ValueConvInv => '$val * 1e5',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    0x3c => {
        Name => 'FNumber',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => 'int($val * 100 + 0.5)',
    },
    0x4e => {
        Name => 'ISO',
        Format => 'int16u',
    },
);

# Kodak type 4 maker notes (ref PH)
%Image::ExifTool::Kodak::Type4 = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    NOTES => 'These tags are used by the DC200 and DC215.',
    WRITABLE => 1,
    FIRST_ENTRY => 0,
    0x20 => {
        Name => 'OriginalFileName',
        Format => 'string[12]',
    },
);

# Kodak type 5 maker notes (ref PH)
%Image::ExifTool::Kodak::Type5 = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    NOTES => q{
        These tags are used by the CX4200, CX4210, CX4230, CX4300, CX4310, CX6200
        and CX6230.
    },
    WRITABLE => 1,
    FIRST_ENTRY => 0,
    0x14 => {
        Name => 'ExposureTime',
        Format => 'int32u',
        ValueConv => '$val / 1e5',
        ValueConvInv => '$val * 1e5',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    0x1a => {
        Name => 'WhiteBalance',
        PrintConv => {
            1 => 'Daylight',
            2 => 'Flash',
            3 => 'Tungsten',
        },
    },
    0x1c => {
        Name => 'FNumber',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => 'int($val * 100 + 0.5)',
    },
    0x1e => {
        Name => 'ISO',
        Format => 'int16u',
    },
    0x20 => {
        Name => 'OpticalZoom',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0x22 => {
        Name => 'DigitalZoom',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0x27 => {
        Name => 'FlashMode',
        PrintConv => {
            0 => 'Auto',
            1 => 'On',
            2 => 'Off',
            3 => 'Red-Eye',
        },
    },
    0x2a => {
        Name => 'ImageRotated',
        PrintConv => { 0 => 'No', 1 => 'Yes' },
    },
    0x2b => {
        Name => 'Macro',
        PrintConv => { 0 => 'On', 1 => 'Off' },
    },
);

# Kodak type 6 maker notes (ref PH)
%Image::ExifTool::Kodak::Type6 = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    NOTES => 'These tags are used by the DX3215 and DX3700.',
    WRITABLE => 1,
    FIRST_ENTRY => 0,
    0x10 => {
        Name => 'ExposureTime',
        Format => 'int32u',
        ValueConv => '$val / 1e5',
        ValueConvInv => '$val * 1e5',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    0x14 => {
        Name => 'ISOSetting',
        Format => 'int32u',
        Unknown => 1,
    },
    0x18 => {
        Name => 'FNumber',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => 'int($val * 100 + 0.5)',
    },
    0x1a => {
        Name => 'ISO',
        Format => 'int16u',
    },
    0x1c => {
        Name => 'OpticalZoom',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0x1e => {
        Name => 'DigitalZoom',
        Format => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0x22 => {
        Name => 'Flash',
        Format => 'int16u',
        PrintConv => {
            0 => 'No Flash',
            1 => 'Fired',
        },
    },
);

# Kodak type 7 maker notes (ref PH)
%Image::ExifTool::Kodak::Type7 = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    WRITABLE => 1,
    FIRST_ENTRY => 0,
    NOTES => q{
        The maker notes of models such as the C340, C433, CC533, LS755, V803 and
        V1003 seem to start with the camera serial number.  The C310, C315, C330,
        C643, C743, CD33, CD43, CX7220 and CX7300 maker notes are also decoded using
        this table, although the strings for these cameras don't conform to the
        usual Kodak serial number format, and instead have the model name followed
        by 8 digits.
    },
    0 => { # (not confirmed)
        Name => 'SerialNumber',
        Format => 'string[16]',
        ValueConv => '$val=~s/\s+$//; $val', # remove trailing whitespace
        ValueConvInv => '$val',
    },
);

# Kodak IFD-format maker notes (ref PH)
%Image::ExifTool::Kodak::Type8 = (
    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    NOTES => q{
        Kodak models such as the ZD710, P712, P850, P880, V1233, V1253, V1275,
        V1285, Z612, Z712, Z812, Z885 use standard TIFF IFD format for the maker
        notes.  In keeping with Kodak's strategy of inconsistent makernotes, models
        such as the M380, M1033, M1093, V1073, V1273, Z1012, Z1085 and Z8612
        also use these tags, but these makernotes begin with a TIFF header instead
        of an IFD entry count and use relative instead of absolute offsets.  There
        is a large amount of information stored in these maker notes (apparently
        with much duplication), but relatively few tags have so far been decoded.
    },
    0xfc00 => [{
        Name => 'SubIFD0',
        Condition => '$format eq "undef"',
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        NestedHtmlDump => 2, # (so HtmlDump doesn't show these as double-referenced)
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD0',
            Base => '$start',
            ProcessProc => \&ProcessKodakIFD,
            WriteProc => \&WriteKodakIFD,
        },
    },{
        Name => 'SubIFD0',
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        Flags => 'SubIFD',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD0',
            Start => '$val',
            # (odd but true: the Base for this SubIFD is different than 0xfc01-0xfc06)
        },
    }],
    # SubIFD1 and higher data is preceded by a TIFF byte order mark to indicate
    # the byte ordering used.  Beginning with the M580, these subdirectories are
    # stored as 'undef' data rather than as a standard EXIF SubIFD.
    0xfc01 => [{
        Name => 'SubIFD1',
        Condition => '$format eq "undef"',
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        NestedHtmlDump => 2,
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD1',
            Base => '$start',
        },
    },{
        Name => 'SubIFD1',
        Condition => '$$valPt ne "\0\0\0\0"',   # may be zero if dir doesn't exist
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        Flags => 'SubIFD',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD1',
            Start => '$val',
            Base => '$start',
        },
    }],
    0xfc02 => [{
        Name => 'SubIFD2',
        Condition => '$format eq "undef"',
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        NestedHtmlDump => 2,
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD2',
            Base => '$start',
        },
    },{
        Name => 'SubIFD2',
        Condition => '$$valPt ne "\0\0\0\0"',   # may be zero if dir doesn't exist
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        Flags => 'SubIFD',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD2',
            Start => '$val',
            Base => '$start',
        },
    }],
    0xfc03 => [{
        Name => 'SubIFD3',
        Condition => '$format eq "undef"',
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        NestedHtmlDump => 2,
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD3',
            Base => '$start',
        },
    },{
        Name => 'SubIFD3',
        Condition => '$$valPt ne "\0\0\0\0"',   # may be zero if dir doesn't exist
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        Flags => 'SubIFD',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD3',
            Start => '$val',
            Base => '$start',
        },
    }],
    # (SubIFD4 has the pointer zeroed in my samples, but support it
    # in case it is used by future models -- ignored if pointer is zero)
    0xfc04 => [{
        Name => 'SubIFD4',
        Condition => '$format eq "undef"',
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        NestedHtmlDump => 2,
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD4',
            Base => '$start',
        },
    },{
        Name => 'SubIFD4',
        Condition => '$$valPt ne "\0\0\0\0"',   # may be zero if dir doesn't exist
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        Flags => 'SubIFD',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD4',
            Start => '$val',
            Base => '$start',
        },
    }],
    0xfc05 => [{
        Name => 'SubIFD5',
        Condition => '$format eq "undef"',
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        NestedHtmlDump => 2,
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD5',
            Base => '$start',
        },
    },{
        Name => 'SubIFD5',
        Condition => '$$valPt ne "\0\0\0\0"',   # may be zero if dir doesn't exist
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        Flags => 'SubIFD',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD5',
            Start => '$val',
            Base => '$start',
        },
    }],
    0xfc06 => [{ # new for the M580
        Name => 'SubIFD6',
        Condition => '$format eq "undef"',
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        NestedHtmlDump => 2,
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD6',
            Base => '$start',
        },
    },{
        Name => 'SubIFD6',
        Condition => '$$valPt ne "\0\0\0\0"',   # may be zero if dir doesn't exist
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        Flags => 'SubIFD',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD6',
            Start => '$val',
            Base => '$start',
        },
    }],
    0xfcff => {
        Name => 'SubIFD255',
        Condition => '$format eq "undef"',
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        NestedHtmlDump => 2,
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SubIFD0',
            # (uses the same Base as the main MakerNote IFD)
        },
    },
    0xff00 => {
        Name => 'CameraInfo',
        Condition => '$$valPt ne "\0\0\0\0"',   # may be zero if dir doesn't exist
        Groups => { 1 => 'MakerNotes' },        # SubIFD needs group 1 set
        Flags => 'SubIFD',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::CameraInfo',
            Start => '$val',
            # (uses the same Base as the main MakerNote IFD)
        },
    },
);

# Kodak type 9 maker notes (ref PH)
%Image::ExifTool::Kodak::Type9 = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    WRITABLE => 1,
    FIRST_ENTRY => 0,
    NOTES => q{
        These tags are used by the Kodak C140, C180, C913, C1013, M320, M340 and
        M550, as well as various cameras marketed by other manufacturers.
    },
    0x0c => [
        {
            Name => 'FNumber',
            Condition => '$$self{Make} =~ /Kodak/i',
            Format => 'int16u',
            ValueConv => '$val / 100',
            ValueConvInv => 'int($val * 100 + 0.5)',
        },{
            Name => 'FNumber',
            Format => 'int16u',
            ValueConv => '$val / 10',
            ValueConvInv => 'int($val * 10 + 0.5)',
        },
    ],
    0x10 => {
        Name => 'ExposureTime',
        Format => 'int32u',
        ValueConv => '$val / 1e6',
        ValueConvInv => '$val * 1e6',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    0x14 => {
        Name => 'DateTimeOriginal',
        Description => 'Date/Time Original',
        Groups => { 2 => 'Time' },
        Format => 'string[20]',
        Shift => 'Time',
        ValueConv => '$val=~s{/}{:}g; $val',
        ValueConvInv => '$val=~s{^(\d{4}):(\d{2}):}{$1/$2/}; $val',
        PrintConv => '$self->ConvertDateTime($val)',
        PrintConvInv => '$self->InverseDateTime($val,0)',
    },
    0x34 => {
        Name => 'ISO',
        Format => 'int16u',
    },
    0x57 => {
        Name => 'FirmwareVersion',
        Condition => '$$self{Make} =~ /Kodak/i',
        Format => 'string[16]',
        Notes => 'Kodak only',
    },
    0xa8 => {
        Name => 'UnknownNumber', # (was SerialNumber, but not unique for all cameras. eg. C1013)
        Condition => '$$self{Make} =~ /Kodak/i and $$valPt =~ /^([A-Z0-9]{1,11}\0|[A-Z0-9]{12})/i',
        Format => 'string[12]',
        Notes => 'Kodak only',
        Writable => 0,
    },
    0xc4 => {
        Name => 'UnknownNumber', # (confirmed NOT to be serial number for Easyshare Mini - PH)
        Condition => '$$self{Make} =~ /Kodak/i and $$valPt =~ /^([A-Z0-9]{1,11}\0|[A-Z0-9]{12})/i',
        Format => 'string[12]',
        Notes => 'Kodak only',
        Writable => 0,
    },
);

# more Kodak IFD-format maker notes (ref PH)
%Image::ExifTool::Kodak::Type10 = (
    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PRIORITY => 0,
    NOTES => q{
        Another variation of the IFD-format type, this time with just a byte order
        indicator instead of a full TIFF header.  These tags are used by the Z980.
    },
    # 0x01 int16u - always 0
    0x02 => {
        Name => 'PreviewImageSize',
        Writable => 'int16u',
        Count => 2,
    },
    # 0x03 int32u - ranges from about 33940 to 40680
    # 0x04 int32u - always 18493
    # 0x06 undef[4] - 07 d9 04 11
    # 0x07 undef[3] - varies
    # 0x08 int16u - 1 (mostly), 2
    # 0x09 int16u - 255
    # 0x0b int16u[2] - '0 0' (mostly), '20 0', '21 0', '1 0'
    # 0x0c int16u - 1 (mostly), 3, 259, 260
    # 0x0d int16u - 0
    # 0x0e int16u - 0, 1, 2 (MeteringMode? 0=Partial, 1,2=Multi)
    # 0x0f int16u - 0, 5 (MeteringMode? 0=Multi, 5=Partial)
    # 0x10 int16u - ranges from about 902 to 2308
    0x12 => {
        Name => 'ExposureTime',
        Writable => 'int32u',
        ValueConv => '$val / 1e5',
        ValueConvInv => '$val * 1e5',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    0x13 => {
        Name => 'FNumber',
        Writable => 'int16u',
        ValueConv => '$val / 10',
        ValueConvInv => '$val * 10',
    },
    0x14 => {
        Name => 'ISO',
        Writable => 'int16u',
        ValueConv => 'exp($val/3*log(2))*25',
        ValueConvInv => '3*log($val/25)/log(2)',
        PrintConv => 'int($val + 0.5)',
        PrintConvInv => '$val',
    },
    # 0x15 int16u - 18-25 (SceneMode? 21=auto, 24=Aperture Priority, 19=high speed)
    # 0x16 int16u - 50
    # 0x17 int16u - 0, 65535 (MeteringMode? 0=Multi, 65535=Partial)
    # 0x19 int16u - 0, 4 (WhiteBalance? 0=Auto, 4=Manual)
    # 0x1a int16u - 0, 65535
    # 0x1b int16u - 416-696
    # 0x1c int16u - 251-439 (low when 0x1b is high)
    0x1d => {
        Name => 'FocalLength',
        Writable => 'int32u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
        PrintConv => '"$val mm"',
        PrintConvInv => '$val=~s/\s*mm//;$val',
    },
    # 0x1e int16u - 100
    # 0x1f int16u - 0, 1
    # 0x20,0x21 int16u - 1
    # 0x27 undef[4] - fe ff ff ff
    # 0x32 undef[4] - 00 00 00 00
    # 0x61 int32u[2] - '0 0' or '34050 0'
    # 0x62 int8u - 0, 1
    # 0x63 int8u - 1
    # 0x64,0x65 int8u - 0, 1, 2
    # 0x66 int32u - 0
    # 0x67 int32u - 3
    # 0x68 int32u - 0
    # 0x3fe undef[2540]
);

# Kodak PixPro S-1 maker notes (ref PH)
# (similar to Ricoh::Type2 and GE::Main)
%Image::ExifTool::Kodak::Type11 = (
    # (can't currently write these)
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    NOTES =>q{
        These tags are found in models such as the PixPro S-1.  They are not
        writable because the inconsistency of Kodak maker notes is beginning to get
        on my nerves.
    },
    0x0203 => {
        Name => 'PictureEffect',
        PrintConv => {
            0 => 'None',
            3 => 'Monochrome',
            9 => 'Kodachrome',
        },
    },
    # 0x0204 - ExposureComp or FlashExposureComp maybe?
    0x0207 => 'KodakModel',
    0x0300 => 'KodakMake',
    0x0308 => 'LensSerialNumber',
    0x0309 => 'LensModel',
);

# Kodak SubIFD0 tags (ref PH)
%Image::ExifTool::Kodak::SubIFD0 = (
    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    NOTES => 'SubIFD0 through SubIFD5 tags are written a number of newer Kodak models.',
    0xfa02 => {
        Name => 'SceneMode',
        Writable => 'int16u',
        Notes => 'may not be valid for some models', # eg. M580?
        PrintConvColumns => 2,
        PrintConv => {
            1 => 'Sport',
            3 => 'Portrait',
            4 => 'Landscape',
            6 => 'Beach',
            7 => 'Night Portrait',
            8 => 'Night Landscape',
            9 => 'Snow',
            10 => 'Text',
            11 => 'Fireworks',
            12 => 'Macro',
            13 => 'Museum',
            16 => 'Children',
            17 => 'Program',
            18 => 'Aperture Priority',
            19 => 'Shutter Priority',
            20 => 'Manual',
            25 => 'Back Light',
            28 => 'Candlelight',
            29 => 'Sunset',
            31 => 'Panorama Left-right',
            32 => 'Panorama Right-left',
            33 => 'Smart Scene',
            34 => 'High ISO',
        },
    },
    # 0xfa04 - values: 0 (normally), 2 (panorama shots)
    # 0xfa0f - values: 0 (normally), 1 (macro?)
    # 0xfa11 - some sort of FNumber (x 100)
    0xfa19 => {
        Name => 'SerialNumber', # (verified with Z712 - PH)
        Writable => 'string',
    },
    0xfa1d => {
        Name => 'KodakImageWidth',
        Writable => 'int16u',
    },
    0xfa1e => {
        Name => 'KodakImageHeight',
        Writable => 'int16u',
    },
    0xfa20 => {
        Name => 'SensorWidth',
        Writable => 'int16u',
    },
    0xfa21 => {
        Name => 'SensorHeight',
        Writable => 'int16u',
    },
    0xfa23 => {
        Name => 'FNumber',
        Writable => 'int16u',
        Priority => 0,
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0xfa24 => {
        Name => 'ExposureTime',
        Writable => 'int32u',
        Priority => 0,
        ValueConv => '$val / 1e5',
        ValueConvInv => '$val * 1e5',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    0xfa2e => {
        Name => 'ISO',
        Writable => 'int16u',
        Priority => 0,
    },
    0xfa3d => {
        Name => 'OpticalZoom',
        Writable => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
        PrintConv => 'sprintf("%.2f",$val)',
        PrintConvInv => '$val=~s/ ?x//; $val',
    },
    0xfa46 => {
        Name => 'ISO',
        Writable => 'int16u',
        Priority => 0,
    },
    # 0xfa4c - related to focal length (1=wide, 32=full zoom)
    0xfa51 => {
        Name => 'KodakImageWidth',
        Writable => 'int16u',
    },
    0xfa52 => {
        Name => 'KodakImageHeight',
        Writable => 'int16u',
    },
    0xfa54 => {
        Name => 'ThumbnailWidth',
        Writable => 'int16u',
    },
    0xfa55 => {
        Name => 'ThumbnailHeight',
        Writable => 'int16u',
    },
    0xfa57 => {
        Name => 'PreviewImageWidth',
        Writable => 'int16u',
    },
    0xfa58 => {
        Name => 'PreviewImageHeight',
        Writable => 'int16u',
    },
);

# Kodak SubIFD1 tags (ref PH)
%Image::ExifTool::Kodak::SubIFD1 = (
    PROCESS_PROC => \&ProcessKodakIFD,
    WRITE_PROC => \&WriteKodakIFD,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    0x0027 => {
        Name => 'ISO',
        Writable => 'int16u',
        Priority => 0,
    },
    0x0028 => {
        Name => 'ISO',
        Writable => 'int16u',
        Priority => 0,
    },
);

my %sceneModeUsed = (
    0 => 'Program',
    2 => 'Aperture Priority',
    3 => 'Shutter Priority',
    4 => 'Manual',
    5 => 'Portrait',
    6 => 'Sport',
    7 => 'Children',
    8 => 'Museum',
    10 => 'High ISO',
    11 => 'Text',
    12 => 'Macro',
    13 => 'Back Light',
    16 => 'Landscape',
    17 => 'Night Landscape',
    18 => 'Night Portrait',
    19 => 'Snow',
    20 => 'Beach',
    21 => 'Fireworks',
    22 => 'Sunset',
    23 => 'Candlelight',
    28 => 'Panorama',
);

# Kodak SubIFD2 tags (ref PH)
%Image::ExifTool::Kodak::SubIFD2 = (
    PROCESS_PROC => \&ProcessKodakIFD,
    WRITE_PROC => \&WriteKodakIFD,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    0x6002 => {
        Name => 'SceneModeUsed',
        Writable => 'int32u',
        PrintConvColumns => 2,
        PrintConv => \%sceneModeUsed,
    },
    0x6006 => {
        Name => 'OpticalZoom',
        Writable => 'int32u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
        PrintConv => 'sprintf("%.2f",$val)',
        PrintConvInv => '$val=~s/ ?x//; $val',
    },
    # 0x6009 - some sort of FNumber (x 100)
    0x6103 => {
        Name => 'MaxAperture',
        Writable => 'int32u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0xf002 => {
        Name => 'SceneModeUsed',
        Writable => 'int32u',
        PrintConvColumns => 2,
        PrintConv => \%sceneModeUsed,
    },
    0xf006 => {
        Name => 'OpticalZoom',
        Writable => 'int32u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
        PrintConv => 'sprintf("%.2f",$val)',
        PrintConvInv => '$val=~s/ ?x//; $val',
    },
    # 0xf009 - some sort of FNumber (x 100)
    0xf103 => {
        Name => 'FNumber',
        Writable => 'int32u',
        Priority => 0,
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0xf104 => {
        Name => 'ExposureTime',
        Writable => 'int32u',
        Priority => 0,
        ValueConv => '$val / 1e6',
        ValueConvInv => '$val * 1e6',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    0xf105 => {
        Name => 'ISO',
        Writable => 'int32u',
        Priority => 0,
        ValueConv => '$val / 10',
        ValueConvInv => '$val * 10',
    },
);

# Kodak SubIFD3 tags (ref PH)
%Image::ExifTool::Kodak::SubIFD3 = (
    PROCESS_PROC => \&ProcessKodakIFD,
    WRITE_PROC => \&WriteKodakIFD,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    0x1000 => {
        Name => 'OpticalZoom',
        Writable => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
        PrintConv => 'sprintf("%.2f",$val)',
        PrintConvInv => '$val=~s/ ?x//; $val',
    },
    # 0x1002 - related to focal length (1=wide, 32=full zoom)
    # 0x1006 - pictures remaining? (gradually decreases as pictures are taken)
#
# the following unknown Kodak tags in subIFD3 may store an IFD count of 0 or 1 instead
# of the correct value (which changes from model to model).  This bad count is fixed
# with the "FixCount" patch.  Models known to have this problem include:
# M380, M1033, M1093IS, V1073, V1233, V1253, V1273, V1275, V1285, Z612, Z712,
# Z812, Z885, Z915, Z950, Z1012IS, Z1085IS, ZD710
#
    0x2007 => {
        Name => 'Kodak_SubIFD3_0x2007',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x2008 => {
        Name => 'Kodak_SubIFD3_0x2008',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x2009 => {
        Name => 'Kodak_SubIFD3_0x2009',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x200a => {
        Name => 'Kodak_SubIFD3_0x200a',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x200b => {
        Name => 'Kodak_SubIFD3_0x200b',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x3020 => {
        Name => 'Kodak_SubIFD3_0x3020',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x3030 => {
        Name => 'Kodak_SubIFD3_0x3030',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x3040 => {
        Name => 'Kodak_SubIFD3_0x3040',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x3050 => {
        Name => 'Kodak_SubIFD3_0x3050',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x3060 => {
        Name => 'Kodak_SubIFD3_0x3060',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x8001 => {
        Name => 'Kodak_SubIFD3_0x8001',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x8002 => {
        Name => 'Kodak_SubIFD3_0x8002',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x8003 => {
        Name => 'Kodak_SubIFD3_0x8003',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x8004 => {
        Name => 'Kodak_SubIFD3_0x8004',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x8005 => {
        Name => 'Kodak_SubIFD3_0x8005',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x8006 => {
        Name => 'Kodak_SubIFD3_0x8006',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x8007 => {
        Name => 'Kodak_SubIFD3_0x8007',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x8008 => {
        Name => 'Kodak_SubIFD3_0x8008',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x8009 => {
        Name => 'Kodak_SubIFD3_0x8009',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x800a => {
        Name => 'Kodak_SubIFD3_0x800a',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x800b => {
        Name => 'Kodak_SubIFD3_0x800b',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
    0x800c => {
        Name => 'Kodak_SubIFD3_0x800c',
        Flags => [ 'FixCount', 'Unknown', 'Hidden' ],
    },
);

# Kodak SubIFD4 tags (ref PH)
%Image::ExifTool::Kodak::SubIFD4 = (
    PROCESS_PROC => \&ProcessKodakIFD,
    WRITE_PROC => \&WriteKodakIFD,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
);

# Kodak SubIFD5 tags (ref PH)
%Image::ExifTool::Kodak::SubIFD5 = (
    PROCESS_PROC => \&ProcessKodakIFD,
    WRITE_PROC => \&WriteKodakIFD,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    0x000f => {
        Name => 'OpticalZoom',
        Writable => 'int16u',
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
        PrintConv => 'sprintf("%.2f",$val)',
        PrintConvInv => '$val=~s/ ?x//; $val',
    },
);

# Kodak SubIFD6 tags (ref PH)
%Image::ExifTool::Kodak::SubIFD6 = (
    PROCESS_PROC => \&ProcessKodakIFD,
    WRITE_PROC => \&WriteKodakIFD,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    NOTES => 'SubIFD6 is written by the M580.',
);

# Decoded from P712, P850 and P880 samples (ref PH)
%Image::ExifTool::Kodak::CameraInfo = (
    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    NOTES => 'These tags are used by the  P712, P850 and P880.',
    0xf900 => {
        Name => 'SensorWidth',
        Writable => 'int16u',
        Notes => 'effective sensor size',
    },
    0xf901 => {
        Name => 'SensorHeight',
        Writable => 'int16u',
    },
    0xf902 => {
        Name => 'BayerPattern',
        Writable => 'string',
    },
    0xf903 => {
        Name => 'SensorFullWidth',
        Writable => 'int16u',
        Notes => 'includes black border?',
    },
    0xf904 => {
        Name => 'SensorFullHeight',
        Writable => 'int16u',
    },
    0xf907 => {
        Name => 'KodakImageWidth',
        Writable => 'int16u',
    },
    0xf908 => {
        Name => 'KodakImageHeight',
        Writable => 'int16u',
    },
    0xfa00 => {
        Name => 'KodakInfoType',
        Writable => 'string',
    },
    0xfa04 => {
        Name => 'SerialNumber', # (unverified)
        Writable => 'string',
    },
    0xfd04 => {
        Name => 'FNumber',
        Writable => 'int16u',
        Priority => 0,
        ValueConv => '$val / 100',
        ValueConvInv => '$val * 100',
    },
    0xfd05 => {
        Name => 'ExposureTime',
        Writable => 'int32u',
        Priority => 0,
        ValueConv => '$val / 1e6',
        ValueConvInv => '$val * 1e6',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
        PrintConvInv => 'Image::ExifTool::Exif::ConvertFraction($val)',
    },
    0xfd06 => {
        Name => 'ISO',
        Writable => 'int16u',
        Priority => 0,
    },
);

# treat unknown maker notes as binary data (allows viewing with -U)
%Image::ExifTool::Kodak::Unknown = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    FIRST_ENTRY => 0,
);

# tags found in the KodakIFD (in IFD0 of KDC, DCR, TIFF and JPEG images) (ref PH)
%Image::ExifTool::Kodak::IFD = (
    GROUPS => { 0 => 'MakerNotes', 1 => 'KodakIFD', 2 => 'Image'},
    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    WRITE_GROUP => 'KodakIFD',
    SET_GROUP1 => 1,
    NOTES => q{
        These tags are found in a separate IFD of JPEG, TIFF, DCR and KDC images
        from some older Kodak models such as the DC50, DC120, DCS760C, DCS Pro 14N,
        14nx, SLR/n, Pro Back and Canon EOS D2000.
    },
    # 0x0000: int8u[4]    - values: "1 0 0 0" (DC50), "1 1 0 0" (DC120)
    0x0001 => {
        # (related to EV but exact meaning unknown)
        Name => 'UnknownEV',
        Writable => 'rational64u',
        Unknown => 1,
    },
    # 0x0002: int8u       - values: 0
    0x0003 => {
        Name => 'ExposureValue',
        Writable => 'rational64u',
    },
    # 0x0004: rational64u - values: 2.875,3.375,3.625,4,4.125,7.25
    # 0x0005: int8u       - values: 0
    # 0x0006: int32u[12]  - ?
    # 0x0007: int32u[3]   - values: "65536 67932 69256"
    0x03e9 => { Name => 'OriginalFileName', Writable => 'string' },
    0x03eb => 'SensorLeftBorder',
    0x03ec => 'SensorTopBorder',
    0x03ed => 'SensorImageWidth',
    0x03ee => 'SensorImageHeight',
    0x03f1 => {
        Name => 'TextualInfo',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::TextualInfo',
        },
    },
    # 0x03f2 - FlashMode (ref 4)
    # 0x03f3 - FlashCompensation (ref 4)
    # 0x03f8 - MinAperture (ref 4)
    # 0x03f9 - MaxAperture (ref 4)
    0x03fc => { #3
        Name => 'WhiteBalance',
        Writable => 'int16u',
        Priority => 0,
        PrintConv => { },   # no values yet known
    },
    0x03fd => { #3
        Name => 'Processing',
        Condition => '$count == 72',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::Processing',
        },
    },
    0x0401 => {
        Name => 'Time',
        Groups => { 2 => 'Time' },
        Writable => 'string',
    },
    0x0406 => { #4
        Name => 'CameraTemperature',
        # (when count is 2, values seem related to temperature, but are not Celius)
        Condition => '$count == 1',
        Groups => { 2 => 'Camera' },
        Writable => 'rational64s',
        PrintConv => '"$val C"',
        PrintConvInv => '$val=~s/ ?C//; $val',
    },
    0x0407 => { #4
        Name => 'AdapterVoltage',
        Groups => { 2 => 'Camera' },
        Writable => 'rational64u',
    },
    0x0408 => { #4
        Name => 'BatteryVoltage',
        Groups => { 2 => 'Camera' },
        Writable => 'rational64u',
    },
    0x0414 => { Name => 'NCDFileInfo',      Writable => 'string' },
    0x0846 => { #3
        Name => 'ColorTemperature',
        Writable => 'int16u',
    },
    0x0848 => 'WB_RGBLevelsDaylight', #4
    0x0849 => 'WB_RGBLevelsTungsten', #4
    0x084a => 'WB_RGBLevelsFluorescent', #4
    0x084b => 'WB_RGBLevelsFlash', #4
    0x084c => 'WB_RGBLevelsCustom', #4
    0x084d => 'WB_RGBLevelsAuto', #4
    0x0852 => 'WB_RGBMul0', #3
    0x0853 => 'WB_RGBMul1', #3
    0x0854 => 'WB_RGBMul2', #3
    0x0855 => 'WB_RGBMul3', #3
    0x085c => { Name => 'WB_RGBCoeffs0', Binary => 1 }, #3
    0x085d => { Name => 'WB_RGBCoeffs1', Binary => 1 }, #3
    0x085e => { Name => 'WB_RGBCoeffs2', Binary => 1 }, #3
    0x085f => { Name => 'WB_RGBCoeffs3', Binary => 1 }, #3
    # 0x089d => true analogue ISO values possible (ref 4)
    # 0x089e => true analogue ISO used at capture (ref 4)
    # 0x089f => ISO calibration gain (ref 4)
    # 0x08a0 => ISO calibration gain table (ref 4)
    # 0x08a1 => exposure headroom coefficient (ref 4)
    0x0903 => { Name => 'BaseISO', Writable => 'rational64u' }, #4 (ISO before digital gain)
    # 0x090d: linear table (ref 3)
    0x09ce => { Name => 'SensorSerialNumber', Writable => 'string', Groups => { 2 => 'Camera' } }, #4
    # 0x0c81: some sort of date (manufacture date?) - PH
    0x0ce5 => { Name => 'FirmwareVersion',  Writable => 'string', Groups => { 2 => 'Camera' } },
    0x0e4c => { #4
        Name => 'KodakLook',
        Format => 'undef',
        Writable => 'string',
        ValueConv => '$val=~tr/\0/\n/; $val',
        ValueConvInv => '$val=~tr/\n/\0/; $val',
    },
    0x1389 => { Name => 'InputProfile',     Writable => 'undef', Binary => 1 }, #4
    0x138a => { Name => 'KodakLookProfile', Writable => 'undef', Binary => 1 }, #4
    0x138b => { Name => 'OutputProfile',    Writable => 'undef', Binary => 1 }, #4
    # 0x1390: value: "DCSProSLRn" (tone curve name?) - PH
    0x1391 => { Name => 'ToneCurveFileName',Writable => 'string' },
    0x1784 => { Name => 'ISO',              Writable => 'int32u' }, #3
);

# contains WB adjust set in software (ref 3)
%Image::ExifTool::Kodak::Processing = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    WRITE_PROC => \&Image::ExifTool::WriteBinaryData,
    CHECK_PROC => \&Image::ExifTool::CheckBinaryData,
    FORMAT => 'int16u',
    FIRST_ENTRY => 0,
    20 => {
        Name => 'WB_RGBLevels',
        Format => 'int16u[3]',
        ValueConv => q{
            my @a = split ' ',$val;
            foreach (@a) {
                $_ = 2048 / $_ if $_;
            }
            return join ' ', @a;
        }
    },
);

# tags found in the Kodak KDC_IFD (in IFD0 of KDC images) (ref 3)
%Image::ExifTool::Kodak::KDC_IFD = (
    GROUPS => { 0 => 'MakerNotes', 1 => 'KDC_IFD', 2 => 'Image'},
    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    WRITE_GROUP => 'KDC_IFD',
    SET_GROUP1 => 1,
    NOTES => q{
        These tags are found in a separate IFD of KDC images from some newer Kodak
        models such as the P880 and Z1015IS.
    },
    0xfa00 => {
        Name => 'SerialNumber', #PH (unverified)
        Writable => 'string',
    },
    0xfa0d => {
        Name => 'WhiteBalance',
        Writable => 'int8u',
        PrintConv => { #PH
            0 => 'Auto',
            1 => 'Fluorescent', # (NC)
            2 => 'Tungsten', # (NC)
            3 => 'Daylight', # (NC)
            6 => 'Shade', # (NC, called "Open Shade" by Kodak)
        },
    },
    # the following tags are numbered for use in the Composite tag lookup
    0xfa25 => 'WB_RGBLevelsAuto',
    0xfa27 => 'WB_RGBLevelsTungsten', # (NC)
    0xfa28 => 'WB_RGBLevelsFluorescent', # (NC)
    0xfa29 => 'WB_RGBLevelsDaylight', # (NC)
    0xfa2a => 'WB_RGBLevelsShade', # (NC)
);

# textual-based Kodak TextualInfo tags (not found in KDC images) (ref PH)
%Image::ExifTool::Kodak::TextualInfo = (
    GROUPS => { 0 => 'MakerNotes', 1 => 'Kodak', 2 => 'Image'},
    PROCESS_PROC => \&ProcessKodakText,
    NOTES => q{
        Below is a list of tags which have been observed in the Kodak TextualInfo
        data, however ExifTool will extract information from any tags found here.
    },
    'Actual Compensation' => 'ActualCompensation',
    'AF Function'   => 'AFMode', # values: "S" (=Single?, then maybe C for Continuous, M for Manual?) - PH
    'Aperture'      => {
        Name => 'Aperture',
        ValueConv => '$val=~s/^f//i; $val',
    },
    'Auto Bracket'  => 'AutoBracket',
    'Brightness Value' => 'BrightnessValue',
    'Camera'        => 'CameraModel',
    'Camera body'   => 'CameraBody',
    'Compensation'  => 'ExposureCompensation',
    'Date'          => {
        Name => 'Date',
        Groups => { 2 => 'Time' },
    },
    'Exposure Bias' => 'ExposureBias',
    'Exposure Mode' => {
        Name => 'ExposureMode',
        PrintConv => {
            'M' => 'Manual',
            'A' => 'Aperture Priority', #(NC -- I suppose this could be "Auto" too)
            'S' => 'Shutter Priority', #(NC)
            'P' => 'Program', #(NC)
            'B' => 'Bulb', #(NC)
            # have seen "Manual (M)" written by DCS760C - PH
        },
    },
    'Firmware Version' => 'FirmwareVersion',
    'Flash Compensation' => 'FlashExposureComp',
    'Flash Fired'   => 'FlashFired',
    'Flash Sync Mode' => 'FlashSyncMode',
    'Focal Length'  => {
        Name => 'FocalLength',
        PrintConv => '"$val mm"',
    },
    'Height'        => 'KodakImageHeight',
    'Image Number'  => 'ImageNumber',
    'ISO'           => 'ISO',
    'ISO Speed'     => 'ISO',
    'Max Aperture'  => {
        Name => 'MaxAperture',
        ValueConv => '$val=~s/^f//i; $val',
    },
    'Meter Mode'    => 'MeterMode',
    'Min Aperture'  => {
        Name => 'MinAperture',
        ValueConv => '$val=~s/^f//i; $val',
    },
    'Popup Flash'   => 'PopupFlash',
    'Serial Number' => 'SerialNumber',
    'Shooting Mode' => 'ShootingMode',
    'Shutter'       => 'ShutterSpeed',
    'Temperature'   => 'Temperature', # with a value of 15653, what could this be? - PH
    'Time'          => {
        Name => 'Time',
        Groups => { 2 => 'Time' },
    },
    'White balance' => 'WhiteBalance',
    'Width'         => 'KodakImageWidth',
    '_other_info'   => {
        Name => 'OtherInfo',
        Notes => 'any other information without a tag name',
    },
);

# Kodak APP3 "Meta" tags (ref 2)
%Image::ExifTool::Kodak::Meta = (
    GROUPS => { 0 => 'Meta', 1 => 'MetaIFD', 2 => 'Image'},
    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
    CHECK_PROC => \&Image::ExifTool::Exif::CheckExif,
    WRITE_GROUP => 'MetaIFD',   # default write group
    NOTES => q{
        These tags are found in the APP3 "Meta" segment of JPEG images from Kodak
        cameras such as the DC280, DC3400, DC5000, MC3, M580, Z950 and Z981.  The
        structure of this segment is similar to the APP1 "Exif" segment, but a
        different set of tags is used.
    },
    0xc350 => 'FilmProductCode',
    0xc351 => 'ImageSourceEK',
    0xc352 => 'CaptureConditionsPAR',
    0xc353 => {
        Name => 'CameraOwner',
        Writable => 'undef',
        RawConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val,$tag)',
        RawConvInv => 'Image::ExifTool::Exif::EncodeExifText($self,$val)',
    },
    0xc354 => {
        Name => 'SerialNumber',
        Writable => 'undef',
        Groups => { 2 => 'Camera' },
        RawConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val,$tag)', #PH
        RawConvInv => 'Image::ExifTool::Exif::EncodeExifText($self,$val)',
    },
    0xc355 => 'UserSelectGroupTitle',
    0xc356 => 'DealerIDNumber',
    0xc357 => 'CaptureDeviceFID',
    0xc358 => 'EnvelopeNumber',
    0xc359 => 'FrameNumber',
    0xc35a => 'FilmCategory',
    0xc35b => 'FilmGencode',
    0xc35c => 'ModelAndVersion',
    0xc35d => 'FilmSize',
    0xc35e => 'SBA_RGBShifts',
    0xc35f => 'SBAInputImageColorspace',
    0xc360 => 'SBAInputImageBitDepth',
    0xc361 => {
        Name => 'SBAExposureRecord',
        Binary => 1,
    },
    0xc362 => {
        Name => 'UserAdjSBA_RGBShifts',
        Binary => 1,
    },
    0xc363 => 'ImageRotationStatus',
    0xc364 => 'RollGuidElements',
    0xc365 => 'MetadataNumber',
    0xc366 => 'EditTagArray',
    0xc367 => 'Magnification',
    # 0xc36b - string[8]: "1.0"
    0xc36c => 'NativeXResolution',
    0xc36d => 'NativeYResolution',
    0xc36e => {
        Name => 'KodakEffectsIFD',
        Flags => 'SubIFD',
        Groups => { 1 => 'KodakEffectsIFD' },
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::SpecialEffects',
            Start => '$val',
        },
    },
    0xc36f => {
        Name => 'KodakBordersIFD',
        Flags => 'SubIFD',
        Groups => { 1 => 'KodakBordersIFD' },
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::Borders',
            Start => '$val',
        },
    },
    0xc37a => 'NativeResolutionUnit',
    0xc418 => 'SourceImageDirectory',
    0xc419 => 'SourceImageFileName',
    0xc41a => 'SourceImageVolumeName',
    0xc46c => 'PrintQuality',
    0xc46e => 'ImagePrintStatus',
    # 0cx46f - int16u: 1
);

# Kodak APP3 "Meta" Special Effects sub-IFD (ref 2)
%Image::ExifTool::Kodak::SpecialEffects = (
    GROUPS => { 0 => 'Meta', 1 => 'KodakEffectsIFD', 2 => 'Image'},
    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
    0 => 'DigitalEffectsVersion',
    1 => {
        Name => 'DigitalEffectsName',
        PrintConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val,"DigitalEffectsName")',
    },
    2 => 'DigitalEffectsType',
);

# Kodak APP3 "Meta" Borders sub-IFD (ref 2)
%Image::ExifTool::Kodak::Borders = (
    GROUPS => { 0 => 'Meta', 1 => 'KodakBordersIFD', 2 => 'Image'},
    WRITE_PROC => \&Image::ExifTool::Exif::WriteExif,
    0 => 'BordersVersion',
    1 => {
        Name => 'BorderName',
        PrintConv => 'Image::ExifTool::Exif::ConvertExifText($self,$val,"BorderName")',
    },
    2 => 'BorderID',
    3 => 'BorderLocation',
    4 => 'BorderType',
    8 => 'WatermarkType',
);

# tags in Kodak MOV videos (ref PH)
# (similar information in Kodak,Minolta,Nikon,Olympus,Pentax and Sanyo videos)
%Image::ExifTool::Kodak::MOV = (
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    GROUPS => { 0 => 'MakerNotes', 2 => 'Camera' },
    FIRST_ENTRY => 0,
    NOTES => q{
        This information is found in the TAGS atom of MOV videos from Kodak models
        such as the P880.
    },
    0 => {
        Name => 'Make',
        Format => 'string[21]',
    },
    0x16 => {
        Name => 'Model',
        Format => 'string[42]',
    },
    0x40 => {
        Name => 'ModelType',
        Format => 'string[8]',
    },
    # (01 00 at offset 0x48)
    0x4e => {
        Name => 'ExposureTime',
        Format => 'int32u',
        ValueConv => '$val ? 10 / $val : 0',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
    },
    0x52 => {
        Name => 'FNumber',
        Format => 'rational64u',
        PrintConv => 'sprintf("%.1f",$val)',
    },
    0x5a => {
        Name => 'ExposureCompensation',
        Format => 'rational64s',
        PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)',
    },
    # 0x6c => 'WhiteBalance', ?
    0x70 => {
        Name => 'FocalLength',
        Format => 'rational64u',
        PrintConv => 'sprintf("%.1f mm",$val)',
    },
);

# Kodak DcMD atoms (ref PH)
%Image::ExifTool::Kodak::DcMD = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Video' },
    NOTES => 'Metadata directory found in MOV and MP4 videos from some Kodak cameras.',
    Cmbo => {
        Name => 'CameraByteOrder',
        PrintConv => {
            II => 'Little-endian (Intel, II)',
            MM => 'Big-endian (Motorola, MM)',
        },
    },
    CMbo => { # (as written by Kodak Playsport video camera)
        Name => 'CameraByteOrder',
        PrintConv => {
            II => 'Little-endian (Intel, II)',
            MM => 'Big-endian (Motorola, MM)',
        },
    },
    DcME => {
        Name => 'DcME',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::DcME',
        },
    },
    DcEM => {
        Name => 'DcEM',
        SubDirectory => {
            TagTable => 'Image::ExifTool::Kodak::DcEM',
        },
    },
);

# Kodak DcME atoms (ref PH)
%Image::ExifTool::Kodak::DcME = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Video' },
    # Mtmd - 24 bytes: ("00 00 00 00 00 00 00 01" x 3)
    # Keyw - keywords? (six bytes all zero)
    # Rate -  2 bytes: 00 00
);

# Kodak DcEM atoms (ref PH)
%Image::ExifTool::Kodak::DcEM = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Video' },
    # Mtmd - 24 bytes: ("00 00 00 00 00 00 00 01" x 3)
    # Csat - 16 bytes: 00 06 00 00 62 00 61 00 73 00 69 00 63 00 00 00 [....b.a.s.i.c...]
    # Ksre -  8 bytes: 00 01 00 00 00 00
);

# tags in "free" atom of Kodak M5370 MP4 videos (ref PH)
%Image::ExifTool::Kodak::Free = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Video' },
    NOTES => q{
        Information stored in the "free" atom of Kodak MP4 videos. (VERY bad form
        for Kodak to store useful information in an atom intended for unused space!)
    },
    # (2012/01/19: Kodak files for bankruptcy -- this is poetic metadata justice)
    Seri => {
        Name => 'SerialNumber',
        # byte 0 is string length;  byte 1 is zero;  string starts at byte 2
        ValueConv => 'substr($val, 2, unpack("C",$val))',
    },
    SVer => {
        Name => 'FirmwareVersion',
        ValueConv => 'substr($val, 2, unpack("C",$val))',
    },
    # Clor - 2 bytes: 0 1  (?)
    # CapM - 2 bytes: 0 1  (capture mode? = exposure mode?)
    # WBMD - 2 bytes: 0 0  (white balance?)
    Expc => { # (NC)
        Name => 'ExposureCompensation',
        Format => 'int16s',
        ValueConv => '$val / 3', # (guess)
        PrintConv => 'Image::ExifTool::Exif::PrintFraction($val)',
    },
    # Zone - 2 bytes: 0 2  (time zone? focus zone?)
    # FoMD - 2 bytes: 0 0  (focus mode?)
    # Shap - 2 bytes: 0 2  (sharpness?)
    Expo => {
        Name => 'ExposureTime',
        Format => 'rational32u',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
    },
    FNum => {
        Name => 'FNumber',
        Format => 'int16u',
        ValueConv => '$val / 100',
        PrintConv => 'Image::ExifTool::Exif::PrintFNumber($val)',
    },
    ISOS => { Name => 'ISO', Format => 'int16u' },
    StSV => {
        Name => 'ShutterSpeedValue',
        Format => 'int16s',
        ValueConv => 'abs($val)<100 ? 2**(-$val/3) : 0',
        PrintConv => 'Image::ExifTool::Exif::PrintExposureTime($val)',
    },
    AprV => {
        Name => 'ApertureValue',
        Format => 'int16s',
        ValueConv => '2 ** ($val / 2000)',
        PrintConv => 'sprintf("%.1f",$val)',
    },
    BrtV => { # (NC)
        Name => 'BrightnessValue',
        Format => 'int32s',
        ValueConv => '$val / 1000', # (guess)
    },
    FoLn => {
        Name => 'FocalLength',
        Groups => { 2 => 'Camera' },
        Format => 'int16u',
        PrintConv => 'sprintf("%.1f mm",$val)',
    },
    FL35 => {
        Name => 'FocalLengthIn35mmFormat',
        Groups => { 2 => 'Camera' },
        Format => 'int16u',
        PrintConv => '"$val mm"',
    },
    Scrn => {
        Name => 'PreviewInfo',
        SubDirectory => { TagTable => 'Image::ExifTool::Kodak::Scrn' },
    },
);

# tags in "frea" atom of Kodak PixPro SP360 MP4 videos (ref PH)
%Image::ExifTool::Kodak::frea = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
    NOTES => 'Information stored in the "frea" atom of Kodak PixPro SP360 MP4 videos.',
    # tima - 4 bytes: 0 0 0 0x20
    thma => { Name => 'ThumbnailImage', Groups => { 2 => 'Preview' }, Binary => 1 },
    scra => { Name => 'PreviewImage',   Groups => { 2 => 'Preview' }, Binary => 1 },
);

# preview information in free/Scrn atom of MP4 videos (ref PH)
%Image::ExifTool::Kodak::Scrn = (
    GROUPS => { 0 => 'MakerNotes', 2 => 'Image' },
    PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
    FORMAT => 'int16u',
    0 => 'PreviewImageWidth',
    1 => 'PreviewImageHeight',
    2 => { Name => 'PreviewImageLength', Format => 'int32u' },
    4 => {
        Name => 'PreviewImage',
        Groups => { 2 => 'Preview' },
        Format => 'undef[$val{2}]',
        RawConv => '$self->ValidateImage(\$val, $tag)',
    },
);

# Kodak composite tags
%Image::ExifTool::Kodak::Composite = (
    GROUPS => { 2 => 'Camera' },
    DateCreated => {
        Groups => { 2 => 'Time' },
        Require => {
            0 => 'Kodak:YearCreated',
            1 => 'Kodak:MonthDayCreated',
        },
        ValueConv => '"$val[0]:$val[1]"',
    },
    WB_RGBLevels => {
        Require => {
            0 => 'KDC_IFD:WhiteBalance',
        },
        # indices of the following entries are KDC_IFD:WhiteBalance + 1
        Desire => {
            1 => 'WB_RGBLevelsAuto',
            2 => 'WB_RGBLevelsFluorescent',
            3 => 'WB_RGBLevelsTungsten',
            4 => 'WB_RGBLevelsDaylight',
            5 => 'WB_RGBLevels4',
            6 => 'WB_RGBLevels5',
            7 => 'WB_RGBLevelsShade',
        },
        ValueConv => '$val[$val[0] + 1]',
    },
    WB_RGBLevels2 => {
        Name => 'WB_RGBLevels',
        Require => {
            0 => 'KodakIFD:WhiteBalance',
            1 => 'WB_RGBMul0',
            2 => 'WB_RGBMul1',
            3 => 'WB_RGBMul2',
            4 => 'WB_RGBMul3',
            5 => 'WB_RGBCoeffs0',
            6 => 'WB_RGBCoeffs1',
            7 => 'WB_RGBCoeffs2',
            8 => 'WB_RGBCoeffs3',
        },
        # indices of the following entries are KDC_IFD:WhiteBalance + 1
        Desire => {
            9 => 'KodakIFD:ColorTemperature',
            10 => 'Kodak:WB_RGBLevels',
        },
        ValueConv => 'Image::ExifTool::Kodak::CalculateRGBLevels(@val)',
    },
);

# add our composite tags
Image::ExifTool::AddCompositeTags('Image::ExifTool::Kodak');

#------------------------------------------------------------------------------
# Calculate RGB levels from associated tags (ref 3)
# Inputs: 0) KodakIFD:WhiteBalance, 1-4) WB_RGBMul0-3, 5-8) WB_RGBCoeffs0-3
#         9) (optional) KodakIFD:ColorTemperature, 10) (optional) Kodak:WB_RGBLevels
# Returns: WB_RGBLevels or undef
sub CalculateRGBLevels(@)
{
    return undef if $_[10]; # use existing software levels if they exist
    my $wbi = $_[0];
    return undef if $wbi < 0 or $wbi > 3;
    my @mul = split ' ', $_[$wbi + 1], 13; # (only use the first 12 coeffs)
    my @coefs = split ' ', ${$_[$wbi + 5]}; # (extra de-reference for Binary data)
    my $wbtemp100 = ($_[9] || 6500) / 100;
    return undef unless @mul >= 3 and @coefs >= 12;
    my ($i, $c, $n, $num, @cam_mul);
    for ($c=$n=0; $c<3; ++$c) {
        for ($num=$i=0; $i<4; ++$i) {
            $num += $coefs[$n++] * ($wbtemp100 ** $i);
        }
        $cam_mul[$c] = 2048 / ($num * $mul[$c]);
    }
    return join(' ', @cam_mul);
}

#------------------------------------------------------------------------------
# Process Kodak textual TextualInfo
# Inputs: 0) ExifTool object ref, 1) dirInfo hash ref, 2) tag table ref
# Returns: 1 on success
sub ProcessKodakText($$$)
{
    my ($et, $dirInfo, $tagTablePtr) = @_;
    my $dataPt = $$dirInfo{DataPt};
    my $dirStart = $$dirInfo{DirStart} || 0;
    my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart;
    my $data = substr($$dataPt, $dirStart, $dirLen);
    $data =~ s/\0.*//s;     # truncate at null if it exists
    my @lines = split /[\n\r]+/, $data;
    my ($line, $success, @other, $tagInfo);
    $et->VerboseDir('Kodak Text');
    foreach $line (@lines) {
        if ($line =~ /(.*?):\s*(.*)/) {
            my ($tag, $val) = ($1, $2);
            if ($$tagTablePtr{$tag}) {
                $tagInfo = $et->GetTagInfo($tagTablePtr, $tag);
            } else {
                my $tagName = $tag;
                $tagName =~ s/([A-Z])\s+([A-Za-z])/${1}_\U$2/g;
                $tagName =~ s/([a-z])\s+([A-Za-z0-9])/${1}\U$2/g;
                $tagName =~ s/\s+//g;
                $tagName =~ s/[^-\w]+//g;   # delete remaining invalid characters
                $tagName = 'NoName' unless $tagName;
                $tagInfo = { Name => $tagName };
                AddTagToTable($tagTablePtr, $tag, $tagInfo);
            }
            $et->HandleTag($tagTablePtr, $tag, $val, TagInfo => $tagInfo);
            $success = 1;
        } else {
            # strip off leading/trailing white space and ignore blank lines
            push @other, $1 if $line =~ /^\s*(\S.*?)\s*$/;
        }
    }
    if ($success) {
        if (@other) {
            $tagInfo = $et->GetTagInfo($tagTablePtr, '_other_info');
            $et->FoundTag($tagInfo, \@other);
        }
    } else {
        $et->Warn("Can't parse Kodak TextualInfo data", 1);
    }
    return $success;
}

#------------------------------------------------------------------------------
# Process Kodak IFD (with leading byte order mark)
# Inputs: 0) ExifTool object ref, 1) dirInfo hash ref, 2) tag table ref
# Returns: 1 on success, otherwise returns 0 and sets a Warning
sub ProcessKodakIFD($$$)
{
    my ($et, $dirInfo, $tagTablePtr) = @_;
    my $dirStart = $$dirInfo{DirStart} || 0;
    return 1 if $dirStart <= 0 or $dirStart + 2 > $$dirInfo{DataLen};
    my $byteOrder = substr(${$$dirInfo{DataPt}}, $dirStart, 2);
    unless (Image::ExifTool::SetByteOrder($byteOrder)) {
        $et->Warn("Invalid Kodak $$dirInfo{Name} directory");
        return 1;
    }
    $$dirInfo{DirStart} += 2;   # skip byte order mark
    $$dirInfo{DirLen} -= 2;
    if ($$et{HTML_DUMP}) {
        my $base = $$dirInfo{Base} + $$dirInfo{DataPos};
        $et->HDump($dirStart+$base, 2, "Byte Order Mark");
    }
    return Image::ExifTool::Exif::ProcessExif($et, $dirInfo, $tagTablePtr);
}

#------------------------------------------------------------------------------
# Write Kodak IFD (with leading byte order mark)
# Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref
# Returns: Exif data block (may be empty if no Exif data) or undef on error
sub WriteKodakIFD($$$)
{
    my ($et, $dirInfo, $tagTablePtr) = @_;
    my $dirStart = $$dirInfo{DirStart} || 0;
    return '' if $dirStart <= 0 or $dirStart + 2 > $$dirInfo{DataLen};
    my $byteOrder = substr(${$$dirInfo{DataPt}}, $dirStart, 2);
    return '' unless Image::ExifTool::SetByteOrder($byteOrder);
    $$dirInfo{DirStart} += 2;   # skip byte order mark
    $$dirInfo{DirLen} -= 2;
    my $buff = Image::ExifTool::Exif::WriteExif($et, $dirInfo, $tagTablePtr);
    return $buff unless defined $buff and length $buff;
    # apply one-time fixup for length of byte order mark
    if ($$dirInfo{Fixup}) {
        $dirInfo->{Fixup}->{Shift} += 2;
        $$dirInfo{Fixup}->ApplyFixup(\$buff);
        delete $$dirInfo{Fixup};
    }
    return Image::ExifTool::GetByteOrder() . $buff;
}

1;  # end

__END__

=head1 NAME

Image::ExifTool::Kodak - Kodak EXIF maker notes and APP3 "Meta" tags

=head1 SYNOPSIS

This module is loaded automatically by Image::ExifTool when required.

=head1 DESCRIPTION

This module contains definitions required by Image::ExifTool to
interpret Kodak maker notes EXIF meta information.

=head1 AUTHOR

Copyright 2003-2015, Phil Harvey (phil at owl.phy.queensu.ca)

This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=head1 REFERENCES

=over 4

=item L<Image::MetaData::JPEG|Image::MetaData::JPEG>

=item L<http://www.ozhiker.com/electronics/pjmt/jpeg_info/meta.html>

=item L<http://www.cybercom.net/~dcoffin/dcraw/>

=item (...plus lots of testing with my daughter's CX4200!)

=back

=head1 SEE ALSO

L<Image::ExifTool::TagNames/Kodak Tags>,
L<Image::ExifTool(3pm)|Image::ExifTool>

=cut