| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2 |  |  |  |  |  |  | # File:         Writer.pl | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # Description:  ExifTool write routines | 
| 5 |  |  |  |  |  |  | # | 
| 6 |  |  |  |  |  |  | # Notes:        Also contains some less used ExifTool functions | 
| 7 |  |  |  |  |  |  | # | 
| 8 |  |  |  |  |  |  | # URL:          https://exiftool.org/ | 
| 9 |  |  |  |  |  |  | # | 
| 10 |  |  |  |  |  |  | # Revisions:    12/16/2004 - P. Harvey Created | 
| 11 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | package Image::ExifTool; | 
| 14 |  |  |  |  |  |  |  | 
| 15 | 58 |  |  | 58 |  | 472 | use strict; | 
|  | 58 |  |  |  |  | 129 |  | 
|  | 58 |  |  |  |  | 2678 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 58 |  |  | 58 |  | 168468 | use Image::ExifTool::TagLookup qw(FindTagInfo TagExists); | 
|  | 58 |  |  |  |  | 9621 |  | 
|  | 58 |  |  |  |  | 20825 |  | 
| 18 | 58 |  |  | 58 |  | 41261 | use Image::ExifTool::Fixup; | 
|  | 58 |  |  |  |  | 182 |  | 
|  | 58 |  |  |  |  | 129955 |  | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | sub AssembleRational($$@); | 
| 21 |  |  |  |  |  |  | sub LastInList($); | 
| 22 |  |  |  |  |  |  | sub CreateDirectory($$); | 
| 23 |  |  |  |  |  |  | sub NextFreeTagKey($$); | 
| 24 |  |  |  |  |  |  | sub RemoveNewValueHash($$$); | 
| 25 |  |  |  |  |  |  | sub RemoveNewValuesForGroup($$); | 
| 26 |  |  |  |  |  |  | sub GetWriteGroup1($$); | 
| 27 |  |  |  |  |  |  | sub Sanitize($$); | 
| 28 |  |  |  |  |  |  | sub ConvInv($$$$$;$$); | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | my $loadedAllTables;    # flag indicating we loaded all tables | 
| 31 |  |  |  |  |  |  | my $advFmtSelf;         # ExifTool object during evaluation of advanced formatting expr | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | # the following is a road map of where we write each directory | 
| 34 |  |  |  |  |  |  | # in the different types of files. | 
| 35 |  |  |  |  |  |  | my %tiffMap = ( | 
| 36 |  |  |  |  |  |  | IFD0         => 'TIFF', | 
| 37 |  |  |  |  |  |  | IFD1         => 'IFD0', | 
| 38 |  |  |  |  |  |  | XMP          => 'IFD0', | 
| 39 |  |  |  |  |  |  | ICC_Profile  => 'IFD0', | 
| 40 |  |  |  |  |  |  | ExifIFD      => 'IFD0', | 
| 41 |  |  |  |  |  |  | GPS          => 'IFD0', | 
| 42 |  |  |  |  |  |  | SubIFD       => 'IFD0', | 
| 43 |  |  |  |  |  |  | GlobParamIFD => 'IFD0', | 
| 44 |  |  |  |  |  |  | PrintIM      => 'IFD0', | 
| 45 |  |  |  |  |  |  | IPTC         => 'IFD0', | 
| 46 |  |  |  |  |  |  | Photoshop    => 'IFD0', | 
| 47 |  |  |  |  |  |  | InteropIFD   => 'ExifIFD', | 
| 48 |  |  |  |  |  |  | MakerNotes   => 'ExifIFD', | 
| 49 |  |  |  |  |  |  | CanonVRD     => 'MakerNotes', # (so VRDOffset will get updated) | 
| 50 |  |  |  |  |  |  | NikonCapture => 'MakerNotes', # (to allow delete by group) | 
| 51 |  |  |  |  |  |  | PhaseOne     => 'MakerNotes', # (for editing PhaseOne SensorCalibration tags) | 
| 52 |  |  |  |  |  |  | ); | 
| 53 |  |  |  |  |  |  | my %exifMap = ( | 
| 54 |  |  |  |  |  |  | IFD1         => 'IFD0', | 
| 55 |  |  |  |  |  |  | EXIF         => 'IFD0', # to write EXIF as a block | 
| 56 |  |  |  |  |  |  | ExifIFD      => 'IFD0', | 
| 57 |  |  |  |  |  |  | GPS          => 'IFD0', | 
| 58 |  |  |  |  |  |  | SubIFD       => 'IFD0', | 
| 59 |  |  |  |  |  |  | GlobParamIFD => 'IFD0', | 
| 60 |  |  |  |  |  |  | PrintIM      => 'IFD0', | 
| 61 |  |  |  |  |  |  | InteropIFD   => 'ExifIFD', | 
| 62 |  |  |  |  |  |  | MakerNotes   => 'ExifIFD', | 
| 63 |  |  |  |  |  |  | NikonCapture => 'MakerNotes', # (to allow delete by group) | 
| 64 |  |  |  |  |  |  | # (no CanonVRD trailer allowed) | 
| 65 |  |  |  |  |  |  | ); | 
| 66 |  |  |  |  |  |  | my %jpegMap = ( | 
| 67 |  |  |  |  |  |  | %exifMap, # covers all JPEG EXIF mappings | 
| 68 |  |  |  |  |  |  | JFIF         => 'APP0', | 
| 69 |  |  |  |  |  |  | CIFF         => 'APP0', | 
| 70 |  |  |  |  |  |  | IFD0         => 'APP1', | 
| 71 |  |  |  |  |  |  | XMP          => 'APP1', | 
| 72 |  |  |  |  |  |  | ICC_Profile  => 'APP2', | 
| 73 |  |  |  |  |  |  | FlashPix     => 'APP2', | 
| 74 |  |  |  |  |  |  | MPF          => 'APP2', | 
| 75 |  |  |  |  |  |  | Meta         => 'APP3', | 
| 76 |  |  |  |  |  |  | MetaIFD      => 'Meta', | 
| 77 |  |  |  |  |  |  | RMETA        => 'APP5', | 
| 78 |  |  |  |  |  |  | Ducky        => 'APP12', | 
| 79 |  |  |  |  |  |  | Photoshop    => 'APP13', | 
| 80 |  |  |  |  |  |  | Adobe        => 'APP14', | 
| 81 |  |  |  |  |  |  | IPTC         => 'Photoshop', | 
| 82 |  |  |  |  |  |  | MakerNotes   => ['ExifIFD', 'CIFF'], # (first parent is the default) | 
| 83 |  |  |  |  |  |  | CanonVRD     => 'MakerNotes', # (so VRDOffset will get updated) | 
| 84 |  |  |  |  |  |  | NikonCapture => 'MakerNotes', # (to allow delete by group) | 
| 85 |  |  |  |  |  |  | Comment      => 'COM', | 
| 86 |  |  |  |  |  |  | ); | 
| 87 |  |  |  |  |  |  | my %dirMap = ( | 
| 88 |  |  |  |  |  |  | JPEG => \%jpegMap, | 
| 89 |  |  |  |  |  |  | EXV  => \%jpegMap, | 
| 90 |  |  |  |  |  |  | TIFF => \%tiffMap, | 
| 91 |  |  |  |  |  |  | ORF  => \%tiffMap, | 
| 92 |  |  |  |  |  |  | RAW  => \%tiffMap, | 
| 93 |  |  |  |  |  |  | EXIF => \%exifMap, | 
| 94 |  |  |  |  |  |  | ); | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | # module names and write functions for each writable file type | 
| 97 |  |  |  |  |  |  | # (defaults to "$type" and "Process$type" if not defined) | 
| 98 |  |  |  |  |  |  | # - types that are handled specially will not appear in this list | 
| 99 |  |  |  |  |  |  | my %writableType = ( | 
| 100 |  |  |  |  |  |  | CRW => [ 'CanonRaw',    'WriteCRW' ], | 
| 101 |  |  |  |  |  |  | DR4 =>   'CanonVRD', | 
| 102 |  |  |  |  |  |  | EPS => [ 'PostScript',  'WritePS'  ], | 
| 103 |  |  |  |  |  |  | FLIF=> [ undef,         'WriteFLIF'], | 
| 104 |  |  |  |  |  |  | GIF =>   undef, | 
| 105 |  |  |  |  |  |  | ICC => [ 'ICC_Profile', 'WriteICC' ], | 
| 106 |  |  |  |  |  |  | IND =>   'InDesign', | 
| 107 |  |  |  |  |  |  | JP2 =>   'Jpeg2000', | 
| 108 |  |  |  |  |  |  | JXL =>   'Jpeg2000', | 
| 109 |  |  |  |  |  |  | MIE =>   undef, | 
| 110 |  |  |  |  |  |  | MOV => [ 'QuickTime',   'WriteMOV' ], | 
| 111 |  |  |  |  |  |  | MRW =>   'MinoltaRaw', | 
| 112 |  |  |  |  |  |  | PDF => [ undef,         'WritePDF' ], | 
| 113 |  |  |  |  |  |  | PNG =>   undef, | 
| 114 |  |  |  |  |  |  | PPM =>   undef, | 
| 115 |  |  |  |  |  |  | PS  => [ 'PostScript',  'WritePS'  ], | 
| 116 |  |  |  |  |  |  | PSD =>   'Photoshop', | 
| 117 |  |  |  |  |  |  | RAF => [ 'FujiFilm',    'WriteRAF' ], | 
| 118 |  |  |  |  |  |  | RIFF=> [ 'RIFF',        'WriteRIFF'], | 
| 119 |  |  |  |  |  |  | VRD =>   'CanonVRD', | 
| 120 |  |  |  |  |  |  | WEBP=> [ 'RIFF',        'WriteRIFF'], | 
| 121 |  |  |  |  |  |  | X3F =>   'SigmaRaw', | 
| 122 |  |  |  |  |  |  | XMP => [ undef,         'WriteXMP' ], | 
| 123 |  |  |  |  |  |  | ); | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | # RAW file types | 
| 126 |  |  |  |  |  |  | my %rawType = ( | 
| 127 |  |  |  |  |  |  | '3FR'=> 1,  CR3 => 1,  IIQ => 1,  NEF => 1,  RW2 => 1, | 
| 128 |  |  |  |  |  |  | ARQ => 1,  CRW => 1,  K25 => 1,  NRW => 1,  RWL => 1, | 
| 129 |  |  |  |  |  |  | ARW => 1,  DCR => 1,  KDC => 1,  ORF => 1,  SR2 => 1, | 
| 130 |  |  |  |  |  |  | ARW => 1,  ERF => 1,  MEF => 1,  PEF => 1,  SRF => 1, | 
| 131 |  |  |  |  |  |  | CR2 => 1,  FFF => 1,  MOS => 1,  RAW => 1,  SRW => 1, | 
| 132 |  |  |  |  |  |  | ); | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | # groups we are allowed to delete | 
| 135 |  |  |  |  |  |  | # Notes: | 
| 136 |  |  |  |  |  |  | # 1) these names must either exist in %dirMap, or be translated in InitWriteDirs()) | 
| 137 |  |  |  |  |  |  | # 2) any dependencies must be added to %excludeGroups | 
| 138 |  |  |  |  |  |  | my @delGroups = qw( | 
| 139 |  |  |  |  |  |  | Adobe AFCP APP0 APP1 APP2 APP3 APP4 APP5 APP6 APP7 APP8 APP9 APP10 APP11 | 
| 140 |  |  |  |  |  |  | APP12 APP13 APP14 APP15 CanonVRD CIFF Ducky EXIF ExifIFD File FlashPix | 
| 141 |  |  |  |  |  |  | FotoStation GlobParamIFD GPS ICC_Profile IFD0 IFD1 Insta360 InteropIFD IPTC | 
| 142 |  |  |  |  |  |  | ItemList JFIF Jpeg2000 Keys MakerNotes Meta MetaIFD Microsoft MIE MPF | 
| 143 |  |  |  |  |  |  | NikonApp NikonCapture PDF PDF-update PhotoMechanic Photoshop PNG PNG-pHYs | 
| 144 |  |  |  |  |  |  | PrintIM QuickTime RMETA RSRC SubIFD Trailer UserData XML XML-* XMP XMP-* | 
| 145 |  |  |  |  |  |  | ); | 
| 146 |  |  |  |  |  |  | # family 2 group names that we can delete | 
| 147 |  |  |  |  |  |  | my @delGroup2 = qw( | 
| 148 |  |  |  |  |  |  | Audio Author Camera Document ExifTool Image Location Other Preview Printing | 
| 149 |  |  |  |  |  |  | Time Video | 
| 150 |  |  |  |  |  |  | ); | 
| 151 |  |  |  |  |  |  | # Extra groups to delete when deleting another group | 
| 152 |  |  |  |  |  |  | my %delMore = ( | 
| 153 |  |  |  |  |  |  | QuickTime => [ qw(ItemList UserData Keys) ], | 
| 154 |  |  |  |  |  |  | XMP => [ 'XMP-*' ], | 
| 155 |  |  |  |  |  |  | XML => [ 'XML-*' ], | 
| 156 |  |  |  |  |  |  | ); | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | # family 0 groups where directories should never be deleted | 
| 159 |  |  |  |  |  |  | my %permanentDir = ( QuickTime => 1, Jpeg2000 => 1 ); | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | # lookup for all valid family 2 groups (lower case) | 
| 162 |  |  |  |  |  |  | my %family2groups = map { lc $_ => 1 } @delGroup2, 'Unknown'; | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | # groups we don't delete when deleting all information | 
| 165 |  |  |  |  |  |  | my $protectedGroups = '(IFD1|SubIFD|InteropIFD|GlobParamIFD|PDF-update|Adobe)'; | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | # other group names of new tag values to remove when deleting an entire group | 
| 168 |  |  |  |  |  |  | my %removeGroups = ( | 
| 169 |  |  |  |  |  |  | IFD0    => [ 'EXIF', 'MakerNotes' ], | 
| 170 |  |  |  |  |  |  | EXIF    => [ 'MakerNotes' ], | 
| 171 |  |  |  |  |  |  | ExifIFD => [ 'MakerNotes', 'InteropIFD' ], | 
| 172 |  |  |  |  |  |  | Trailer => [ 'CanonVRD' ], #(because we can add back CanonVRD as a block) | 
| 173 |  |  |  |  |  |  | ); | 
| 174 |  |  |  |  |  |  | # related family 0/1 groups in @delGroups (and not already in %jpegMap) | 
| 175 |  |  |  |  |  |  | # that must be removed from delete list when excluding a group | 
| 176 |  |  |  |  |  |  | my %excludeGroups = ( | 
| 177 |  |  |  |  |  |  | EXIF         => [ qw(IFD0 IFD1 ExifIFD GPS MakerNotes GlobParamIFD InteropIFD PrintIM SubIFD) ], | 
| 178 |  |  |  |  |  |  | IFD0         => [ 'EXIF' ], | 
| 179 |  |  |  |  |  |  | IFD1         => [ 'EXIF' ], | 
| 180 |  |  |  |  |  |  | ExifIFD      => [ 'EXIF' ], | 
| 181 |  |  |  |  |  |  | GPS          => [ 'EXIF' ], | 
| 182 |  |  |  |  |  |  | MakerNotes   => [ 'EXIF' ], | 
| 183 |  |  |  |  |  |  | InteropIFD   => [ 'EXIF' ], | 
| 184 |  |  |  |  |  |  | GlobParamIFD => [ 'EXIF' ], | 
| 185 |  |  |  |  |  |  | PrintIM      => [ 'EXIF' ], | 
| 186 |  |  |  |  |  |  | CIFF         => [ 'MakerNotes' ], | 
| 187 |  |  |  |  |  |  | # technically correct, but very uncommon and not a good reason to avoid deleting trailer | 
| 188 |  |  |  |  |  |  | # IPTC         => [ qw(AFCP FotoStation Trailer) ], | 
| 189 |  |  |  |  |  |  | AFCP         => [ 'Trailer' ], | 
| 190 |  |  |  |  |  |  | FotoStation  => [ 'Trailer' ], | 
| 191 |  |  |  |  |  |  | CanonVRD     => [ 'Trailer' ], | 
| 192 |  |  |  |  |  |  | PhotoMechanic=> [ 'Trailer' ], | 
| 193 |  |  |  |  |  |  | MIE          => [ 'Trailer' ], | 
| 194 |  |  |  |  |  |  | QuickTime    => [ qw(ItemList UserData Keys) ], | 
| 195 |  |  |  |  |  |  | ); | 
| 196 |  |  |  |  |  |  | # translate (lower case) wanted group when writing for tags where group name may change | 
| 197 |  |  |  |  |  |  | my %translateWantGroup = ( | 
| 198 |  |  |  |  |  |  | ciff  => 'canonraw', | 
| 199 |  |  |  |  |  |  | ); | 
| 200 |  |  |  |  |  |  | # group names to translate for writing | 
| 201 |  |  |  |  |  |  | my %translateWriteGroup = ( | 
| 202 |  |  |  |  |  |  | EXIF  => 'ExifIFD', | 
| 203 |  |  |  |  |  |  | Meta  => 'MetaIFD', | 
| 204 |  |  |  |  |  |  | File  => 'Comment', | 
| 205 |  |  |  |  |  |  | # any entry in this table causes the write group to be set from the | 
| 206 |  |  |  |  |  |  | # tag information instead of whatever the user specified... | 
| 207 |  |  |  |  |  |  | MIE   => 'MIE', | 
| 208 |  |  |  |  |  |  | APP14 => 'APP14', | 
| 209 |  |  |  |  |  |  | ); | 
| 210 |  |  |  |  |  |  | # names of valid EXIF and Meta directories (lower case keys): | 
| 211 |  |  |  |  |  |  | my %exifDirs = ( | 
| 212 |  |  |  |  |  |  | gps          => 'GPS', | 
| 213 |  |  |  |  |  |  | exififd      => 'ExifIFD', | 
| 214 |  |  |  |  |  |  | subifd       => 'SubIFD', | 
| 215 |  |  |  |  |  |  | globparamifd => 'GlobParamIFD', | 
| 216 |  |  |  |  |  |  | interopifd   => 'InteropIFD', | 
| 217 |  |  |  |  |  |  | previewifd   => 'PreviewIFD', # (in MakerNotes) | 
| 218 |  |  |  |  |  |  | metaifd      => 'MetaIFD', # Kodak APP3 Meta | 
| 219 |  |  |  |  |  |  | makernotes   => 'MakerNotes', | 
| 220 |  |  |  |  |  |  | ); | 
| 221 |  |  |  |  |  |  | # valid family 0 groups when WriteGroup is set to "All" | 
| 222 |  |  |  |  |  |  | my %allFam0 = ( | 
| 223 |  |  |  |  |  |  | exif         => 1, | 
| 224 |  |  |  |  |  |  | makernotes   => 1, | 
| 225 |  |  |  |  |  |  | ); | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | my @writableMacOSTags = qw( | 
| 228 |  |  |  |  |  |  | FileCreateDate MDItemFinderComment MDItemFSCreationDate MDItemFSLabel MDItemUserTags | 
| 229 |  |  |  |  |  |  | XAttrQuarantine | 
| 230 |  |  |  |  |  |  | ); | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | # min/max values for integer formats | 
| 233 |  |  |  |  |  |  | my %intRange = ( | 
| 234 |  |  |  |  |  |  | 'int8u'  => [0, 0xff], | 
| 235 |  |  |  |  |  |  | 'int8s'  => [-0x80, 0x7f], | 
| 236 |  |  |  |  |  |  | 'int16u' => [0, 0xffff], | 
| 237 |  |  |  |  |  |  | 'int16uRev' => [0, 0xffff], | 
| 238 |  |  |  |  |  |  | 'int16s' => [-0x8000, 0x7fff], | 
| 239 |  |  |  |  |  |  | 'int32u' => [0, 0xffffffff], | 
| 240 |  |  |  |  |  |  | 'int32s' => [-0x80000000, 0x7fffffff], | 
| 241 |  |  |  |  |  |  | 'int64u' => [0, 18446744073709551615], | 
| 242 |  |  |  |  |  |  | 'int64s' => [-9223372036854775808, 9223372036854775807], | 
| 243 |  |  |  |  |  |  | ); | 
| 244 |  |  |  |  |  |  | # lookup for file types with block-writable EXIF | 
| 245 |  |  |  |  |  |  | my %blockExifTypes = map { $_ => 1 } qw(JPEG PNG JP2 MIE EXIF FLIF MOV MP4 RIFF); | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | my $maxSegmentLen = 0xfffd;     # maximum length of data in a JPEG segment | 
| 248 |  |  |  |  |  |  | my $maxXMPLen = $maxSegmentLen; # maximum length of XMP data in JPEG | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | # value separators when conversion list is used (in SetNewValue) | 
| 251 |  |  |  |  |  |  | my %listSep = ( PrintConv => '; ?', ValueConv => ' ' ); | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | # printConv hash keys to ignore when doing reverse lookup | 
| 254 |  |  |  |  |  |  | my %ignorePrintConv = map { $_ => 1 } qw(OTHER BITMASK Notes); | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 257 |  |  |  |  |  |  | # Set tag value | 
| 258 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 259 |  |  |  |  |  |  | #         1) tag key, tag name, or '*' (optionally prefixed by group name), | 
| 260 |  |  |  |  |  |  | #            or undef to reset all previous SetNewValue() calls | 
| 261 |  |  |  |  |  |  | #         2) new value (scalar, scalar ref or list ref), or undef to delete tag | 
| 262 |  |  |  |  |  |  | #         3-N) Options: | 
| 263 |  |  |  |  |  |  | #           Type => PrintConv, ValueConv or Raw - specifies value type | 
| 264 |  |  |  |  |  |  | #           AddValue => true to add to list of existing values instead of overwriting | 
| 265 |  |  |  |  |  |  | #           DelValue => true to delete this existing value value from a list, or | 
| 266 |  |  |  |  |  |  | #                       or doing a conditional delete, or to shift a time value | 
| 267 |  |  |  |  |  |  | #           Group => family 0 or 1 group name (case insensitive) | 
| 268 |  |  |  |  |  |  | #           Replace => 0, 1 or 2 - overwrite previous new values (2=reset) | 
| 269 |  |  |  |  |  |  | #           Protected => bitmask to write tags with specified protections | 
| 270 |  |  |  |  |  |  | #           EditOnly => true to only edit existing tags (don't create new tag) | 
| 271 |  |  |  |  |  |  | #           EditGroup => true to only edit existing groups (don't create new group) | 
| 272 |  |  |  |  |  |  | #           Shift => undef, 0, +1 or -1 - shift value if possible | 
| 273 |  |  |  |  |  |  | #           NoFlat => treat flattened tags as 'unsafe' | 
| 274 |  |  |  |  |  |  | #           NoShortcut => true to prevent looking up shortcut tags | 
| 275 |  |  |  |  |  |  | #           ProtectSaved => protect existing new values with a save count greater than this | 
| 276 |  |  |  |  |  |  | #           IgnorePermanent => ignore attempts to delete a permanent tag | 
| 277 |  |  |  |  |  |  | #           CreateGroups => [internal use] createGroups hash ref from related tags | 
| 278 |  |  |  |  |  |  | #           ListOnly => [internal use] set only list or non-list tags | 
| 279 |  |  |  |  |  |  | #           SetTags => [internal use] hash ref to return tagInfo refs of set tags | 
| 280 |  |  |  |  |  |  | #           Sanitized => [internal use] set to avoid double-sanitizing the value | 
| 281 |  |  |  |  |  |  | # Returns: number of tags set (plus error string in list context) | 
| 282 |  |  |  |  |  |  | # Notes: For tag lists (like Keywords), call repeatedly with the same tag name for | 
| 283 |  |  |  |  |  |  | #        each value in the list.  Internally, the new information is stored in | 
| 284 |  |  |  |  |  |  | #        the following members of the $$self{NEW_VALUE}{$tagInfo} hash: | 
| 285 |  |  |  |  |  |  | #           TagInfo - tag info ref | 
| 286 |  |  |  |  |  |  | #           DelValue - list ref for raw values to delete | 
| 287 |  |  |  |  |  |  | #           Value - list ref for raw values to add (not defined if deleting the tag) | 
| 288 |  |  |  |  |  |  | #           IsCreating - must be set for the tag to be added for the standard file types, | 
| 289 |  |  |  |  |  |  | #                        otherwise just changed if it already exists.  This may be | 
| 290 |  |  |  |  |  |  | #                        overridden for file types with a PREFERRED metadata type. | 
| 291 |  |  |  |  |  |  | #                        Set to 2 to create individual tags but not new groups | 
| 292 |  |  |  |  |  |  | #           EditOnly - flag set if tag should never be created (regardless of file type). | 
| 293 |  |  |  |  |  |  | #                      If this is set, then IsCreating must be false | 
| 294 |  |  |  |  |  |  | #           CreateOnly - flag set if creating only (never edit existing tag) | 
| 295 |  |  |  |  |  |  | #           CreateGroups - hash of all family 0 group names where tag may be created | 
| 296 |  |  |  |  |  |  | #           WriteGroup - group name where information is being written (correct case) | 
| 297 |  |  |  |  |  |  | #           WantGroup - group name as specified in call to function (case insensitive) | 
| 298 |  |  |  |  |  |  | #           Next - pointer to next new value hash (if more than one) | 
| 299 |  |  |  |  |  |  | #           NoReplace - set if value was created with Replace=0 | 
| 300 |  |  |  |  |  |  | #           AddBefore - number of list items added by a subsequent Replace=0 call | 
| 301 |  |  |  |  |  |  | #           IsNVH - Flag indicating this is a new value hash | 
| 302 |  |  |  |  |  |  | #           Shift - shift value | 
| 303 |  |  |  |  |  |  | #           Save - counter used by SaveNewValues()/RestoreNewValues() | 
| 304 |  |  |  |  |  |  | #           MAKER_NOTE_FIXUP - pointer to fixup if necessary for a maker note value | 
| 305 |  |  |  |  |  |  | sub SetNewValue($;$$%) | 
| 306 |  |  |  |  |  |  | { | 
| 307 | 5702 |  |  | 5702 | 1 | 47063 | local $_; | 
| 308 | 5702 |  |  |  |  | 21102 | my ($self, $tag, $value, %options) = @_; | 
| 309 | 5702 |  |  |  |  | 10451 | my ($err, $tagInfo, $family); | 
| 310 | 5702 |  |  |  |  | 14543 | my $verbose = $$self{OPTIONS}{Verbose}; | 
| 311 | 5702 |  |  |  |  | 10914 | my $out = $$self{OPTIONS}{TextOut}; | 
| 312 | 5702 |  | 100 |  |  | 19569 | my $protected = $options{Protected} || 0; | 
| 313 | 5702 |  |  |  |  | 10070 | my $listOnly = $options{ListOnly}; | 
| 314 | 5702 |  |  |  |  | 10123 | my $setTags = $options{SetTags}; | 
| 315 | 5702 |  |  |  |  | 9319 | my $noFlat = $options{NoFlat}; | 
| 316 | 5702 |  |  |  |  | 9217 | my $numSet = 0; | 
| 317 |  |  |  |  |  |  |  | 
| 318 | 5702 | 100 |  |  |  | 12663 | unless (defined $tag) { | 
| 319 | 40 |  |  |  |  | 960 | delete $$self{NEW_VALUE}; | 
| 320 | 40 |  |  |  |  | 121 | $$self{SAVE_COUNT} = 0; | 
| 321 | 40 |  |  |  |  | 170 | $$self{DEL_GROUP} = { }; | 
| 322 | 40 |  |  |  |  | 189 | return 1; | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  | # allow value to be scalar or list reference | 
| 325 | 5662 | 100 |  |  |  | 13093 | if (ref $value) { | 
| 326 | 218 | 100 |  |  |  | 1331 | if (ref $value eq 'ARRAY') { | 
|  |  | 100 |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | # value is an ARRAY so it may have more than one entry | 
| 328 |  |  |  |  |  |  | # - set values both separately and as a combined string if there are more than one | 
| 329 | 78 | 100 |  |  |  | 315 | if (@$value > 1) { | 
| 330 |  |  |  |  |  |  | # set all list-type tags first | 
| 331 | 51 |  |  |  |  | 121 | my $replace = $options{Replace}; | 
| 332 | 51 |  |  |  |  | 104 | my $noJoin; | 
| 333 | 51 |  |  |  |  | 154 | foreach (@$value) { | 
| 334 | 153 | 100 |  |  |  | 398 | $noJoin = 1 if ref $_; | 
| 335 | 153 |  |  |  |  | 811 | my ($n, $e) = SetNewValue($self, $tag, $_, %options, ListOnly => 1); | 
| 336 | 153 | 100 |  |  |  | 450 | $err = $e if $e; | 
| 337 | 153 |  |  |  |  | 285 | $numSet += $n; | 
| 338 | 153 |  |  |  |  | 474 | delete $options{Replace}; # don't replace earlier values in list | 
| 339 |  |  |  |  |  |  | } | 
| 340 | 51 | 100 |  |  |  | 267 | return $numSet if $noJoin;  # don't join if list contains objects | 
| 341 |  |  |  |  |  |  | # and now set only non-list tags | 
| 342 | 50 |  |  |  |  | 294 | $value = join $$self{OPTIONS}{ListSep}, @$value; | 
| 343 | 50 |  |  |  |  | 864 | $options{Replace} = $replace; | 
| 344 | 50 |  |  |  |  | 166 | $listOnly = $options{ListOnly} = 0; | 
| 345 |  |  |  |  |  |  | } else { | 
| 346 | 27 |  |  |  |  | 91 | $value = $$value[0]; | 
| 347 | 27 | 50 |  |  |  | 128 | $value = $$value if ref $value eq 'SCALAR'; # (handle single scalar ref in a list) | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  | } elsif (ref $value eq 'SCALAR') { | 
| 350 | 127 |  |  |  |  | 390 | $value = $$value; | 
| 351 |  |  |  |  |  |  | } | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  | # un-escape as necessary and make sure the Perl UTF-8 flag is OFF for the value | 
| 354 |  |  |  |  |  |  | # if perl is 5.6 or greater (otherwise our byte manipulations get corrupted!!) | 
| 355 | 5661 | 100 | 100 |  |  | 39640 | $self->Sanitize(\$value) if defined $value and not ref $value and not $options{Sanitized}; | 
|  |  |  | 100 |  |  |  |  | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | # set group name in options if specified | 
| 358 | 5661 | 100 |  |  |  | 19781 | ($options{Group}, $tag) = ($1, $2) if $tag =~ /(.*):(.+)/; | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | # allow trailing '#' for ValueConv value | 
| 361 | 5661 | 100 |  |  |  | 15783 | $options{Type} = 'ValueConv' if $tag =~ s/#$//; | 
| 362 | 5661 |  | 66 |  |  | 24697 | my $convType = $options{Type} || ($$self{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv'); | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | # filter value if necessary | 
| 365 | 5661 | 100 | 50 |  |  | 26096 | $self->Filter($$self{OPTIONS}{FilterW}, \$value) or return 0 if $convType eq 'PrintConv'; | 
| 366 |  |  |  |  |  |  |  | 
| 367 | 5661 |  |  |  |  | 10573 | my (@wantGroup, $family2); | 
| 368 | 5661 |  |  |  |  | 10545 | my $wantGroup = $options{Group}; | 
| 369 | 5661 | 100 |  |  |  | 12445 | if ($wantGroup) { | 
| 370 | 2446 |  |  |  |  | 8183 | foreach (split /:/, $wantGroup) { | 
| 371 | 2472 | 50 | 33 |  |  | 16713 | next unless length($_) and /^(\d+)?(.*)/; # separate family number and group name | 
| 372 | 2472 |  |  |  |  | 8571 | my ($f, $g) = ($1, $2); | 
| 373 | 2472 |  |  |  |  | 5500 | my $lcg = lc $g; | 
| 374 |  |  |  |  |  |  | # save group/family unless '*' or 'all' | 
| 375 | 2472 | 100 | 66 |  |  | 12295 | push @wantGroup, [ $f, $lcg ] unless $lcg eq '*' or $lcg eq 'all'; | 
| 376 | 2472 | 100 |  |  |  | 9498 | if ($g =~ s/^ID-//i) {          # family 7 is a tag ID | 
|  |  | 100 |  |  |  |  |  | 
| 377 | 1 | 50 | 33 |  |  | 10 | return 0 if defined $f and $f ne 7; | 
| 378 | 1 |  |  |  |  | 6 | $wantGroup[-1] = [ 7, $g ]; # group name with 'ID-' removed and case preserved | 
| 379 |  |  |  |  |  |  | } elsif (defined $f) { | 
| 380 | 30 | 50 |  |  |  | 124 | $f > 2 and return 0;        # only allow family 0, 1 or 2 | 
| 381 | 30 | 100 |  |  |  | 113 | $family2 = 1 if $f == 2;    # set flag indicating family 2 was used | 
| 382 |  |  |  |  |  |  | } else { | 
| 383 | 2441 | 100 |  |  |  | 8606 | $family2 = 1 if $family2groups{$lcg}; | 
| 384 |  |  |  |  |  |  | } | 
| 385 |  |  |  |  |  |  | } | 
| 386 | 2446 | 100 |  |  |  | 6055 | undef $wantGroup unless @wantGroup; | 
| 387 |  |  |  |  |  |  | } | 
| 388 |  |  |  |  |  |  |  | 
| 389 | 5661 |  |  |  |  | 12075 | $tag =~ s/ .*//;    # convert from tag key to tag name if necessary | 
| 390 | 5661 | 100 |  |  |  | 13997 | $tag = '*' if lc($tag) eq 'all';    # use '*' instead of 'all' | 
| 391 |  |  |  |  |  |  | # | 
| 392 |  |  |  |  |  |  | # handle group delete | 
| 393 |  |  |  |  |  |  | # | 
| 394 | 5661 |  | 100 |  |  | 15938 | while ($tag eq '*' and not defined $value and not $family2 and @wantGroup < 2) { | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 395 |  |  |  |  |  |  | # set groups to delete | 
| 396 | 47 |  |  |  |  | 120 | my (@del, $grp); | 
| 397 | 47 |  | 66 |  |  | 225 | my $remove = ($options{Replace} and $options{Replace} > 1); | 
| 398 | 47 | 100 |  |  |  | 199 | if ($wantGroup) { | 
| 399 | 34 | 50 |  |  |  | 2285 | @del = grep /^$wantGroup$/i, @delGroups unless $wantGroup =~ /^XM[LP]-\*$/i; | 
| 400 |  |  |  |  |  |  | # remove associated groups when excluding from mass delete | 
| 401 | 34 | 100 | 100 |  |  | 251 | if (@del and $remove) { | 
| 402 |  |  |  |  |  |  | # remove associated groups in other family | 
| 403 | 4 | 100 |  |  |  | 23 | push @del, @{$excludeGroups{$del[0]}} if $excludeGroups{$del[0]}; | 
|  | 2 |  |  |  |  | 12 |  | 
| 404 |  |  |  |  |  |  | # remove upstream groups according to JPEG map | 
| 405 | 4 |  |  |  |  | 13 | my $dirName = $del[0]; | 
| 406 | 4 |  |  |  |  | 9 | my @dirNames; | 
| 407 | 4 |  |  |  |  | 9 | for (;;) { | 
| 408 | 10 |  |  |  |  | 23 | my $parent = $jpegMap{$dirName}; | 
| 409 | 10 | 50 |  |  |  | 28 | if (ref $parent) { | 
| 410 | 0 |  |  |  |  | 0 | push @dirNames, @$parent; | 
| 411 | 0 |  |  |  |  | 0 | $parent = pop @dirNames; | 
| 412 |  |  |  |  |  |  | } | 
| 413 | 10 | 100 | 66 |  |  | 46 | $dirName = $parent || shift @dirNames or last; | 
| 414 | 6 |  |  |  |  | 14 | push @del, $dirName;    # exclude this too | 
| 415 |  |  |  |  |  |  | } | 
| 416 |  |  |  |  |  |  | } | 
| 417 |  |  |  |  |  |  | # allow MIE groups to be deleted by number, | 
| 418 |  |  |  |  |  |  | # and allow any XMP family 1 group to be deleted | 
| 419 | 34 | 100 |  |  |  | 221 | push @del, uc($wantGroup) if $wantGroup =~ /^(MIE\d+|XM[LP]-[-\w]*\w)$/i; | 
| 420 |  |  |  |  |  |  | } else { | 
| 421 |  |  |  |  |  |  | # push all groups plus '*', except the protected groups | 
| 422 | 13 |  |  |  |  | 1505 | push @del, (grep !/^$protectedGroups$/, @delGroups), '*'; | 
| 423 |  |  |  |  |  |  | } | 
| 424 | 47 | 50 |  |  |  | 202 | if (@del) { | 
|  |  | 0 |  |  |  |  |  | 
| 425 | 47 |  |  |  |  | 110 | ++$numSet; | 
| 426 | 47 |  |  |  |  | 112 | my @donegrps; | 
| 427 | 47 |  |  |  |  | 139 | my $delGroup = $$self{DEL_GROUP}; | 
| 428 | 47 |  |  |  |  | 165 | foreach $grp (@del) { | 
| 429 | 804 | 100 |  |  |  | 1306 | if ($remove) { | 
| 430 | 23 |  |  |  |  | 38 | my $didExcl; | 
| 431 | 23 | 100 |  |  |  | 73 | if ($grp =~ /^(XM[LP])(-.*)?$/) { | 
| 432 | 4 |  |  |  |  | 15 | my $x = $1; | 
| 433 | 4 | 100 | 33 |  |  | 38 | if ($grp eq $x) { | 
|  |  | 50 |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | # exclude all related family 1 groups too | 
| 435 | 1 |  |  |  |  | 15 | foreach (keys %$delGroup) { | 
| 436 | 58 | 100 |  |  |  | 165 | next unless /^(-?)$x-/; | 
| 437 | 1 | 50 |  |  |  | 6 | push @donegrps, $_ unless $1; | 
| 438 | 1 |  |  |  |  | 5 | delete $$delGroup{$_}; | 
| 439 |  |  |  |  |  |  | } | 
| 440 |  |  |  |  |  |  | } elsif ($$delGroup{"$x-*"} and not $$delGroup{"-$grp"}) { | 
| 441 |  |  |  |  |  |  | # must also exclude XMP or XML to prevent bulk delete | 
| 442 | 3 | 100 |  |  |  | 14 | if ($$delGroup{$x}) { | 
| 443 | 2 |  |  |  |  | 6 | push @donegrps, $x; | 
| 444 | 2 |  |  |  |  | 8 | delete $$delGroup{$x}; | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  | # flag XMP/XML family 1 group for exclusion with leading '-' | 
| 447 | 3 |  |  |  |  | 9 | $$delGroup{"-$grp"} = 1; | 
| 448 | 3 |  |  |  |  | 9 | $didExcl = 1; | 
| 449 |  |  |  |  |  |  | } | 
| 450 |  |  |  |  |  |  | } | 
| 451 | 23 | 100 |  |  |  | 54 | if (exists $$delGroup{$grp}) { | 
| 452 | 15 |  |  |  |  | 31 | delete $$delGroup{$grp}; | 
| 453 |  |  |  |  |  |  | } else { | 
| 454 | 8 | 100 |  |  |  | 22 | next unless $didExcl; | 
| 455 |  |  |  |  |  |  | } | 
| 456 |  |  |  |  |  |  | } else { | 
| 457 | 781 |  |  |  |  | 1672 | $$delGroup{$grp} = 1; | 
| 458 |  |  |  |  |  |  | # add extra groups to delete if necessary | 
| 459 | 781 | 100 |  |  |  | 1738 | if ($delMore{$grp}) { | 
| 460 | 49 |  |  |  |  | 100 | $$delGroup{$_} = 1, push @donegrps, $_ foreach @{$delMore{$grp}}; | 
|  | 49 |  |  |  |  | 286 |  | 
| 461 |  |  |  |  |  |  | } | 
| 462 |  |  |  |  |  |  | # remove all of this group from previous new values | 
| 463 | 781 |  |  |  |  | 1374 | $self->RemoveNewValuesForGroup($grp); | 
| 464 |  |  |  |  |  |  | } | 
| 465 | 799 |  |  |  |  | 1427 | push @donegrps, $grp; | 
| 466 |  |  |  |  |  |  | } | 
| 467 | 47 | 100 | 66 |  |  | 286 | if ($verbose > 1 and @donegrps) { | 
| 468 | 1 |  |  |  |  | 5 | @donegrps = sort @donegrps; | 
| 469 | 1 | 50 |  |  |  | 6 | my $msg = $remove ? 'Excluding from deletion' : 'Deleting tags in'; | 
| 470 | 1 |  |  |  |  | 11 | print $out "  $msg: @donegrps\n"; | 
| 471 |  |  |  |  |  |  | } | 
| 472 |  |  |  |  |  |  | } elsif (grep /^$wantGroup$/i, @delGroup2) { | 
| 473 | 0 |  |  |  |  | 0 | last;   # allow tags to be deleted by group2 name | 
| 474 |  |  |  |  |  |  | } else { | 
| 475 | 0 |  |  |  |  | 0 | $err = "Not a deletable group: $wantGroup"; | 
| 476 |  |  |  |  |  |  | } | 
| 477 |  |  |  |  |  |  | # all done | 
| 478 | 47 | 50 |  |  |  | 181 | return ($numSet, $err) if wantarray; | 
| 479 | 47 | 50 |  |  |  | 166 | $err and warn "$err\n"; | 
| 480 | 47 |  |  |  |  | 307 | return $numSet; | 
| 481 |  |  |  |  |  |  | } | 
| 482 |  |  |  |  |  |  |  | 
| 483 |  |  |  |  |  |  | # initialize write/create flags | 
| 484 | 5614 |  |  |  |  | 8481 | my $createOnly; | 
| 485 | 5614 |  |  |  |  | 10051 | my $editOnly = $options{EditOnly}; | 
| 486 | 5614 |  |  |  |  | 9078 | my $editGroup = $options{EditGroup}; | 
| 487 | 5614 |  |  |  |  | 11708 | my $writeMode = $$self{OPTIONS}{WriteMode}; | 
| 488 | 5614 | 100 |  |  |  | 12631 | if ($writeMode ne 'wcg') { | 
| 489 | 27 | 100 |  |  |  | 101 | $createOnly = 1 if $writeMode !~ /w/i;  # don't write existing tags | 
| 490 | 27 | 100 |  |  |  | 142 | if ($writeMode !~ /c/i) { | 
|  |  | 100 |  |  |  |  |  | 
| 491 | 2 | 50 |  |  |  | 8 | return 0 if $createOnly;    # nothing to do unless writing existing tags | 
| 492 | 2 |  |  |  |  | 15 | $editOnly = 1;              # don't create new tags | 
| 493 |  |  |  |  |  |  | } elsif ($writeMode !~ /g/i) { | 
| 494 | 8 |  |  |  |  | 47 | $editGroup = 1;             # don't create new groups | 
| 495 |  |  |  |  |  |  | } | 
| 496 |  |  |  |  |  |  | } | 
| 497 | 5614 |  |  |  |  | 10333 | my ($ifdName, $mieGroup, $movGroup, $fg); | 
| 498 |  |  |  |  |  |  | # set family 1 group names | 
| 499 | 5614 |  |  |  |  | 11400 | foreach $fg (@wantGroup) { | 
| 500 | 2338 | 100 | 100 |  |  | 6780 | next if defined $$fg[0] and $$fg[0] != 1; | 
| 501 | 2319 |  |  |  |  | 4756 | $_ = $$fg[1]; | 
| 502 |  |  |  |  |  |  | # set $ifdName if this group is a valid IFD or SubIFD name | 
| 503 | 2319 |  |  |  |  | 3363 | my $grpName; | 
| 504 | 2319 | 100 | 100 |  |  | 19598 | if (/^IFD(\d+)$/i) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 505 | 131 |  |  |  |  | 411 | $grpName = $ifdName = "IFD$1"; | 
| 506 |  |  |  |  |  |  | } elsif (/^SubIFD(\d+)$/i) { | 
| 507 | 0 |  |  |  |  | 0 | $grpName = $ifdName = "SubIFD$1"; | 
| 508 |  |  |  |  |  |  | } elsif (/^Version(\d+)$/i) { | 
| 509 | 0 |  |  |  |  | 0 | $grpName = $ifdName = "Version$1"; # Sony IDC VersionIFD | 
| 510 |  |  |  |  |  |  | } elsif ($exifDirs{$_}) { | 
| 511 | 318 |  |  |  |  | 790 | $grpName = $exifDirs{$_}; | 
| 512 | 318 | 50 | 33 |  |  | 956 | $ifdName = $grpName unless $ifdName and $allFam0{$_}; | 
| 513 |  |  |  |  |  |  | } elsif ($allFam0{$_}) { | 
| 514 | 293 |  |  |  |  | 678 | $grpName = $allFam0{$_}; | 
| 515 |  |  |  |  |  |  | } elsif (/^Track(\d+)$/i) { | 
| 516 | 1 |  |  |  |  | 6 | $grpName = $movGroup = "Track$1";  # QuickTime track | 
| 517 |  |  |  |  |  |  | } elsif (/^MIE(\d*-?)(\w+)$/i) { | 
| 518 | 2 |  |  |  |  | 12 | $grpName = $mieGroup = "MIE$1" . ucfirst(lc($2)); | 
| 519 |  |  |  |  |  |  | } elsif (not $ifdName and /^XMP\b/i) { | 
| 520 |  |  |  |  |  |  | # must load XMP table to set group1 names | 
| 521 | 500 |  |  |  |  | 1897 | my $table = GetTagTable('Image::ExifTool::XMP::Main'); | 
| 522 | 500 |  |  |  |  | 1761 | my $writeProc = $$table{WRITE_PROC}; | 
| 523 | 500 | 50 |  |  |  | 1288 | if ($writeProc) { | 
| 524 | 58 |  |  | 58 |  | 567 | no strict 'refs'; | 
|  | 58 |  |  |  |  | 141 |  | 
|  | 58 |  |  |  |  | 87413 |  | 
| 525 | 500 |  |  |  |  | 1664 | &$writeProc(); | 
| 526 |  |  |  |  |  |  | } | 
| 527 |  |  |  |  |  |  | } | 
| 528 |  |  |  |  |  |  | # fix case for known groups | 
| 529 | 2319 | 100 | 66 |  |  | 12462 | $wantGroup =~ s/$grpName/$grpName/i if $grpName and $grpName ne $_; | 
| 530 |  |  |  |  |  |  | } | 
| 531 |  |  |  |  |  |  | # | 
| 532 |  |  |  |  |  |  | # get list of tags we want to set | 
| 533 |  |  |  |  |  |  | # | 
| 534 | 5614 |  |  |  |  | 9986 | my $origTag = $tag; | 
| 535 | 5614 |  |  |  |  | 18615 | my @matchingTags = FindTagInfo($tag); | 
| 536 | 5614 |  |  |  |  | 16094 | until (@matchingTags) { | 
| 537 | 1416 |  |  |  |  | 2695 | my $langCode; | 
| 538 |  |  |  |  |  |  | # allow language suffix of form "-en_CA" or "-" on tag name | 
| 539 | 1416 | 100 | 100 |  |  | 8027 | if ($tag =~ /^([?*\w]+)-([a-z]{2})(_[a-z]{2})$/i or # MIE | 
|  |  | 50 |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | $tag =~ /^([?*\w]+)-([a-z]{2,3}|[xi])(-[a-z\d]{2,8}(-[a-z\d]{1,8})*)?$/i) # XMP/PNG/QuickTime | 
| 541 |  |  |  |  |  |  | { | 
| 542 | 51 |  |  |  |  | 175 | $tag = $1; | 
| 543 |  |  |  |  |  |  | # normalize case of language codes | 
| 544 | 51 |  |  |  |  | 148 | $langCode = lc($2); | 
| 545 | 51 | 100 |  |  |  | 265 | $langCode .= (length($3) == 3 ? uc($3) : lc($3)) if $3; | 
|  |  | 100 |  |  |  |  |  | 
| 546 | 51 |  |  |  |  | 151 | my @newMatches = FindTagInfo($tag); | 
| 547 | 51 |  |  |  |  | 133 | foreach $tagInfo (@newMatches) { | 
| 548 |  |  |  |  |  |  | # only allow language codes in tables which support them | 
| 549 | 238 | 50 |  |  |  | 712 | next unless $$tagInfo{Table}; | 
| 550 | 238 | 100 |  |  |  | 620 | my $langInfoProc = $$tagInfo{Table}{LANG_INFO} or next; | 
| 551 | 186 |  |  |  |  | 630 | my $langInfo = &$langInfoProc($tagInfo, $langCode); | 
| 552 | 186 | 100 |  |  |  | 545 | push @matchingTags, $langInfo if $langInfo; | 
| 553 |  |  |  |  |  |  | } | 
| 554 | 51 | 100 |  |  |  | 225 | last if @matchingTags; | 
| 555 |  |  |  |  |  |  | } elsif (not $options{NoShortcut}) { | 
| 556 |  |  |  |  |  |  | # look for a shortcut or alias | 
| 557 | 1365 |  |  |  |  | 10585 | require Image::ExifTool::Shortcuts; | 
| 558 | 1365 |  |  |  |  | 32729 | my ($match) = grep /^\Q$tag\E$/i, keys %Image::ExifTool::Shortcuts::Main; | 
| 559 | 1365 |  |  |  |  | 4115 | undef $err; | 
| 560 | 1365 | 100 |  |  |  | 3747 | if ($match) { | 
| 561 | 1 |  |  |  |  | 5 | $options{NoShortcut} = $options{Sanitized} = 1; | 
| 562 | 1 |  |  |  |  | 2 | foreach $tag (@{$Image::ExifTool::Shortcuts::Main{$match}}) { | 
|  | 1 |  |  |  |  | 4 |  | 
| 563 | 3 |  |  |  |  | 69 | my ($n, $e) = $self->SetNewValue($tag, $value, %options); | 
| 564 | 3 |  |  |  |  | 8 | $numSet += $n; | 
| 565 | 3 | 50 |  |  |  | 13 | $e and $err = $e; | 
| 566 |  |  |  |  |  |  | } | 
| 567 | 1 | 50 |  |  |  | 4 | undef $err if $numSet;  # no error if any set successfully | 
| 568 | 1 | 50 |  |  |  | 5 | return ($numSet, $err) if wantarray; | 
| 569 | 1 | 50 |  |  |  | 3 | $err and warn "$err\n"; | 
| 570 | 1 |  |  |  |  | 10 | return $numSet; | 
| 571 |  |  |  |  |  |  | } | 
| 572 |  |  |  |  |  |  | } | 
| 573 | 1366 | 50 |  |  |  | 3217 | unless ($listOnly) { | 
| 574 | 1366 | 100 |  |  |  | 4269 | if (not TagExists($tag)) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 575 | 49 | 50 |  |  |  | 243 | if ($tag =~ /^[-\w*?]+$/) { | 
| 576 | 49 | 100 |  |  |  | 151 | my $pre = $wantGroup ? $wantGroup . ':' : ''; | 
| 577 | 49 |  |  |  |  | 135 | $err = "Tag '$pre${origTag}' is not defined"; | 
| 578 | 49 | 100 |  |  |  | 152 | $err .= ' or has a bad language code' if $origTag =~ /-/; | 
| 579 | 49 | 50 | 66 |  |  | 168 | if (not $pre and uc($origTag) eq 'TAG') { | 
| 580 | 0 |  |  |  |  | 0 | $err .= " (specify a writable tag name, not '${origTag}' literally)" | 
| 581 |  |  |  |  |  |  | } | 
| 582 |  |  |  |  |  |  | } else { | 
| 583 | 0 |  |  |  |  | 0 | $err = "Invalid tag name '${tag}'"; | 
| 584 | 0 | 0 |  |  |  | 0 | $err .= " (remove the leading '\$')" if $tag =~ /^\$/; | 
| 585 |  |  |  |  |  |  | } | 
| 586 |  |  |  |  |  |  | } elsif ($langCode) { | 
| 587 | 0 |  |  |  |  | 0 | $err = "Tag '${tag}' does not support alternate languages"; | 
| 588 |  |  |  |  |  |  | } elsif ($wantGroup) { | 
| 589 | 507 |  |  |  |  | 1488 | $err = "Sorry, $wantGroup:$origTag doesn't exist or isn't writable"; | 
| 590 |  |  |  |  |  |  | } else { | 
| 591 | 810 |  |  |  |  | 2383 | $err = "Sorry, $origTag is not writable"; | 
| 592 |  |  |  |  |  |  | } | 
| 593 | 1366 | 50 |  |  |  | 3483 | $verbose > 2 and print $out "$err\n"; | 
| 594 |  |  |  |  |  |  | } | 
| 595 |  |  |  |  |  |  | # all done | 
| 596 | 1366 | 50 |  |  |  | 7954 | return ($numSet, $err) if wantarray; | 
| 597 | 0 | 0 |  |  |  | 0 | $err and warn "$err\n"; | 
| 598 | 0 |  |  |  |  | 0 | return $numSet; | 
| 599 |  |  |  |  |  |  | } | 
| 600 |  |  |  |  |  |  | # get group name that we're looking for | 
| 601 | 4247 |  |  |  |  | 7743 | my $foundMatch = 0; | 
| 602 |  |  |  |  |  |  | # | 
| 603 |  |  |  |  |  |  | # determine the groups for all tags found, and the tag with | 
| 604 |  |  |  |  |  |  | # the highest priority group | 
| 605 |  |  |  |  |  |  | # | 
| 606 | 4247 |  |  |  |  | 11354 | my (@tagInfoList, @writeAlsoList, %writeGroup, %preferred, %tagPriority); | 
| 607 | 4247 |  |  |  |  | 0 | my (%avoid, $wasProtected, $noCreate, %highestPriority, %highestQT); | 
| 608 |  |  |  |  |  |  |  | 
| 609 | 4247 |  |  |  |  | 9022 | TAG: foreach $tagInfo (@matchingTags) { | 
| 610 | 69370 |  |  |  |  | 258461 | $tag = $$tagInfo{Name};     # get tag name for warnings | 
| 611 | 69370 |  |  |  |  | 113129 | my $lcTag = lc $tag;        # get lower-case tag name for use in variables | 
| 612 |  |  |  |  |  |  | # initialize highest priority if we are starting a new tag | 
| 613 | 69370 | 100 |  |  |  | 186215 | $highestPriority{$lcTag} = -999 unless defined $highestPriority{$lcTag}; | 
| 614 | 69370 |  |  |  |  | 102325 | my ($priority, $writeGroup); | 
| 615 | 69370 | 100 |  |  |  | 206580 | my $prfTag = defined $$tagInfo{Preferred} ? $$tagInfo{Preferred} : $$tagInfo{Table}{PREFERRED}; | 
| 616 | 69370 | 100 |  |  |  | 127313 | if ($wantGroup) { | 
| 617 |  |  |  |  |  |  | # a WriteGroup of All is special | 
| 618 | 49375 |  | 100 |  |  | 101221 | my $wgAll = ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All'); | 
| 619 | 49375 |  |  |  |  | 123503 | my @grp = $self->GetGroup($tagInfo); | 
| 620 | 49375 |  |  |  |  | 86564 | my $hiPri = 1000; | 
| 621 | 49375 |  |  |  |  | 83937 | foreach $fg (@wantGroup) { | 
| 622 | 49413 |  |  |  |  | 92465 | my ($fam, $lcWant) = @$fg; | 
| 623 | 49413 | 100 |  |  |  | 103946 | $lcWant = $translateWantGroup{$lcWant} if $translateWantGroup{$lcWant}; | 
| 624 |  |  |  |  |  |  | # only set tag in specified group | 
| 625 |  |  |  |  |  |  | # bump priority of preferred tag | 
| 626 | 49413 | 100 |  |  |  | 88511 | $hiPri += $prfTag if $prfTag; | 
| 627 | 49413 | 100 | 66 |  |  | 90313 | if (not defined $fam) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 628 | 49171 | 100 |  |  |  | 100386 | if ($lcWant eq lc $grp[0]) { | 
| 629 |  |  |  |  |  |  | # don't go to more general write group of "All" | 
| 630 |  |  |  |  |  |  | # if something more specific was wanted | 
| 631 | 2166 | 100 | 100 |  |  | 5094 | $writeGroup = $grp[0] if $wgAll and not $writeGroup; | 
| 632 | 2166 |  |  |  |  | 4200 | next; | 
| 633 |  |  |  |  |  |  | } | 
| 634 | 47005 | 100 |  |  |  | 89455 | next if $lcWant eq lc $grp[2]; | 
| 635 |  |  |  |  |  |  | } elsif ($fam == 7) { | 
| 636 | 2 | 100 |  |  |  | 7 | next if IsSameID($$tagInfo{TagID}, $lcWant); | 
| 637 |  |  |  |  |  |  | } elsif ($fam != 1 and not $$tagInfo{AllowGroup}) { | 
| 638 | 132 | 100 |  |  |  | 335 | next if $lcWant eq lc $grp[$fam]; | 
| 639 | 110 | 100 | 100 |  |  | 345 | if ($wgAll and not $fam and $allFam0{$lcWant}) { | 
|  |  |  | 100 |  |  |  |  | 
| 640 | 5 | 100 |  |  |  | 24 | $writeGroup or $writeGroup = $allFam0{$lcWant}; | 
| 641 | 5 |  |  |  |  | 13 | next; | 
| 642 |  |  |  |  |  |  | } | 
| 643 | 105 |  |  |  |  | 275 | next TAG;   # wrong group | 
| 644 |  |  |  |  |  |  | } | 
| 645 |  |  |  |  |  |  | # handle family 1 groups specially | 
| 646 | 36670 | 100 | 66 |  |  | 206180 | if ($grp[0] eq 'EXIF' or $grp[0] eq 'SonyIDC' or $wgAll) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 647 | 1597 | 100 | 100 |  |  | 5509 | unless ($ifdName and $lcWant eq lc $ifdName) { | 
| 648 | 1119 | 100 | 100 |  |  | 4421 | next TAG unless $wgAll and not $fam and $allFam0{$lcWant}; | 
|  |  |  | 100 |  |  |  |  | 
| 649 | 7 | 100 |  |  |  | 33 | $writeGroup = $allFam0{$lcWant} unless $writeGroup; | 
| 650 | 7 |  |  |  |  | 19 | next; | 
| 651 |  |  |  |  |  |  | } | 
| 652 | 478 | 100 | 100 |  |  | 1325 | next TAG if $wgAll and $allFam0{$lcWant} and $fam; | 
|  |  |  | 100 |  |  |  |  | 
| 653 |  |  |  |  |  |  | # can't yet write PreviewIFD tags (except for image) | 
| 654 | 476 | 50 |  |  |  | 1009 | $lcWant eq 'PreviewIFD' and ++$foundMatch, next TAG; | 
| 655 | 476 |  |  |  |  | 1081 | $writeGroup = $ifdName; # write to the specified IFD | 
| 656 |  |  |  |  |  |  | } elsif ($grp[0] eq 'QuickTime') { | 
| 657 | 1552 | 100 |  |  |  | 3790 | if ($grp[1] eq 'Track#') { | 
| 658 | 16 | 100 | 66 |  |  | 91 | next TAG unless $movGroup and $lcWant eq lc($movGroup); | 
| 659 | 1 |  |  |  |  | 12 | $writeGroup = $movGroup; | 
| 660 |  |  |  |  |  |  | } else { | 
| 661 | 1536 |  |  |  |  | 3743 | my $grp = $$tagInfo{Table}{WRITE_GROUP}; | 
| 662 | 1536 | 100 | 100 |  |  | 7068 | next TAG unless $grp and $lcWant eq lc $grp; | 
| 663 | 28 |  |  |  |  | 74 | $writeGroup = $grp; | 
| 664 |  |  |  |  |  |  | } | 
| 665 |  |  |  |  |  |  | } elsif ($grp[0] eq 'MIE') { | 
| 666 | 768 | 100 | 66 |  |  | 3654 | next TAG unless $mieGroup and $lcWant eq lc($mieGroup); | 
| 667 | 2 |  |  |  |  | 6 | $writeGroup = $mieGroup; # write to specific MIE group | 
| 668 |  |  |  |  |  |  | # set specific write group with document number if specified | 
| 669 | 2 | 0 | 33 |  |  | 19 | if ($writeGroup =~ /^MIE\d+$/ and $$tagInfo{Table}{WRITE_GROUP}) { | 
| 670 | 0 |  |  |  |  | 0 | $writeGroup = $$tagInfo{Table}{WRITE_GROUP}; | 
| 671 | 0 |  |  |  |  | 0 | $writeGroup =~ s/^MIE/$mieGroup/; | 
| 672 |  |  |  |  |  |  | } | 
| 673 |  |  |  |  |  |  | } elsif (not $$tagInfo{AllowGroup} or $lcWant !~ /^$$tagInfo{AllowGroup}$/i) { | 
| 674 |  |  |  |  |  |  | # allow group1 name to be specified | 
| 675 | 32752 | 100 |  |  |  | 100109 | next TAG unless $lcWant eq lc $grp[1]; | 
| 676 |  |  |  |  |  |  | } | 
| 677 |  |  |  |  |  |  | } | 
| 678 | 13635 | 100 | 66 |  |  | 67019 | $writeGroup or $writeGroup = ($$tagInfo{WriteGroup} || $$tagInfo{Table}{WRITE_GROUP} || $grp[0]); | 
| 679 | 13635 |  |  |  |  | 26362 | $priority = $hiPri; # highest priority since group was specified | 
| 680 |  |  |  |  |  |  | } | 
| 681 | 33630 |  |  |  |  | 46913 | ++$foundMatch; | 
| 682 |  |  |  |  |  |  | # must do a dummy call to the write proc to autoload write package | 
| 683 |  |  |  |  |  |  | # before checking Writable flag | 
| 684 | 33630 |  |  |  |  | 52305 | my $table = $$tagInfo{Table}; | 
| 685 | 33630 |  |  |  |  | 64333 | my $writeProc = $$table{WRITE_PROC}; | 
| 686 |  |  |  |  |  |  | # load source table if this was a user-defined table | 
| 687 | 33630 | 100 |  |  |  | 72032 | if ($$table{SRC_TABLE}) { | 
| 688 | 9 |  |  |  |  | 43 | my $src = GetTagTable($$table{SRC_TABLE}); | 
| 689 | 9 | 50 |  |  |  | 23 | $writeProc = $$src{WRITE_PROC} unless $writeProc; | 
| 690 |  |  |  |  |  |  | } | 
| 691 |  |  |  |  |  |  | { | 
| 692 | 58 |  |  | 58 |  | 508 | no strict 'refs'; | 
|  | 58 |  |  |  |  | 158 |  | 
|  | 58 |  |  |  |  | 705401 |  | 
|  | 33630 |  |  |  |  | 47712 |  | 
| 693 | 33630 | 100 | 66 |  |  | 107806 | next unless $writeProc and &$writeProc(); | 
| 694 |  |  |  |  |  |  | } | 
| 695 |  |  |  |  |  |  | # must still check writable flags in case of UserDefined tags | 
| 696 | 33630 |  |  |  |  | 71142 | my $writable = $$tagInfo{Writable}; | 
| 697 |  |  |  |  |  |  | next unless $writable or ($$table{WRITABLE} and | 
| 698 | 33630 | 50 | 66 |  |  | 143305 | not defined $writable and not $$tagInfo{SubDirectory}); | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 699 |  |  |  |  |  |  | # set specific write group (if we didn't already) | 
| 700 | 33629 | 100 | 66 |  |  | 89095 | if (not $writeGroup or ($translateWriteGroup{$writeGroup} and | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 701 |  |  |  |  |  |  | (not $$tagInfo{WriteGroup} or $$tagInfo{WriteGroup} ne 'All'))) | 
| 702 |  |  |  |  |  |  | { | 
| 703 |  |  |  |  |  |  | # use default write group | 
| 704 | 20071 |  | 100 |  |  | 61903 | $writeGroup = $$tagInfo{WriteGroup} || $$tagInfo{Table}{WRITE_GROUP}; | 
| 705 |  |  |  |  |  |  | # use group 0 name if no WriteGroup specified | 
| 706 | 20071 |  |  |  |  | 53966 | my $group0 = $self->GetGroup($tagInfo, 0); | 
| 707 | 20071 | 100 |  |  |  | 46176 | $writeGroup or $writeGroup = $group0; | 
| 708 |  |  |  |  |  |  | # get priority for this group | 
| 709 | 20071 | 100 |  |  |  | 36648 | unless ($priority) { | 
| 710 | 19994 |  |  |  |  | 43859 | $priority = $$self{WRITE_PRIORITY}{lc($writeGroup)}; | 
| 711 | 19994 | 100 |  |  |  | 37728 | unless ($priority) { | 
| 712 | 3502 |  | 100 |  |  | 11295 | $priority = $$self{WRITE_PRIORITY}{lc($group0)} || 0; | 
| 713 |  |  |  |  |  |  | } | 
| 714 |  |  |  |  |  |  | } | 
| 715 |  |  |  |  |  |  | # adjust priority based on Preferred level for this tag | 
| 716 | 20071 | 100 |  |  |  | 40320 | $priority += $prfTag if $prfTag; | 
| 717 |  |  |  |  |  |  | } | 
| 718 |  |  |  |  |  |  | # don't write tag if protected | 
| 719 | 33629 |  |  |  |  | 54686 | my $prot = $$tagInfo{Protected}; | 
| 720 | 33629 | 100 | 100 |  |  | 72888 | $prot = 1 if $noFlat and defined $$tagInfo{Flat}; | 
| 721 | 33629 | 100 |  |  |  | 60858 | if ($prot) { | 
| 722 | 2237 |  |  |  |  | 4591 | $prot &= ~$protected; | 
| 723 | 2237 | 100 |  |  |  | 4671 | if ($prot) { | 
| 724 | 1200 |  |  |  |  | 4419 | my %lkup = ( 1=>'unsafe', 2=>'protected', 3=>'unsafe and protected'); | 
| 725 | 1200 |  |  |  |  | 2430 | $wasProtected = $lkup{$prot}; | 
| 726 | 1200 | 100 |  |  |  | 2683 | if ($verbose > 1) { | 
| 727 | 1 |  |  |  |  | 7 | my $wgrp1 = $self->GetWriteGroup1($tagInfo, $writeGroup); | 
| 728 | 1 |  |  |  |  | 9 | print $out "Sorry, $wgrp1:$tag is $wasProtected for writing\n"; | 
| 729 |  |  |  |  |  |  | } | 
| 730 | 1200 |  |  |  |  | 3567 | next; | 
| 731 |  |  |  |  |  |  | } | 
| 732 |  |  |  |  |  |  | } | 
| 733 |  |  |  |  |  |  | # set priority for this tag | 
| 734 | 32429 |  |  |  |  | 107535 | $tagPriority{$tagInfo} = $priority; | 
| 735 |  |  |  |  |  |  | # keep track of highest priority QuickTime tag | 
| 736 |  |  |  |  |  |  | $highestQT{$lcTag} = $priority if $$table{GROUPS}{0} eq 'QuickTime' and | 
| 737 | 32429 | 100 | 100 |  |  | 99590 | (not defined $highestQT{$lcTag} or $highestQT{$lcTag} < $priority); | 
|  |  |  | 100 |  |  |  |  | 
| 738 | 32429 | 100 |  |  |  | 87225 | if ($priority > $highestPriority{$lcTag}) { | 
|  |  | 100 |  |  |  |  |  | 
| 739 | 10286 |  |  |  |  | 17699 | $highestPriority{$lcTag} = $priority; | 
| 740 | 10286 |  |  |  |  | 36588 | $preferred{$lcTag} = { $tagInfo => 1 }; | 
| 741 | 10286 | 100 |  |  |  | 30802 | $avoid{$lcTag} = $$tagInfo{Avoid} ? 1 : 0; | 
| 742 |  |  |  |  |  |  | } elsif ($priority == $highestPriority{$lcTag}) { | 
| 743 |  |  |  |  |  |  | # create all tags with highest priority | 
| 744 | 13312 |  |  |  |  | 32544 | $preferred{$lcTag}{$tagInfo} = 1; | 
| 745 | 13312 | 100 |  |  |  | 32304 | ++$avoid{$lcTag} if $$tagInfo{Avoid}; | 
| 746 |  |  |  |  |  |  | } | 
| 747 | 32429 | 100 |  |  |  | 64068 | if ($$tagInfo{WriteAlso}) { | 
| 748 |  |  |  |  |  |  | # store WriteAlso tags separately so we can set them first | 
| 749 | 108 |  |  |  |  | 329 | push @writeAlsoList, $tagInfo; | 
| 750 |  |  |  |  |  |  | } else { | 
| 751 | 32321 |  |  |  |  | 58759 | push @tagInfoList, $tagInfo; | 
| 752 |  |  |  |  |  |  | } | 
| 753 |  |  |  |  |  |  | # special case to allow override of XMP WriteGroup | 
| 754 | 32429 | 100 |  |  |  | 64166 | if ($writeGroup eq 'XMP') { | 
| 755 | 5452 |  | 33 |  |  | 17980 | my $wg = $$tagInfo{WriteGroup} || $$table{WRITE_GROUP}; | 
| 756 | 5452 | 50 |  |  |  | 12111 | $writeGroup = $wg if $wg; | 
| 757 |  |  |  |  |  |  | } | 
| 758 | 32429 |  |  |  |  | 101733 | $writeGroup{$tagInfo} = $writeGroup; | 
| 759 |  |  |  |  |  |  | } | 
| 760 |  |  |  |  |  |  | # sort tag info list in reverse order of priority (highest number last) | 
| 761 |  |  |  |  |  |  | # so we get the highest priority error message in the end | 
| 762 | 4247 |  |  |  |  | 14446 | @tagInfoList = sort { $tagPriority{$a} <=> $tagPriority{$b} } @tagInfoList; | 
|  | 54114 |  |  |  |  | 108374 |  | 
| 763 |  |  |  |  |  |  | # must write any tags which also write other tags first | 
| 764 | 4247 | 100 |  |  |  | 11189 | unshift @tagInfoList, @writeAlsoList if @writeAlsoList; | 
| 765 |  |  |  |  |  |  |  | 
| 766 |  |  |  |  |  |  | # check priorities for each set of tags we are writing | 
| 767 | 4247 |  |  |  |  | 7448 | my $lcTag; | 
| 768 | 4247 |  |  |  |  | 14684 | foreach $lcTag (keys %preferred) { | 
| 769 |  |  |  |  |  |  | # don't create tags with priority 0 if group priorities are set | 
| 770 | 9495 | 100 | 66 |  |  | 44299 | if ($preferred{$lcTag} and $highestPriority{$lcTag} == 0 and | 
|  |  |  | 66 |  |  |  |  | 
| 771 | 9 |  |  |  |  | 50 | %{$$self{WRITE_PRIORITY}}) | 
| 772 |  |  |  |  |  |  | { | 
| 773 | 9 |  |  |  |  | 31 | delete $preferred{$lcTag} | 
| 774 |  |  |  |  |  |  | } | 
| 775 |  |  |  |  |  |  | # avoid creating tags with 'Avoid' flag set if there are other alternatives | 
| 776 | 9495 | 50 | 66 |  |  | 24670 | if ($avoid{$lcTag} and $preferred{$lcTag}) { | 
| 777 | 1362 | 100 |  |  |  | 2505 | if ($avoid{$lcTag} < scalar(keys %{$preferred{$lcTag}})) { | 
|  | 1362 | 100 |  |  |  | 8489 |  | 
| 778 |  |  |  |  |  |  | # just remove the 'Avoid' tags since there are other preferred tags | 
| 779 | 1226 |  |  |  |  | 3276 | foreach $tagInfo (@tagInfoList) { | 
| 780 | 4482573 | 100 |  |  |  | 8785437 | next unless $lcTag eq lc $$tagInfo{Name}; | 
| 781 | 5774 | 100 |  |  |  | 17149 | delete $preferred{$lcTag}{$tagInfo} if $$tagInfo{Avoid}; | 
| 782 |  |  |  |  |  |  | } | 
| 783 |  |  |  |  |  |  | } elsif ($highestPriority{$lcTag} < 1000) { | 
| 784 |  |  |  |  |  |  | # look for another priority tag to create instead | 
| 785 | 51 |  |  |  |  | 168 | my $nextHighest = 0; | 
| 786 | 51 |  |  |  |  | 106 | my @nextBestTags; | 
| 787 | 51 |  |  |  |  | 143 | foreach $tagInfo (@tagInfoList) { | 
| 788 | 10868 | 100 |  |  |  | 21679 | next unless $lcTag eq lc $$tagInfo{Name}; | 
| 789 | 212 | 100 |  |  |  | 540 | my $priority = $tagPriority{$tagInfo} or next; | 
| 790 | 211 | 100 |  |  |  | 521 | next if $priority == $highestPriority{$lcTag}; | 
| 791 | 159 | 50 |  |  |  | 336 | next if $priority < $nextHighest; | 
| 792 | 159 |  |  |  |  | 251 | my $permanent = $$tagInfo{Permanent}; | 
| 793 | 159 | 50 |  |  |  | 430 | $permanent = $$tagInfo{Table}{PERMANENT} unless defined $permanent; | 
| 794 | 159 | 100 | 66 |  |  | 553 | next if $$tagInfo{Avoid} or $permanent; | 
| 795 | 133 | 100 |  |  |  | 360 | next if $writeGroup{$tagInfo} eq 'MakerNotes'; | 
| 796 | 89 | 100 |  |  |  | 238 | if ($nextHighest < $priority) { | 
| 797 | 84 |  |  |  |  | 140 | $nextHighest = $priority; | 
| 798 | 84 |  |  |  |  | 171 | undef @nextBestTags; | 
| 799 |  |  |  |  |  |  | } | 
| 800 | 89 |  |  |  |  | 219 | push @nextBestTags, $tagInfo; | 
| 801 |  |  |  |  |  |  | } | 
| 802 | 51 | 100 |  |  |  | 211 | if (@nextBestTags) { | 
| 803 |  |  |  |  |  |  | # change our preferred tags to the next best tags | 
| 804 | 35 |  |  |  |  | 102 | delete $preferred{$lcTag}; | 
| 805 | 35 |  |  |  |  | 94 | foreach $tagInfo (@nextBestTags) { | 
| 806 | 36 |  |  |  |  | 223 | $preferred{$lcTag}{$tagInfo} = 1; | 
| 807 |  |  |  |  |  |  | } | 
| 808 |  |  |  |  |  |  | } | 
| 809 |  |  |  |  |  |  | } | 
| 810 |  |  |  |  |  |  | } | 
| 811 |  |  |  |  |  |  | } | 
| 812 |  |  |  |  |  |  | # | 
| 813 |  |  |  |  |  |  | # generate new value hash for each tag | 
| 814 |  |  |  |  |  |  | # | 
| 815 | 4247 |  |  |  |  | 8742 | my ($prioritySet, $createGroups, %alsoWrote); | 
| 816 |  |  |  |  |  |  |  | 
| 817 | 4247 |  |  |  |  | 8721 | delete $$self{CHECK_WARN};  # reset CHECK_PROC warnings | 
| 818 |  |  |  |  |  |  |  | 
| 819 |  |  |  |  |  |  | # loop through all valid tags to find the one(s) to write | 
| 820 | 4247 |  |  |  |  | 8318 | foreach $tagInfo (@tagInfoList) { | 
| 821 | 32417 | 100 |  |  |  | 90823 | next if $alsoWrote{$tagInfo};   # don't rewrite tags we already wrote | 
| 822 |  |  |  |  |  |  | # only process List or non-List tags if specified | 
| 823 | 32388 | 100 | 100 |  |  | 79656 | next if defined $listOnly and ($listOnly xor $$tagInfo{List}); | 
|  |  |  | 100 |  |  |  |  | 
| 824 | 32167 |  |  |  |  | 49200 | my $noConv; | 
| 825 | 32167 |  |  |  |  | 86587 | my $writeGroup = $writeGroup{$tagInfo}; | 
| 826 | 32167 |  |  |  |  | 63324 | my $permanent = $$tagInfo{Permanent}; | 
| 827 | 32167 | 100 |  |  |  | 100607 | $permanent = $$tagInfo{Table}{PERMANENT} unless defined $permanent; | 
| 828 | 32167 | 100 | 100 |  |  | 96111 | $writeGroup eq 'MakerNotes' and $permanent = 1 unless defined $permanent; | 
| 829 | 32167 |  |  |  |  | 87417 | my $wgrp1 = $self->GetWriteGroup1($tagInfo, $writeGroup); | 
| 830 | 32167 |  |  |  |  | 67996 | $tag = $$tagInfo{Name};     # get tag name for warnings | 
| 831 | 32167 |  |  |  |  | 60004 | my $lcTag = lc $tag; | 
| 832 | 32167 |  | 100 |  |  | 87467 | my $pref = $preferred{$lcTag} || { }; | 
| 833 | 32167 |  |  |  |  | 57778 | my $shift = $options{Shift}; | 
| 834 | 32167 |  |  |  |  | 52908 | my $addValue = $options{AddValue}; | 
| 835 | 32167 | 100 |  |  |  | 68305 | if (defined $shift) { | 
| 836 |  |  |  |  |  |  | # (can't currently shift list-type tags) | 
| 837 | 164 |  |  |  |  | 288 | my $shiftable; | 
| 838 | 164 | 50 |  |  |  | 360 | if ($$tagInfo{List}) { | 
| 839 | 0 |  |  |  |  | 0 | $shiftable = '';    # can add/delete but not shift | 
| 840 |  |  |  |  |  |  | } else { | 
| 841 | 164 |  |  |  |  | 300 | $shiftable = $$tagInfo{Shift}; | 
| 842 | 164 | 100 |  |  |  | 354 | unless ($shift) { | 
| 843 |  |  |  |  |  |  | # set shift according to AddValue/DelValue | 
| 844 | 24 | 50 |  |  |  | 60 | $shift = 1 if $addValue; | 
| 845 |  |  |  |  |  |  | # can shift a date/time with -=, but this is | 
| 846 |  |  |  |  |  |  | # a conditional delete operation for other tags | 
| 847 | 24 | 0 | 33 |  |  | 75 | $shift = -1 if $options{DelValue} and defined $shiftable and $shiftable eq 'Time'; | 
|  |  |  | 33 |  |  |  |  | 
| 848 |  |  |  |  |  |  | } | 
| 849 | 164 | 50 | 33 |  |  | 900 | if ($shift and (not defined $value or not length $value)) { | 
|  |  |  | 33 |  |  |  |  | 
| 850 |  |  |  |  |  |  | # (now allow -= to be used for shiftable tag - v8.05) | 
| 851 |  |  |  |  |  |  | #$err = "No value for time shift of $wgrp1:$tag"; | 
| 852 |  |  |  |  |  |  | #$verbose > 2 and print $out "$err\n"; | 
| 853 |  |  |  |  |  |  | #next; | 
| 854 | 0 |  |  |  |  | 0 | undef $shift; | 
| 855 |  |  |  |  |  |  | } | 
| 856 |  |  |  |  |  |  | } | 
| 857 |  |  |  |  |  |  | # can't shift List-type tag | 
| 858 | 164 | 0 | 66 |  |  | 532 | if ((defined $shiftable and not $shiftable) and | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 859 |  |  |  |  |  |  | # and don't try to conditionally delete if Shift is "0" | 
| 860 |  |  |  |  |  |  | ($shift or ($shiftable eq '0' and $options{DelValue}))) | 
| 861 |  |  |  |  |  |  | { | 
| 862 | 0 |  |  |  |  | 0 | $err = "$wgrp1:$tag is not shiftable"; | 
| 863 | 0 | 0 |  |  |  | 0 | $verbose and print $out "$err\n"; | 
| 864 | 0 |  |  |  |  | 0 | next; | 
| 865 |  |  |  |  |  |  | } | 
| 866 |  |  |  |  |  |  | } | 
| 867 | 32167 |  |  |  |  | 50910 | my $val = $value; | 
| 868 | 32167 | 100 | 33 |  |  | 76037 | if (defined $val) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 869 |  |  |  |  |  |  | # check to make sure this is a List or Shift tag if adding | 
| 870 | 21460 | 100 | 100 |  |  | 51664 | if ($addValue and not ($shift or $$tagInfo{List})) { | 
|  |  |  | 100 |  |  |  |  | 
| 871 | 9 | 50 |  |  |  | 41 | if ($addValue eq '2') { | 
| 872 | 0 |  |  |  |  | 0 | undef $addValue;    # quietly reset this option | 
| 873 |  |  |  |  |  |  | } else { | 
| 874 | 9 |  |  |  |  | 33 | $err = "Can't add $wgrp1:$tag (not a List type)"; | 
| 875 | 9 | 50 |  |  |  | 27 | $verbose > 2 and print $out "$err\n"; | 
| 876 | 9 |  |  |  |  | 30 | next; | 
| 877 |  |  |  |  |  |  | } | 
| 878 |  |  |  |  |  |  | } | 
| 879 | 21451 | 100 | 66 |  |  | 101376 | if ($shift) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 880 | 164 | 100 | 66 |  |  | 706 | if ($$tagInfo{Shift} and $$tagInfo{Shift} eq 'Time') { | 
|  |  | 100 |  |  |  |  |  | 
| 881 |  |  |  |  |  |  | # add '+' or '-' prefix to indicate shift direction | 
| 882 | 47 | 100 |  |  |  | 146 | $val = ($shift > 0 ? '+' : '-') . $val; | 
| 883 |  |  |  |  |  |  | # check the shift for validity | 
| 884 | 47 |  |  |  |  | 2231 | require 'Image/ExifTool/Shift.pl'; | 
| 885 | 47 |  |  |  |  | 175 | my $err2 = CheckShift($$tagInfo{Shift}, $val); | 
| 886 | 47 | 50 |  |  |  | 111 | if ($err2) { | 
| 887 | 0 |  |  |  |  | 0 | $err = "$err2 for $wgrp1:$tag"; | 
| 888 | 0 | 0 |  |  |  | 0 | $verbose > 2 and print $out "$err\n"; | 
| 889 | 0 |  |  |  |  | 0 | next; | 
| 890 |  |  |  |  |  |  | } | 
| 891 |  |  |  |  |  |  | } elsif (IsFloat($val)) { | 
| 892 | 113 |  |  |  |  | 343 | $val *= $shift; | 
| 893 |  |  |  |  |  |  | } else { | 
| 894 | 4 |  |  |  |  | 21 | $err = "Shift value for $wgrp1:$tag is not a number"; | 
| 895 | 4 | 50 |  |  |  | 13 | $verbose > 2 and print $out "$err\n"; | 
| 896 | 4 |  |  |  |  | 16 | next; | 
| 897 |  |  |  |  |  |  | } | 
| 898 | 160 |  |  |  |  | 313 | $noConv = 1;    # no conversions if shifting tag | 
| 899 |  |  |  |  |  |  | } elsif (not length $val and $options{DelValue}) { | 
| 900 | 35 |  |  |  |  | 58 | $noConv = 1;    # no conversions for deleting empty value | 
| 901 |  |  |  |  |  |  | } elsif (ref $val eq 'HASH' and not $$tagInfo{Struct}) { | 
| 902 | 2 |  |  |  |  | 11 | $err = "Can't write a structure to $wgrp1:$tag"; | 
| 903 | 2 | 50 |  |  |  | 9 | $verbose > 2 and print $out "$err\n"; | 
| 904 | 2 |  |  |  |  | 7 | next; | 
| 905 |  |  |  |  |  |  | } | 
| 906 |  |  |  |  |  |  | } elsif ($permanent) { | 
| 907 | 6674 | 100 |  |  |  | 14975 | return 0 if $options{IgnorePermanent}; | 
| 908 |  |  |  |  |  |  | # can't delete permanent tags, so set them to DelValue or empty string instead | 
| 909 | 6670 | 100 |  |  |  | 14954 | if (defined $$tagInfo{DelValue}) { | 
| 910 | 33 |  |  |  |  | 135 | $val = $$tagInfo{DelValue}; | 
| 911 | 33 |  |  |  |  | 105 | $noConv = 1;    # DelValue is the raw value, so no conversion necessary | 
| 912 |  |  |  |  |  |  | } else { | 
| 913 | 6637 |  |  |  |  | 10407 | $val = ''; | 
| 914 |  |  |  |  |  |  | } | 
| 915 |  |  |  |  |  |  | } elsif ($addValue or $options{DelValue}) { | 
| 916 | 0 |  |  |  |  | 0 | $err = "No value to add or delete in $wgrp1:$tag"; | 
| 917 | 0 | 0 |  |  |  | 0 | $verbose > 2 and print $out "$err\n"; | 
| 918 | 0 |  |  |  |  | 0 | next; | 
| 919 |  |  |  |  |  |  | } else { | 
| 920 | 4033 | 100 |  |  |  | 10899 | if ($$tagInfo{DelCheck}) { | 
| 921 |  |  |  |  |  |  | #### eval DelCheck ($self, $tagInfo, $wantGroup) | 
| 922 | 6 |  |  |  |  | 604 | my $err2 = eval $$tagInfo{DelCheck}; | 
| 923 | 6 | 50 |  |  |  | 50 | $@ and warn($@), $err2 = 'Error evaluating DelCheck'; | 
| 924 | 6 | 50 |  |  |  | 27 | if (defined $err2) { | 
| 925 |  |  |  |  |  |  | # (allow other tags to be set using DelCheck as a hook) | 
| 926 | 6 | 100 |  |  |  | 110 | $err2 or goto WriteAlso; # GOTO! | 
| 927 | 3 | 50 |  |  |  | 20 | $err2 .= ' for' unless $err2 =~ /delete$/; | 
| 928 | 3 |  |  |  |  | 15 | $err = "$err2 $wgrp1:$tag"; | 
| 929 | 3 | 50 |  |  |  | 13 | $verbose > 2 and print $out "$err\n"; | 
| 930 | 3 |  |  |  |  | 13 | next; | 
| 931 |  |  |  |  |  |  | } | 
| 932 |  |  |  |  |  |  | } | 
| 933 |  |  |  |  |  |  | # set group delete flag if this tag represents an entire group | 
| 934 | 4027 | 100 | 66 |  |  | 10089 | if ($$tagInfo{DelGroup} and not $options{DelValue}) { | 
| 935 | 3 |  |  |  |  | 21 | my @del = ( $tag ); | 
| 936 | 3 |  |  |  |  | 12 | $$self{DEL_GROUP}{$tag} = 1; | 
| 937 |  |  |  |  |  |  | # delete extra groups if necessary | 
| 938 | 3 | 50 |  |  |  | 13 | if ($delMore{$tag}) { | 
| 939 | 0 |  |  |  |  | 0 | $$self{DEL_GROUP}{$_} = 1, push(@del,$_) foreach @{$delMore{$tag}}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 940 |  |  |  |  |  |  | } | 
| 941 |  |  |  |  |  |  | # remove all of this group from previous new values | 
| 942 | 3 |  |  |  |  | 19 | $self->RemoveNewValuesForGroup($tag); | 
| 943 | 3 | 50 |  |  |  | 10 | $verbose and print $out "  Deleting tags in: @del\n"; | 
| 944 | 3 |  |  |  |  | 7 | ++$numSet; | 
| 945 | 3 |  |  |  |  | 12 | next; | 
| 946 |  |  |  |  |  |  | } | 
| 947 | 4024 |  |  |  |  | 5902 | $noConv = 1;    # value is not defined, so don't do conversion | 
| 948 |  |  |  |  |  |  | } | 
| 949 |  |  |  |  |  |  | # apply inverse PrintConv and ValueConv conversions | 
| 950 |  |  |  |  |  |  | # save ValueConv setting for use in ConvInv() | 
| 951 | 32139 | 100 |  |  |  | 64460 | unless ($noConv) { | 
| 952 |  |  |  |  |  |  | # set default conversion type used by ConvInv() and CHECK_PROC routines | 
| 953 | 27887 |  |  |  |  | 56482 | $$self{ConvType} = $convType; | 
| 954 | 27887 |  |  |  |  | 42314 | my $e; | 
| 955 | 27887 |  |  |  |  | 78437 | ($val,$e) = $self->ConvInv($val,$tagInfo,$tag,$wgrp1,$$self{ConvType},$wantGroup); | 
| 956 | 27887 | 100 |  |  |  | 68405 | if (defined $e) { | 
| 957 |  |  |  |  |  |  | # empty error string causes error to be ignored without setting the value | 
| 958 | 8332 | 100 |  |  |  | 20235 | $e or goto WriteAlso; # GOTO! | 
| 959 | 8315 |  |  |  |  | 15210 | $err = $e; | 
| 960 |  |  |  |  |  |  | } | 
| 961 |  |  |  |  |  |  | } | 
| 962 | 32122 | 100 | 100 |  |  | 91054 | if (not defined $val and defined $value) { | 
| 963 |  |  |  |  |  |  | # if value conversion failed, we must still add a NEW_VALUE | 
| 964 |  |  |  |  |  |  | # entry for this tag it it was a DelValue | 
| 965 | 2778 | 50 |  |  |  | 10817 | next unless $options{DelValue}; | 
| 966 | 0 |  |  |  |  | 0 | $val = 'xxx never delete xxx'; | 
| 967 |  |  |  |  |  |  | } | 
| 968 | 29344 | 100 |  |  |  | 78655 | $$self{NEW_VALUE} or $$self{NEW_VALUE} = { }; | 
| 969 | 29344 | 100 |  |  |  | 76412 | if ($options{Replace}) { | 
| 970 |  |  |  |  |  |  | # delete the previous new value | 
| 971 | 14183 |  |  |  |  | 61614 | $self->GetNewValueHash($tagInfo, $writeGroup, 'delete', $options{ProtectSaved}); | 
| 972 |  |  |  |  |  |  | # also delete related tag previous new values | 
| 973 | 14183 | 100 |  |  |  | 40126 | if ($$tagInfo{WriteAlso}) { | 
| 974 | 27 |  |  |  |  | 108 | my ($wgrp, $wtag); | 
| 975 | 27 | 100 | 66 |  |  | 215 | if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) { | 
|  |  |  | 66 |  |  |  |  | 
| 976 | 6 |  |  |  |  | 20 | $wgrp = $writeGroup . ':'; | 
| 977 |  |  |  |  |  |  | } else { | 
| 978 | 21 |  |  |  |  | 84 | $wgrp = ''; | 
| 979 |  |  |  |  |  |  | } | 
| 980 | 27 |  |  |  |  | 68 | foreach $wtag (sort keys %{$$tagInfo{WriteAlso}}) { | 
|  | 27 |  |  |  |  | 201 |  | 
| 981 | 95 |  |  |  |  | 500 | my ($n,$e) = $self->SetNewValue($wgrp . $wtag, undef, Replace=>2); | 
| 982 | 95 |  |  |  |  | 263 | $numSet += $n; | 
| 983 |  |  |  |  |  |  | } | 
| 984 |  |  |  |  |  |  | } | 
| 985 | 14183 | 100 |  |  |  | 33639 | $options{Replace} == 2 and ++$numSet, next; | 
| 986 |  |  |  |  |  |  | } | 
| 987 |  |  |  |  |  |  |  | 
| 988 | 29064 | 100 | 33 |  |  | 69214 | if (defined $val) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 989 |  |  |  |  |  |  | # we are editing this tag, so create a NEW_VALUE hash entry | 
| 990 |  |  |  |  |  |  | my $nvHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create', | 
| 991 | 19713 |  | 66 |  |  | 75940 | $options{ProtectSaved}, ($options{DelValue} and not $shift)); | 
| 992 |  |  |  |  |  |  | # ignore new values protected with ProtectSaved | 
| 993 | 19713 | 50 |  |  |  | 50115 | $nvHash or ++$numSet, next; # (increment $numSet to avoid warning) | 
| 994 | 19713 | 100 | 100 |  |  | 51603 | $$nvHash{NoReplace} = 1 if $$tagInfo{List} and not $options{Replace}; | 
| 995 | 19713 |  |  |  |  | 39994 | $$nvHash{WantGroup} = $wantGroup; | 
| 996 | 19713 | 100 |  |  |  | 40036 | $$nvHash{EditOnly} = 1 if $editOnly; | 
| 997 |  |  |  |  |  |  | # save maker note information if writing maker notes | 
| 998 | 19713 | 100 |  |  |  | 45506 | if ($$tagInfo{MakerNotes}) { | 
| 999 | 22 |  |  |  |  | 111 | $$nvHash{MAKER_NOTE_FIXUP} = $$self{MAKER_NOTE_FIXUP}; | 
| 1000 |  |  |  |  |  |  | } | 
| 1001 | 19713 | 100 | 100 |  |  | 99298 | if ($createOnly) {  # create only (never edit) | 
|  |  | 100 | 100 |  |  |  |  | 
| 1002 |  |  |  |  |  |  | # empty item in DelValue list to never edit existing value | 
| 1003 | 46 |  |  |  |  | 126 | $$nvHash{DelValue} = [ '' ]; | 
| 1004 | 46 |  |  |  |  | 168 | $$nvHash{CreateOnly} = 1; | 
| 1005 |  |  |  |  |  |  | } elsif ($options{DelValue} or $addValue or $shift) { | 
| 1006 |  |  |  |  |  |  | # flag any AddValue or DelValue by creating the DelValue list | 
| 1007 | 227 | 100 |  |  |  | 742 | $$nvHash{DelValue} or $$nvHash{DelValue} = [ ]; | 
| 1008 | 227 | 100 |  |  |  | 530 | if ($shift) { | 
|  |  | 100 |  |  |  |  |  | 
| 1009 |  |  |  |  |  |  | # add shift value to list | 
| 1010 | 160 |  |  |  |  | 376 | $$nvHash{Shift} = $val; | 
| 1011 |  |  |  |  |  |  | } elsif ($options{DelValue}) { | 
| 1012 |  |  |  |  |  |  | # don't create if we are replacing a specific value | 
| 1013 | 54 | 100 | 100 |  |  | 228 | $$nvHash{IsCreating} = 0 unless $val eq '' or $$tagInfo{List}; | 
| 1014 |  |  |  |  |  |  | # add delete value to list | 
| 1015 | 54 | 100 |  |  |  | 92 | push @{$$nvHash{DelValue}}, ref $val eq 'ARRAY' ? @$val : $val; | 
|  | 54 |  |  |  |  | 208 |  | 
| 1016 | 54 | 50 |  |  |  | 174 | if ($verbose > 1) { | 
| 1017 | 0 | 0 |  |  |  | 0 | my $verb = $permanent ? 'Replacing' : 'Deleting'; | 
| 1018 | 0 | 0 |  |  |  | 0 | my $fromList = $$tagInfo{List} ? ' from list' : ''; | 
| 1019 | 0 | 0 |  |  |  | 0 | my @vals = (ref $val eq 'ARRAY' ? @$val : $val); | 
| 1020 | 0 |  |  |  |  | 0 | foreach (@vals) { | 
| 1021 | 0 | 0 |  |  |  | 0 | if (ref $_ eq 'HASH') { | 
| 1022 | 0 |  |  |  |  | 0 | require 'Image/ExifTool/XMPStruct.pl'; | 
| 1023 | 0 |  |  |  |  | 0 | $_ = Image::ExifTool::XMP::SerializeStruct($_); | 
| 1024 |  |  |  |  |  |  | } | 
| 1025 | 0 |  |  |  |  | 0 | print $out "$verb $wgrp1:$tag$fromList if value is '${_}'\n"; | 
| 1026 |  |  |  |  |  |  | } | 
| 1027 |  |  |  |  |  |  | } | 
| 1028 |  |  |  |  |  |  | } | 
| 1029 |  |  |  |  |  |  | } | 
| 1030 |  |  |  |  |  |  | # set priority flag to add only the high priority info | 
| 1031 |  |  |  |  |  |  | # (will only create the priority tag if it doesn't exist, | 
| 1032 |  |  |  |  |  |  | #  others get changed only if they already exist) | 
| 1033 | 19713 | 100 |  |  |  | 53374 | my $prf = defined $$tagInfo{Preferred} ? $$tagInfo{Preferred} : $$tagInfo{Table}{PREFERRED}; | 
| 1034 |  |  |  |  |  |  | # hack to prefer only a single tag in the QuickTime group | 
| 1035 | 19713 | 100 |  |  |  | 56122 | if ($$tagInfo{Table}{GROUPS}{0} eq 'QuickTime') { | 
| 1036 | 657 | 100 |  |  |  | 2791 | $prf = 0 if $tagPriority{$tagInfo} < $highestQT{$lcTag}; | 
| 1037 |  |  |  |  |  |  | } | 
| 1038 | 19713 | 100 | 100 |  |  | 67703 | if ($$pref{$tagInfo} or $prf) { | 
| 1039 | 9130 | 100 | 100 |  |  | 43628 | if ($permanent or $shift) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 1040 |  |  |  |  |  |  | # don't create permanent or Shift-ed tag but define IsCreating | 
| 1041 |  |  |  |  |  |  | # so we know that it is the preferred tag | 
| 1042 | 5374 |  |  |  |  | 12439 | $$nvHash{IsCreating} = 0; | 
| 1043 |  |  |  |  |  |  | } elsif (($$tagInfo{List} and not $options{DelValue}) or | 
| 1044 |  |  |  |  |  |  | not ($$nvHash{DelValue} and @{$$nvHash{DelValue}}) or | 
| 1045 |  |  |  |  |  |  | # also create tag if any DelValue value is empty ('') | 
| 1046 | 58 |  |  |  |  | 398 | grep(/^$/,@{$$nvHash{DelValue}})) | 
| 1047 |  |  |  |  |  |  | { | 
| 1048 | 3742 | 100 |  |  |  | 12083 | $$nvHash{IsCreating} = $editOnly ? 0 : ($editGroup ? 2 : 1); | 
|  |  | 100 |  |  |  |  |  | 
| 1049 |  |  |  |  |  |  | # add to hash of groups where this tag is being created | 
| 1050 | 3742 | 100 | 100 |  |  | 13727 | $createGroups or $createGroups = $options{CreateGroups} || { }; | 
| 1051 | 3742 |  |  |  |  | 14647 | $$createGroups{$self->GetGroup($tagInfo, 0)} = 1; | 
| 1052 | 3742 |  |  |  |  | 10315 | $$nvHash{CreateGroups} = $createGroups; | 
| 1053 |  |  |  |  |  |  | } | 
| 1054 |  |  |  |  |  |  | } | 
| 1055 | 19713 | 100 |  |  |  | 53590 | if ($$nvHash{IsCreating}) { | 
|  |  | 100 |  |  |  |  |  | 
| 1056 | 3732 | 100 |  |  |  | 5668 | if (%{$$self{DEL_GROUP}}) { | 
|  | 3732 |  |  |  |  | 11656 |  | 
| 1057 | 227 |  |  |  |  | 460 | my ($grp, @grps); | 
| 1058 | 227 |  |  |  |  | 382 | foreach $grp (keys %{$$self{DEL_GROUP}}) { | 
|  | 227 |  |  |  |  | 3013 |  | 
| 1059 | 12589 | 100 |  |  |  | 23756 | next if $$self{DEL_GROUP}{$grp} == 2; | 
| 1060 |  |  |  |  |  |  | # set flag indicating tags were written after this group was deleted | 
| 1061 | 354 |  |  |  |  | 490 | $$self{DEL_GROUP}{$grp} = 2; | 
| 1062 | 354 |  |  |  |  | 579 | push @grps, $grp; | 
| 1063 |  |  |  |  |  |  | } | 
| 1064 | 227 | 100 | 66 |  |  | 1161 | if ($verbose > 1 and @grps) { | 
| 1065 | 1 |  |  |  |  | 5 | @grps = sort @grps; | 
| 1066 | 1 |  |  |  |  | 11 | print $out "  Writing new tags after deleting groups: @grps\n"; | 
| 1067 |  |  |  |  |  |  | } | 
| 1068 |  |  |  |  |  |  | } | 
| 1069 |  |  |  |  |  |  | } elsif ($createOnly) { | 
| 1070 | 19 | 100 |  |  |  | 80 | $noCreate = $permanent ? 'permanent' : ($$tagInfo{Avoid} ? 'avoided' : ''); | 
|  |  | 100 |  |  |  |  |  | 
| 1071 | 19 | 50 |  |  |  | 58 | $noCreate or $noCreate = $shift ? 'shifting' : 'not preferred'; | 
|  |  | 100 |  |  |  |  |  | 
| 1072 | 19 | 50 |  |  |  | 54 | $verbose > 2 and print $out "Not creating $wgrp1:$tag ($noCreate)\n"; | 
| 1073 | 19 |  |  |  |  | 67 | next;   # nothing to do (not creating and not editing) | 
| 1074 |  |  |  |  |  |  | } | 
| 1075 | 19694 | 100 | 100 |  |  | 68441 | if ($shift or not $options{DelValue}) { | 
| 1076 | 19640 | 100 |  |  |  | 60878 | $$nvHash{Value} or $$nvHash{Value} = [ ]; | 
| 1077 | 19640 | 100 | 33 |  |  | 45007 | if (not $$tagInfo{List}) { | 
|  |  | 50 |  |  |  |  |  | 
| 1078 |  |  |  |  |  |  | # not a List tag -- overwrite existing value | 
| 1079 | 19135 |  |  |  |  | 44951 | $$nvHash{Value}[0] = $val; | 
| 1080 | 0 |  |  |  |  | 0 | } elsif (defined $$nvHash{AddBefore} and @{$$nvHash{Value}} >= $$nvHash{AddBefore}) { | 
| 1081 |  |  |  |  |  |  | # values from a later argument have been added (ie. Replace=0) | 
| 1082 |  |  |  |  |  |  | # to this list, so the new values should come before these | 
| 1083 | 0 | 0 |  |  |  | 0 | splice @{$$nvHash{Value}}, -$$nvHash{AddBefore}, 0, ref $val eq 'ARRAY' ? @$val : $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1084 |  |  |  |  |  |  | } else { | 
| 1085 |  |  |  |  |  |  | # add at end of existing list | 
| 1086 | 505 | 100 |  |  |  | 872 | push @{$$nvHash{Value}}, ref $val eq 'ARRAY' ? @$val : $val; | 
|  | 505 |  |  |  |  | 1900 |  | 
| 1087 |  |  |  |  |  |  | } | 
| 1088 | 19640 | 100 |  |  |  | 48490 | if ($verbose > 1) { | 
| 1089 |  |  |  |  |  |  | my $ifExists = $$nvHash{IsCreating} ? ( $createOnly ? | 
| 1090 |  |  |  |  |  |  | ($$nvHash{IsCreating} == 2 ? | 
| 1091 |  |  |  |  |  |  | " if $writeGroup exists and tag doesn't" : | 
| 1092 |  |  |  |  |  |  | " if tag doesn't exist") : | 
| 1093 |  |  |  |  |  |  | ($$nvHash{IsCreating} == 2 ? " if $writeGroup exists" : '')) : | 
| 1094 | 26 | 0 | 33 |  |  | 110 | (($$nvHash{DelValue} and @{$$nvHash{DelValue}}) ? | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 1095 |  |  |  |  |  |  | ' if tag was deleted' : ' if tag exists'); | 
| 1096 | 26 | 50 |  |  |  | 68 | my $verb = ($shift ? 'Shifting' : ($addValue ? 'Adding' : 'Writing')); | 
|  |  | 50 |  |  |  |  |  | 
| 1097 | 26 |  |  |  |  | 119 | print $out "$verb $wgrp1:$tag$ifExists\n"; | 
| 1098 |  |  |  |  |  |  | } | 
| 1099 |  |  |  |  |  |  | } | 
| 1100 |  |  |  |  |  |  | } elsif ($permanent) { | 
| 1101 | 5439 |  |  |  |  | 12290 | $err = "Can't delete Permanent tag $wgrp1:$tag"; | 
| 1102 | 5439 | 50 |  |  |  | 10830 | $verbose > 1 and print $out "$err\n"; | 
| 1103 | 5439 |  |  |  |  | 15979 | next; | 
| 1104 |  |  |  |  |  |  | } elsif ($addValue or $options{DelValue}) { | 
| 1105 | 0 | 0 |  |  |  | 0 | $verbose > 1 and print $out "Adding/Deleting nothing does nothing\n"; | 
| 1106 | 0 |  |  |  |  | 0 | next; | 
| 1107 |  |  |  |  |  |  | } else { | 
| 1108 |  |  |  |  |  |  | # create empty new value hash entry to delete this tag | 
| 1109 | 3912 |  |  |  |  | 11522 | $self->GetNewValueHash($tagInfo, $writeGroup, 'delete'); | 
| 1110 | 3912 |  |  |  |  | 8701 | my $nvHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create'); | 
| 1111 | 3912 |  |  |  |  | 8876 | $$nvHash{WantGroup} = $wantGroup; | 
| 1112 | 3912 | 50 |  |  |  | 10610 | $verbose > 1 and print $out "Deleting $wgrp1:$tag\n"; | 
| 1113 |  |  |  |  |  |  | } | 
| 1114 | 23606 | 100 |  |  |  | 46788 | $$setTags{$tagInfo} = 1 if $setTags; | 
| 1115 | 23606 | 100 |  |  |  | 61633 | $prioritySet = 1 if $$pref{$tagInfo}; | 
| 1116 | 23626 |  |  |  |  | 37111 | WriteAlso: | 
| 1117 |  |  |  |  |  |  | ++$numSet; | 
| 1118 |  |  |  |  |  |  | # also write related tags | 
| 1119 | 23626 |  |  |  |  | 40555 | my $writeAlso = $$tagInfo{WriteAlso}; | 
| 1120 | 23626 | 100 |  |  |  | 73090 | if ($writeAlso) { | 
| 1121 | 93 |  |  |  |  | 298 | my ($wgrp, $wtag, $n); | 
| 1122 | 93 | 100 | 66 |  |  | 805 | if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) { | 
|  |  |  | 66 |  |  |  |  | 
| 1123 | 43 |  |  |  |  | 143 | $wgrp = $writeGroup . ':'; | 
| 1124 |  |  |  |  |  |  | } else { | 
| 1125 | 50 |  |  |  |  | 120 | $wgrp = ''; | 
| 1126 |  |  |  |  |  |  | } | 
| 1127 | 93 |  |  |  |  | 592 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 1128 | 93 |  |  |  |  | 697 | foreach $wtag (sort keys %$writeAlso) { | 
| 1129 |  |  |  |  |  |  | my %opts = ( | 
| 1130 |  |  |  |  |  |  | Type => 'ValueConv', | 
| 1131 |  |  |  |  |  |  | Protected   => $protected | 0x02, | 
| 1132 |  |  |  |  |  |  | AddValue    => $addValue, | 
| 1133 |  |  |  |  |  |  | DelValue    => $options{DelValue}, | 
| 1134 |  |  |  |  |  |  | Shift       => $options{Shift}, | 
| 1135 |  |  |  |  |  |  | Replace     => $options{Replace},   # handle lists properly | 
| 1136 | 274 |  |  |  |  | 2249 | CreateGroups=> $createGroups, | 
| 1137 |  |  |  |  |  |  | SetTags     => \%alsoWrote,         # remember tags already written | 
| 1138 |  |  |  |  |  |  | ); | 
| 1139 | 274 |  |  |  |  | 591 | undef $evalWarning; | 
| 1140 |  |  |  |  |  |  | #### eval WriteAlso ($val) | 
| 1141 | 274 |  |  |  |  | 21414 | my $v = eval $$writeAlso{$wtag}; | 
| 1142 |  |  |  |  |  |  | # we wanted to do the eval in case there are side effect, but we | 
| 1143 |  |  |  |  |  |  | # don't want to write a value for a tag that is being deleted: | 
| 1144 | 274 | 100 |  |  |  | 1456 | undef $v unless defined $val; | 
| 1145 | 274 | 50 |  |  |  | 820 | $@ and $evalWarning = $@; | 
| 1146 | 274 | 50 |  |  |  | 746 | unless ($evalWarning) { | 
| 1147 | 274 |  |  |  |  | 2198 | ($n,$evalWarning) = $self->SetNewValue($wgrp . $wtag, $v, %opts); | 
| 1148 | 274 |  |  |  |  | 871 | $numSet += $n; | 
| 1149 |  |  |  |  |  |  | # count this as being set if any related tag is set | 
| 1150 | 274 | 100 | 100 |  |  | 1529 | $prioritySet = 1 if $n and $$pref{$tagInfo}; | 
| 1151 |  |  |  |  |  |  | } | 
| 1152 | 274 | 100 | 66 |  |  | 1592 | if ($evalWarning and (not $err or $verbose > 2)) { | 
|  |  |  | 66 |  |  |  |  | 
| 1153 | 9 |  |  |  |  | 44 | my $str = CleanWarning(); | 
| 1154 | 9 | 50 |  |  |  | 34 | if ($str) { | 
| 1155 | 9 | 50 |  |  |  | 75 | $str .= " for $wtag" unless $str =~ / for [-\w:]+$/; | 
| 1156 | 9 |  |  |  |  | 45 | $str .= " in $wgrp1:$tag (WriteAlso)"; | 
| 1157 | 9 | 50 |  |  |  | 43 | $err or $err = $str; | 
| 1158 | 9 | 50 |  |  |  | 93 | print $out "$str\n" if $verbose > 2; | 
| 1159 |  |  |  |  |  |  | } | 
| 1160 |  |  |  |  |  |  | } | 
| 1161 |  |  |  |  |  |  | } | 
| 1162 |  |  |  |  |  |  | } | 
| 1163 |  |  |  |  |  |  | } | 
| 1164 |  |  |  |  |  |  | # print warning if we couldn't set our priority tag | 
| 1165 | 4243 | 100 | 100 |  |  | 24800 | if (defined $err and not $prioritySet) { | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 1166 | 85 | 50 | 33 |  |  | 568 | warn "$err\n" if $err and not wantarray; | 
| 1167 |  |  |  |  |  |  | } elsif (not $numSet) { | 
| 1168 | 619 | 100 |  |  |  | 2199 | my $pre = $wantGroup ? $wantGroup . ':' : ''; | 
| 1169 | 619 | 100 |  |  |  | 1834 | if ($wasProtected) { | 
|  |  | 100 |  |  |  |  |  | 
| 1170 | 372 |  |  |  |  | 684 | $verbose = 0;   # we already printed this verbose message | 
| 1171 | 372 | 100 | 100 |  |  | 1962 | unless ($options{Replace} and $options{Replace} == 2) { | 
| 1172 | 360 |  |  |  |  | 1370 | $err = "Sorry, $pre$tag is $wasProtected for writing"; | 
| 1173 |  |  |  |  |  |  | } | 
| 1174 |  |  |  |  |  |  | } elsif (not $listOnly) { | 
| 1175 | 240 | 50 | 33 |  |  | 1574 | if ($origTag =~ /[?*]/) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 1176 | 0 | 0 |  |  |  | 0 | if ($noCreate) { | 
|  |  | 0 |  |  |  |  |  | 
| 1177 | 0 |  |  |  |  | 0 | $err = "No tags matching 'pre${origTag}' will be created"; | 
| 1178 | 0 |  |  |  |  | 0 | $verbose = 0;   # (already printed) | 
| 1179 |  |  |  |  |  |  | } elsif ($foundMatch) { | 
| 1180 | 0 |  |  |  |  | 0 | $err = "Sorry, no writable tags matching '$pre${origTag}'"; | 
| 1181 |  |  |  |  |  |  | } else { | 
| 1182 | 0 |  |  |  |  | 0 | $err = "No matching tags for '$pre${origTag}'"; | 
| 1183 |  |  |  |  |  |  | } | 
| 1184 |  |  |  |  |  |  | } elsif ($noCreate) { | 
| 1185 | 0 |  |  |  |  | 0 | $err = "Not creating $pre$tag"; | 
| 1186 | 0 |  |  |  |  | 0 | $verbose = 0;   # (already printed) | 
| 1187 |  |  |  |  |  |  | } elsif ($foundMatch) { | 
| 1188 | 0 |  |  |  |  | 0 | $err = "Sorry, $pre$tag is not writable"; | 
| 1189 |  |  |  |  |  |  | } elsif ($wantGroup and @matchingTags) { | 
| 1190 | 240 |  |  |  |  | 768 | $err = "Sorry, $pre$tag doesn't exist or isn't writable"; | 
| 1191 |  |  |  |  |  |  | } else { | 
| 1192 | 0 |  |  |  |  | 0 | $err = "Tag '$pre${tag}' is not defined"; | 
| 1193 |  |  |  |  |  |  | } | 
| 1194 |  |  |  |  |  |  | } | 
| 1195 | 619 | 100 |  |  |  | 1607 | if ($err) { | 
| 1196 | 600 | 50 |  |  |  | 1438 | $verbose > 2 and print $out "$err\n"; | 
| 1197 | 600 | 50 |  |  |  | 1613 | warn "$err\n" unless wantarray; | 
| 1198 |  |  |  |  |  |  | } | 
| 1199 |  |  |  |  |  |  | } elsif ($$self{CHECK_WARN}) { | 
| 1200 | 0 |  |  |  |  | 0 | $err = $$self{CHECK_WARN}; | 
| 1201 | 0 | 0 |  |  |  | 0 | $verbose > 2 and print $out "$err\n"; | 
| 1202 |  |  |  |  |  |  | } elsif ($err and not $verbose) { | 
| 1203 | 434 |  |  |  |  | 1055 | undef $err; | 
| 1204 |  |  |  |  |  |  | } | 
| 1205 | 4243 | 100 |  |  |  | 42012 | return ($numSet, $err) if wantarray; | 
| 1206 | 419 |  |  |  |  | 31137 | return $numSet; | 
| 1207 |  |  |  |  |  |  | } | 
| 1208 |  |  |  |  |  |  |  | 
| 1209 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1210 |  |  |  |  |  |  | # set new values from information in specified file | 
| 1211 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) source file name or reference, etc | 
| 1212 |  |  |  |  |  |  | #         2-N) List of tags to set (or all if none specified), or reference(s) to | 
| 1213 |  |  |  |  |  |  | #         hash for options to pass to SetNewValue.  The Replace option defaults | 
| 1214 |  |  |  |  |  |  | #         to 1 for SetNewValuesFromFile -- set this to 0 to allow multiple tags | 
| 1215 |  |  |  |  |  |  | #         to be copied to a list | 
| 1216 |  |  |  |  |  |  | # Returns: Hash of information set successfully (includes Warning or Error messages) | 
| 1217 |  |  |  |  |  |  | # Notes: Tag names may contain a group prefix, a leading '-' to exclude from copy, | 
| 1218 |  |  |  |  |  |  | #        and/or a trailing '#' to copy the ValueConv value.  The tag name '*' may | 
| 1219 |  |  |  |  |  |  | #        be used to represent all tags in a group.  An optional destination tag | 
| 1220 |  |  |  |  |  |  | #        may be specified with '>DSTTAG' ('DSTTAG | 
| 1221 |  |  |  |  |  |  | #        case the source tag may also be an expression involving tag names). | 
| 1222 |  |  |  |  |  |  | sub SetNewValuesFromFile($$;@) | 
| 1223 |  |  |  |  |  |  | { | 
| 1224 | 58 |  |  | 58 | 1 | 1234 | local $_; | 
| 1225 | 58 |  |  |  |  | 266 | my ($self, $srcFile, @setTags) = @_; | 
| 1226 | 58 |  |  |  |  | 176 | my ($key, $tag, @exclude, @reqTags); | 
| 1227 |  |  |  |  |  |  |  | 
| 1228 |  |  |  |  |  |  | # get initial SetNewValuesFromFile options | 
| 1229 | 58 |  |  |  |  | 269 | my %opts = ( Replace => 1 );    # replace existing list items by default | 
| 1230 | 58 |  |  |  |  | 384 | while (ref $setTags[0] eq 'HASH') { | 
| 1231 | 1 |  |  |  |  | 5 | $_ = shift @setTags; | 
| 1232 | 1 |  |  |  |  | 9 | foreach $key (keys %$_) { | 
| 1233 | 1 |  |  |  |  | 18 | $opts{$key} = $$_{$key}; | 
| 1234 |  |  |  |  |  |  | } | 
| 1235 |  |  |  |  |  |  | } | 
| 1236 |  |  |  |  |  |  | # expand shortcuts | 
| 1237 | 58 | 100 |  |  |  | 439 | @setTags and ExpandShortcuts(\@setTags); | 
| 1238 | 58 |  |  |  |  | 384 | my $srcExifTool = new Image::ExifTool; | 
| 1239 |  |  |  |  |  |  | # set flag to indicate we are being called from inside SetNewValuesFromFile() | 
| 1240 | 58 |  |  |  |  | 291 | $$srcExifTool{TAGS_FROM_FILE} = 1; | 
| 1241 |  |  |  |  |  |  | # synchronize and increment the file sequence number | 
| 1242 | 58 |  |  |  |  | 301 | $$srcExifTool{FILE_SEQUENCE} = $$self{FILE_SEQUENCE}++; | 
| 1243 |  |  |  |  |  |  | # set options for our extraction tool | 
| 1244 | 58 |  |  |  |  | 185 | my $options = $$self{OPTIONS}; | 
| 1245 |  |  |  |  |  |  | # copy both structured and flattened tags by default (but flattened tags are "unsafe") | 
| 1246 | 58 | 50 |  |  |  | 283 | my $structOpt = defined $$options{Struct} ? $$options{Struct} : 2; | 
| 1247 |  |  |  |  |  |  | # copy structures only if no tags specified (since flattened tags are "unsafe") | 
| 1248 | 58 | 100 | 66 |  |  | 498 | $structOpt = 1 if $structOpt eq '2' and not @setTags; | 
| 1249 |  |  |  |  |  |  | # +------------------------------------------+ | 
| 1250 |  |  |  |  |  |  | # ! DON'T FORGET!!  Must consider each new   ! | 
| 1251 |  |  |  |  |  |  | # ! option to decide how it is handled here. ! | 
| 1252 |  |  |  |  |  |  | # +------------------------------------------+ | 
| 1253 |  |  |  |  |  |  | $srcExifTool->Options( | 
| 1254 |  |  |  |  |  |  | Binary          => 1, | 
| 1255 |  |  |  |  |  |  | Charset         => $$options{Charset}, | 
| 1256 |  |  |  |  |  |  | CharsetEXIF     => $$options{CharsetEXIF}, | 
| 1257 |  |  |  |  |  |  | CharsetFileName => $$options{CharsetFileName}, | 
| 1258 |  |  |  |  |  |  | CharsetID3      => $$options{CharsetID3}, | 
| 1259 |  |  |  |  |  |  | CharsetIPTC     => $$options{CharsetIPTC}, | 
| 1260 |  |  |  |  |  |  | CharsetPhotoshop=> $$options{CharsetPhotoshop}, | 
| 1261 |  |  |  |  |  |  | Composite       => $$options{Composite}, | 
| 1262 |  |  |  |  |  |  | CoordFormat     => $$options{CoordFormat} || '%d %d %.8f', # copy coordinates at high resolution unless otherwise specified | 
| 1263 |  |  |  |  |  |  | DateFormat      => $$options{DateFormat}, | 
| 1264 |  |  |  |  |  |  | Duplicates      => 1, | 
| 1265 |  |  |  |  |  |  | Escape          => $$options{Escape}, | 
| 1266 |  |  |  |  |  |  | # Exclude (set below) | 
| 1267 |  |  |  |  |  |  | ExtendedXMP     => $$options{ExtendedXMP}, | 
| 1268 |  |  |  |  |  |  | ExtractEmbedded => $$options{ExtractEmbedded}, | 
| 1269 |  |  |  |  |  |  | FastScan        => $$options{FastScan}, | 
| 1270 |  |  |  |  |  |  | Filter          => $$options{Filter}, | 
| 1271 |  |  |  |  |  |  | FixBase         => $$options{FixBase}, | 
| 1272 |  |  |  |  |  |  | GlobalTimeShift => $$options{GlobalTimeShift}, | 
| 1273 |  |  |  |  |  |  | HexTagIDs       => $$options{HexTagIDs}, | 
| 1274 |  |  |  |  |  |  | IgnoreMinorErrors=>$$options{IgnoreMinorErrors}, | 
| 1275 |  |  |  |  |  |  | IgnoreTags      => $$options{IgnoreTags}, | 
| 1276 |  |  |  |  |  |  | Lang            => $$options{Lang}, | 
| 1277 |  |  |  |  |  |  | LargeFileSupport=> $$options{LargeFileSupport}, | 
| 1278 |  |  |  |  |  |  | List            => 1, | 
| 1279 |  |  |  |  |  |  | ListItem        => $$options{ListItem}, | 
| 1280 |  |  |  |  |  |  | ListSep         => $$options{ListSep}, | 
| 1281 |  |  |  |  |  |  | MakerNotes      => $$options{FastScan} && $$options{FastScan} > 1 ? undef : 1, | 
| 1282 |  |  |  |  |  |  | MDItemTags      => $$options{MDItemTags}, | 
| 1283 |  |  |  |  |  |  | MissingTagValue => $$options{MissingTagValue}, | 
| 1284 |  |  |  |  |  |  | NoPDFList       => $$options{NoPDFList}, | 
| 1285 |  |  |  |  |  |  | Password        => $$options{Password}, | 
| 1286 |  |  |  |  |  |  | PrintConv       => $$options{PrintConv}, | 
| 1287 |  |  |  |  |  |  | QuickTimeUTC    => $$options{QuickTimeUTC}, | 
| 1288 |  |  |  |  |  |  | RequestAll      => $$options{RequestAll} || 1, # (is this still necessary now that RequestTags are being set?) | 
| 1289 |  |  |  |  |  |  | RequestTags     => $$options{RequestTags}, | 
| 1290 |  |  |  |  |  |  | SaveFormat      => $$options{SaveFormat}, | 
| 1291 |  |  |  |  |  |  | SavePath        => $$options{SavePath}, | 
| 1292 |  |  |  |  |  |  | ScanForXMP      => $$options{ScanForXMP}, | 
| 1293 |  |  |  |  |  |  | StrictDate      => defined $$options{StrictDate} ? $$options{StrictDate} : 1, | 
| 1294 |  |  |  |  |  |  | Struct          => $structOpt, | 
| 1295 |  |  |  |  |  |  | SystemTags      => $$options{SystemTags}, | 
| 1296 |  |  |  |  |  |  | TimeZone        => $$options{TimeZone}, | 
| 1297 |  |  |  |  |  |  | Unknown         => $$options{Unknown}, | 
| 1298 |  |  |  |  |  |  | UserParam       => $$options{UserParam}, | 
| 1299 |  |  |  |  |  |  | Validate        => $$options{Validate}, | 
| 1300 |  |  |  |  |  |  | XAttrTags       => $$options{XAttrTags}, | 
| 1301 |  |  |  |  |  |  | XMPAutoConv     => $$options{XMPAutoConv}, | 
| 1302 | 58 | 50 | 50 |  |  | 2517 | ); | 
|  |  | 50 | 33 |  |  |  |  | 
|  |  |  | 50 |  |  |  |  | 
| 1303 | 58 |  |  |  |  | 282 | $$srcExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET}; | 
| 1304 | 58 |  |  |  |  | 253 | foreach $tag (@setTags) { | 
| 1305 | 62 | 100 |  |  |  | 275 | next if ref $tag; | 
| 1306 | 61 | 100 |  |  |  | 269 | if ($tag =~ /^-(.*)/) { | 
| 1307 |  |  |  |  |  |  | # avoid extracting tags that are excluded | 
| 1308 | 7 |  |  |  |  | 24 | push @exclude, $1; | 
| 1309 | 7 |  |  |  |  | 56 | next; | 
| 1310 |  |  |  |  |  |  | } | 
| 1311 |  |  |  |  |  |  | # add specified tags to list of requested tags | 
| 1312 | 54 |  |  |  |  | 180 | $_ = $tag; | 
| 1313 | 54 | 100 |  |  |  | 610 | if (/(.+?)\s*(>|<)\s*(.+)/) { | 
| 1314 | 23 | 100 |  |  |  | 113 | if ($2 eq '>') { | 
| 1315 | 10 |  |  |  |  | 34 | $_ = $1; | 
| 1316 |  |  |  |  |  |  | } else { | 
| 1317 | 13 |  |  |  |  | 50 | $_ = $3; | 
| 1318 | 13 | 100 |  |  |  | 99 | /\$/ and push(@reqTags, /\$\{?(?:[-\w]+:)*([-\w?*]+)/g), next; | 
| 1319 |  |  |  |  |  |  | } | 
| 1320 |  |  |  |  |  |  | } | 
| 1321 | 49 | 50 |  |  |  | 506 | push @reqTags, $2 if /(^|:)([-\w?*]+)#?$/; | 
| 1322 |  |  |  |  |  |  | } | 
| 1323 | 58 | 100 |  |  |  | 273 | if (@exclude) { | 
| 1324 | 6 |  |  |  |  | 36 | ExpandShortcuts(\@exclude, 1); | 
| 1325 | 6 |  |  |  |  | 41 | $srcExifTool->Options(Exclude => \@exclude); | 
| 1326 |  |  |  |  |  |  | } | 
| 1327 | 58 | 100 |  |  |  | 411 | $srcExifTool->Options(RequestTags => \@reqTags) if @reqTags; | 
| 1328 | 58 |  |  |  |  | 233 | my $printConv = $$options{PrintConv}; | 
| 1329 | 58 | 50 |  |  |  | 316 | if ($opts{Type}) { | 
| 1330 |  |  |  |  |  |  | # save source type separately because it may be different than dst Type | 
| 1331 | 0 |  |  |  |  | 0 | $opts{SrcType} = $opts{Type}; | 
| 1332 |  |  |  |  |  |  | # override PrintConv option with initial Type if given | 
| 1333 | 0 | 0 |  |  |  | 0 | $printConv = ($opts{Type} eq 'PrintConv' ? 1 : 0); | 
| 1334 | 0 |  |  |  |  | 0 | $srcExifTool->Options(PrintConv => $printConv); | 
| 1335 |  |  |  |  |  |  | } | 
| 1336 | 58 | 100 |  |  |  | 275 | my $srcType = $printConv ? 'PrintConv' : 'ValueConv'; | 
| 1337 |  |  |  |  |  |  |  | 
| 1338 |  |  |  |  |  |  | # get all tags from source file (including MakerNotes block) | 
| 1339 | 58 |  |  |  |  | 308 | my $info = $srcExifTool->ImageInfo($srcFile); | 
| 1340 | 58 | 50 | 33 |  |  | 433 | return $info if $$info{Error} and $$info{Error} eq 'Error opening file'; | 
| 1341 | 58 |  |  |  |  | 222 | delete $$srcExifTool{VALUE}{Error}; # delete so we can check this later | 
| 1342 |  |  |  |  |  |  |  | 
| 1343 |  |  |  |  |  |  | # sort tags in reverse order so we get priority tag last | 
| 1344 | 58 |  |  |  |  | 5655 | my @tags = reverse sort keys %$info; | 
| 1345 |  |  |  |  |  |  | # | 
| 1346 |  |  |  |  |  |  | # simply transfer all tags from source image if no tags specified | 
| 1347 |  |  |  |  |  |  | # | 
| 1348 | 58 | 100 |  |  |  | 644 | unless (@setTags) { | 
| 1349 |  |  |  |  |  |  | # transfer maker note information to this object | 
| 1350 | 15 |  |  |  |  | 83 | $$self{MAKER_NOTE_FIXUP} = $$srcExifTool{MAKER_NOTE_FIXUP}; | 
| 1351 | 15 |  |  |  |  | 61 | $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER}; | 
| 1352 | 15 |  |  |  |  | 62 | foreach $tag (@tags) { | 
| 1353 |  |  |  |  |  |  | # don't try to set errors or warnings | 
| 1354 | 2649 | 100 |  |  |  | 10845 | next if $tag =~ /^(Error|Warning)\b/; | 
| 1355 |  |  |  |  |  |  | # get appropriate value type if necessary | 
| 1356 | 2645 | 50 | 33 |  |  | 7848 | if ($opts{SrcType} and $opts{SrcType} ne $srcType) { | 
| 1357 | 0 |  |  |  |  | 0 | $$info{$tag} = $srcExifTool->GetValue($tag, $opts{SrcType}); | 
| 1358 |  |  |  |  |  |  | } | 
| 1359 |  |  |  |  |  |  | # set value for this tag | 
| 1360 | 2645 |  |  |  |  | 12124 | my ($n, $e) = $self->SetNewValue($tag, $$info{$tag}, %opts); | 
| 1361 |  |  |  |  |  |  | # delete this tag if we couldn't set it | 
| 1362 | 2645 | 100 |  |  |  | 10281 | $n or delete $$info{$tag}; | 
| 1363 |  |  |  |  |  |  | } | 
| 1364 | 15 |  |  |  |  | 1409 | return $info; | 
| 1365 |  |  |  |  |  |  | } | 
| 1366 |  |  |  |  |  |  | # | 
| 1367 |  |  |  |  |  |  | # transfer specified tags in the proper order | 
| 1368 |  |  |  |  |  |  | # | 
| 1369 |  |  |  |  |  |  | # 1) loop through input list of tags to set, and build @setList | 
| 1370 | 43 |  |  |  |  | 181 | my (@setList, $set, %setMatches, $t); | 
| 1371 | 43 |  |  |  |  | 162 | foreach $t (@setTags) { | 
| 1372 | 62 | 100 |  |  |  | 316 | if (ref $t eq 'HASH') { | 
| 1373 |  |  |  |  |  |  | # update current options | 
| 1374 | 1 |  |  |  |  | 7 | foreach $key (keys %$t) { | 
| 1375 | 1 |  |  |  |  | 5 | $opts{$key} = $$t{$key}; | 
| 1376 |  |  |  |  |  |  | } | 
| 1377 | 1 |  |  |  |  | 3 | next; | 
| 1378 |  |  |  |  |  |  | } | 
| 1379 |  |  |  |  |  |  | # make a copy of the current options for this setTag | 
| 1380 |  |  |  |  |  |  | # (also use this hash to store expression and wildcard flags, EXPR and WILD) | 
| 1381 | 61 |  |  |  |  | 291 | my $opts = { %opts }; | 
| 1382 | 61 |  |  |  |  | 252 | $tag = lc $t;   # change tag/group names to all lower case | 
| 1383 | 61 |  |  |  |  | 178 | my (@fg, $grp, $dst, $dstGrp, $dstTag, $isExclude); | 
| 1384 |  |  |  |  |  |  | # handle redirection to another tag | 
| 1385 | 61 | 100 |  |  |  | 804 | if ($tag =~ /(.+?)\s*(>|<)\s*(.+)/) { | 
| 1386 | 23 |  |  |  |  | 63 | $dstGrp = ''; | 
| 1387 | 23 |  |  |  |  | 48 | my $opt; | 
| 1388 | 23 | 100 |  |  |  | 110 | if ($2 eq '>') { | 
| 1389 | 10 |  |  |  |  | 50 | ($tag, $dstTag) = ($1, $3); | 
| 1390 |  |  |  |  |  |  | # flag add and delete (eg. '+<' and '-<') redirections | 
| 1391 | 10 | 50 | 33 |  |  | 100 | $opt = $1 if $tag =~ s/\s*([-+])$// or $dstTag =~ s/^([-+])\s*//; | 
| 1392 |  |  |  |  |  |  | } else { | 
| 1393 | 13 |  |  |  |  | 76 | ($tag, $dstTag) = ($3, $1); | 
| 1394 | 13 | 50 |  |  |  | 76 | $opt = $1 if $dstTag =~ s/\s*([-+])$//; | 
| 1395 |  |  |  |  |  |  | # handle expressions | 
| 1396 | 13 | 100 |  |  |  | 62 | if ($tag =~ /\$/) { | 
| 1397 | 5 |  |  |  |  | 18 | $tag = $t;  # restore original case | 
| 1398 |  |  |  |  |  |  | # recover leading whitespace (except for initial single space) | 
| 1399 | 5 |  |  |  |  | 45 | $tag =~ s/(.+?)\s*(>|<) ?//; | 
| 1400 | 5 |  |  |  |  | 26 | $$opts{EXPR} = 1; # flag this expression | 
| 1401 |  |  |  |  |  |  | } else { | 
| 1402 | 8 | 50 |  |  |  | 40 | $opt = $1 if $tag =~ s/^([-+])\s*//; | 
| 1403 |  |  |  |  |  |  | } | 
| 1404 |  |  |  |  |  |  | } | 
| 1405 |  |  |  |  |  |  | # validate tag name(s) | 
| 1406 | 23 | 50 | 66 |  |  | 178 | unless ($$opts{EXPR} or ValidTagName($tag)) { | 
| 1407 | 0 |  |  |  |  | 0 | $self->Warn("Invalid tag name '${tag}'. Use '=' not '<' to assign a tag value"); | 
| 1408 | 0 |  |  |  |  | 0 | next; | 
| 1409 |  |  |  |  |  |  | } | 
| 1410 | 23 | 50 |  |  |  | 110 | ValidTagName($dstTag) or $self->Warn("Invalid tag name '${dstTag}'"), next; | 
| 1411 |  |  |  |  |  |  | # translate '+' and '-' to appropriate SetNewValue option | 
| 1412 | 23 | 50 |  |  |  | 106 | if ($opt) { | 
| 1413 | 0 |  |  |  |  | 0 | $$opts{{ '+' => 'AddValue', '-' => 'DelValue' }->{$opt}} = 1; | 
| 1414 | 0 |  |  |  |  | 0 | $$opts{Shift} = 0;  # shift if shiftable | 
| 1415 |  |  |  |  |  |  | } | 
| 1416 | 23 | 100 |  |  |  | 161 | ($dstGrp, $dstTag) = ($1, $2) if $dstTag =~ /(.*):(.+)/; | 
| 1417 |  |  |  |  |  |  | # ValueConv may be specified separately on the destination with '#' | 
| 1418 | 23 | 50 |  |  |  | 111 | $$opts{Type} = 'ValueConv' if $dstTag =~ s/#$//; | 
| 1419 |  |  |  |  |  |  | # replace tag name of 'all' with '*' | 
| 1420 | 23 | 100 |  |  |  | 95 | $dstTag = '*' if $dstTag eq 'all'; | 
| 1421 |  |  |  |  |  |  | } | 
| 1422 | 61 | 100 |  |  |  | 311 | unless ($$opts{EXPR}) { | 
| 1423 | 56 |  |  |  |  | 235 | $isExclude = ($tag =~ s/^-//); | 
| 1424 | 56 | 100 |  |  |  | 303 | if ($tag =~ /(.*):(.+)/) { | 
| 1425 | 31 |  |  |  |  | 152 | ($grp, $tag) = ($1, $2); | 
| 1426 | 31 |  |  |  |  | 160 | foreach (split /:/, $grp) { | 
| 1427 |  |  |  |  |  |  | # save family/groups in list (ignoring 'all' and '*') | 
| 1428 | 32 | 50 | 33 |  |  | 320 | next unless length($_) and /^(\d+)?(.*)/; | 
| 1429 | 32 |  |  |  |  | 131 | my ($f, $g) = ($1, $2); | 
| 1430 | 32 | 50 |  |  |  | 160 | $f = 7 if $g =~ s/^ID-//i; | 
| 1431 | 32 | 100 | 100 |  |  | 301 | push @fg, [ $f, $g ] unless $g eq '*' or $g eq 'all'; | 
| 1432 |  |  |  |  |  |  | } | 
| 1433 |  |  |  |  |  |  | } | 
| 1434 |  |  |  |  |  |  | # allow ValueConv to be specified by a '#' on the tag name | 
| 1435 | 56 | 50 |  |  |  | 248 | if ($tag =~ s/#$//) { | 
| 1436 | 0 |  |  |  |  | 0 | $$opts{SrcType} = 'ValueConv'; | 
| 1437 | 0 | 0 |  |  |  | 0 | $$opts{Type} = 'ValueConv' unless $dstTag; | 
| 1438 |  |  |  |  |  |  | } | 
| 1439 |  |  |  |  |  |  | # replace 'all' with '*' in tag and group names | 
| 1440 | 56 | 100 |  |  |  | 209 | $tag = '*' if $tag eq 'all'; | 
| 1441 |  |  |  |  |  |  | # allow wildcards in tag names (handle differently from all tags: '*') | 
| 1442 | 56 | 100 | 100 |  |  | 424 | if ($tag =~ /[?*]/ and $tag ne '*') { | 
| 1443 | 2 |  |  |  |  | 9 | $$opts{WILD} = 1;   # set flag indicating wildcards were used in source tag | 
| 1444 | 2 |  |  |  |  | 7 | $tag =~ s/\*/[-\\w]*/g; | 
| 1445 | 2 |  |  |  |  | 13 | $tag =~ s/\?/[-\\w]/g; | 
| 1446 |  |  |  |  |  |  | } | 
| 1447 |  |  |  |  |  |  | } | 
| 1448 |  |  |  |  |  |  | # redirect, exclude or set this tag (Note: @fg is empty if we don't care about the group) | 
| 1449 | 61 | 100 |  |  |  | 270 | if ($dstTag) { | 
|  |  | 100 |  |  |  |  |  | 
| 1450 |  |  |  |  |  |  | # redirect this tag | 
| 1451 | 23 | 50 |  |  |  | 93 | $isExclude and return { Error => "Can't redirect excluded tag" }; | 
| 1452 |  |  |  |  |  |  | # set destination group the same as source if necessary | 
| 1453 |  |  |  |  |  |  | # (removed in 7.72 so '-*:* | 
| 1454 |  |  |  |  |  |  | # $dstGrp = $grp if $dstGrp eq '*' and $grp; | 
| 1455 |  |  |  |  |  |  | # write to specified destination group/tag | 
| 1456 | 23 |  |  |  |  | 88 | $dst = [ $dstGrp, $dstTag ]; | 
| 1457 |  |  |  |  |  |  | } elsif ($isExclude) { | 
| 1458 |  |  |  |  |  |  | # implicitly assume '*' if first entry is an exclusion | 
| 1459 | 7 | 100 |  |  |  | 40 | unshift @setList, [ [ ], '*', [ '', '*' ], $opts ] unless @setList; | 
| 1460 |  |  |  |  |  |  | # exclude this tag by leaving $dst undefined | 
| 1461 |  |  |  |  |  |  | } else { | 
| 1462 | 31 | 100 | 100 |  |  | 270 | $dst = [ $grp || '', $$opts{WILD} ? '*' : $tag ]; # use same group name for dest | 
| 1463 |  |  |  |  |  |  | } | 
| 1464 |  |  |  |  |  |  | # save in reverse order so we don't set tags before an exclude | 
| 1465 | 61 |  |  |  |  | 350 | unshift @setList, [ \@fg, $tag, $dst, $opts ]; | 
| 1466 |  |  |  |  |  |  | } | 
| 1467 |  |  |  |  |  |  | # 2) initialize lists of matching tags for each setTag | 
| 1468 | 43 |  |  |  |  | 162 | foreach $set (@setList) { | 
| 1469 | 62 | 100 |  |  |  | 399 | $$set[2] and $setMatches{$set} = [ ]; | 
| 1470 |  |  |  |  |  |  | } | 
| 1471 |  |  |  |  |  |  | # 3) loop through all tags in source image and save tags matching each setTag | 
| 1472 | 43 |  |  |  |  | 122 | my %rtnInfo; | 
| 1473 | 43 |  |  |  |  | 148 | foreach $tag (@tags) { | 
| 1474 |  |  |  |  |  |  | # don't try to set errors or warnings | 
| 1475 | 6295 | 100 |  |  |  | 15357 | if ($tag =~ /^(Error|Warning)( |$)/) { | 
| 1476 | 13 |  |  |  |  | 45 | $rtnInfo{$tag} = $$info{$tag}; | 
| 1477 | 13 |  |  |  |  | 31 | next; | 
| 1478 |  |  |  |  |  |  | } | 
| 1479 |  |  |  |  |  |  | # only set specified tags | 
| 1480 | 6282 |  |  |  |  | 13031 | my $lcTag = lc(GetTagName($tag)); | 
| 1481 | 6282 |  |  |  |  | 10484 | my (@grp, %grp); | 
| 1482 | 6282 |  |  |  |  | 10183 | SET:    foreach $set (@setList) { | 
| 1483 |  |  |  |  |  |  | # check first for matching tag | 
| 1484 | 8868 | 100 | 100 |  |  | 27453 | unless ($$set[1] eq $lcTag or $$set[1] eq '*') { | 
| 1485 |  |  |  |  |  |  | # handle wildcards | 
| 1486 | 6252 | 100 | 100 |  |  | 17319 | next unless $$set[3]{WILD} and $lcTag =~ /^$$set[1]$/; | 
| 1487 |  |  |  |  |  |  | } | 
| 1488 |  |  |  |  |  |  | # then check for matching group | 
| 1489 | 2630 | 100 |  |  |  | 3804 | if (@{$$set[0]}) { | 
|  | 2630 |  |  |  |  | 5393 |  | 
| 1490 |  |  |  |  |  |  | # get lower case group names if not done already | 
| 1491 | 1468 | 100 |  |  |  | 2913 | unless (@grp) { | 
| 1492 | 1365 |  |  |  |  | 3344 | @grp = map(lc, $srcExifTool->GetGroup($tag)); | 
| 1493 | 1365 |  |  |  |  | 8249 | $grp{$_} = 1 foreach @grp; | 
| 1494 |  |  |  |  |  |  | } | 
| 1495 | 1468 |  |  |  |  | 2367 | foreach (@{$$set[0]}) { | 
|  | 1468 |  |  |  |  | 3018 |  | 
| 1496 | 1510 |  |  |  |  | 2982 | my ($f, $g) = @$_; | 
| 1497 | 1510 | 50 |  |  |  | 2946 | if (not defined $f) { | 
|  |  | 0 |  |  |  |  |  | 
| 1498 | 1510 | 100 |  |  |  | 5119 | next SET unless $grp{$g}; | 
| 1499 |  |  |  |  |  |  | } elsif ($f == 7) { | 
| 1500 | 0 | 0 |  |  |  | 0 | next SET unless IsSameID($srcExifTool->GetTagID($tag), $g); | 
| 1501 |  |  |  |  |  |  | } else { | 
| 1502 | 0 | 0 | 0 |  |  | 0 | next SET unless defined $grp[$f] and $g eq $grp[$f]; | 
| 1503 |  |  |  |  |  |  | } | 
| 1504 |  |  |  |  |  |  | } | 
| 1505 |  |  |  |  |  |  | } | 
| 1506 | 1616 | 100 |  |  |  | 3762 | last unless $$set[2];   # all done if we hit an exclude | 
| 1507 |  |  |  |  |  |  | # add to the list of tags matching this setTag | 
| 1508 | 1442 |  |  |  |  | 1986 | push @{$setMatches{$set}}, $tag; | 
|  | 1442 |  |  |  |  | 5125 |  | 
| 1509 |  |  |  |  |  |  | } | 
| 1510 |  |  |  |  |  |  | } | 
| 1511 |  |  |  |  |  |  | # 4) loop through each setTag in original order, setting new tag values | 
| 1512 | 43 |  |  |  |  | 222 | foreach $set (reverse @setList) { | 
| 1513 |  |  |  |  |  |  | # get options for SetNewValue | 
| 1514 | 62 |  |  |  |  | 189 | my $opts = $$set[3]; | 
| 1515 |  |  |  |  |  |  | # handle expressions | 
| 1516 | 62 | 100 |  |  |  | 253 | if ($$opts{EXPR}) { | 
| 1517 | 5 |  |  |  |  | 43 | my $val = $srcExifTool->InsertTagValues(\@tags, $$set[1], 'Error'); | 
| 1518 | 5 | 50 |  |  |  | 29 | if ($$srcExifTool{VALUE}{Error}) { | 
| 1519 |  |  |  |  |  |  | # pass on any error as a warning | 
| 1520 | 0 |  |  |  |  | 0 | $tag = NextFreeTagKey(\%rtnInfo, 'Warning'); | 
| 1521 | 0 |  |  |  |  | 0 | $rtnInfo{$tag} = $$srcExifTool{VALUE}{Error}; | 
| 1522 | 0 |  |  |  |  | 0 | delete $$srcExifTool{VALUE}{Error}; | 
| 1523 | 0 | 0 |  |  |  | 0 | next unless defined $val; | 
| 1524 |  |  |  |  |  |  | } | 
| 1525 | 5 |  |  |  |  | 14 | my ($dstGrp, $dstTag) = @{$$set[2]}; | 
|  | 5 |  |  |  |  | 27 |  | 
| 1526 | 5 | 50 | 33 |  |  | 46 | $$opts{Protected} = 1 unless $dstTag =~ /[?*]/ and $dstTag ne '*'; | 
| 1527 | 5 | 50 |  |  |  | 24 | $$opts{Group} = $dstGrp if $dstGrp; | 
| 1528 | 5 |  |  |  |  | 48 | my @rtnVals = $self->SetNewValue($dstTag, $val, %$opts); | 
| 1529 | 5 | 50 |  |  |  | 32 | $rtnInfo{$dstTag} = $val if $rtnVals[0]; # tag was set successfully | 
| 1530 | 5 |  |  |  |  | 20 | next; | 
| 1531 |  |  |  |  |  |  | } | 
| 1532 | 57 |  |  |  |  | 138 | foreach $tag (@{$setMatches{$set}}) { | 
|  | 57 |  |  |  |  | 244 |  | 
| 1533 | 1442 |  |  |  |  | 2440 | my ($val, $noWarn); | 
| 1534 | 1442 | 50 | 33 |  |  | 4596 | if ($$opts{SrcType} and $$opts{SrcType} ne $srcType) { | 
| 1535 | 0 |  |  |  |  | 0 | $val = $srcExifTool->GetValue($tag, $$opts{SrcType}); | 
| 1536 |  |  |  |  |  |  | } else { | 
| 1537 | 1442 |  |  |  |  | 4721 | $val = $$info{$tag}; | 
| 1538 |  |  |  |  |  |  | } | 
| 1539 | 1442 |  |  |  |  | 2303 | my ($dstGrp, $dstTag) = @{$$set[2]}; | 
|  | 1442 |  |  |  |  | 3806 |  | 
| 1540 | 1442 | 100 |  |  |  | 3003 | if ($dstGrp) { | 
| 1541 | 1364 |  |  |  |  | 3883 | my @dstGrp = split /:/, $dstGrp; | 
| 1542 |  |  |  |  |  |  | # destination group of '*' writes to same group as source tag | 
| 1543 |  |  |  |  |  |  | # (family 1 unless otherwise specified) | 
| 1544 | 1364 |  |  |  |  | 2965 | foreach (@dstGrp) { | 
| 1545 | 1366 | 100 |  |  |  | 6739 | next unless /^(\d*)(all|\*)$/i; | 
| 1546 | 1082 | 50 |  |  |  | 5428 | $_ = $1 . $srcExifTool->GetGroup($tag, length $1 ? $1 : 1); | 
| 1547 | 1082 |  |  |  |  | 2495 | $noWarn = 1;    # don't warn on wildcard destinations | 
| 1548 |  |  |  |  |  |  | } | 
| 1549 | 1364 |  |  |  |  | 4416 | $$opts{Group} = join ':', @dstGrp; | 
| 1550 |  |  |  |  |  |  | } else { | 
| 1551 | 78 |  |  |  |  | 174 | delete $$opts{Group}; | 
| 1552 |  |  |  |  |  |  | } | 
| 1553 |  |  |  |  |  |  | # transfer maker note information if setting this tag | 
| 1554 | 1442 | 100 |  |  |  | 5030 | if ($$srcExifTool{TAG_INFO}{$tag}{MakerNotes}) { | 
| 1555 | 7 |  |  |  |  | 50 | $$self{MAKER_NOTE_FIXUP} = $$srcExifTool{MAKER_NOTE_FIXUP}; | 
| 1556 | 7 |  |  |  |  | 30 | $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER}; | 
| 1557 |  |  |  |  |  |  | } | 
| 1558 | 1442 | 100 |  |  |  | 3620 | if ($dstTag eq '*') { | 
| 1559 | 1415 |  |  |  |  | 2306 | $dstTag = $tag; | 
| 1560 | 1415 |  |  |  |  | 2163 | $noWarn = 1; | 
| 1561 |  |  |  |  |  |  | } | 
| 1562 | 1442 | 100 | 100 |  |  | 4290 | if ($$set[1] eq '*' or $$set[3]{WILD}) { | 
| 1563 |  |  |  |  |  |  | # don't copy from protected binary tags when using wildcards | 
| 1564 |  |  |  |  |  |  | next if $$srcExifTool{TAG_INFO}{$tag}{Protected} and | 
| 1565 | 1409 | 100 | 100 |  |  | 4334 | $$srcExifTool{TAG_INFO}{$tag}{Binary}; | 
| 1566 |  |  |  |  |  |  | # don't copy to protected tags when using wildcards | 
| 1567 | 1383 |  |  |  |  | 2303 | delete $$opts{Protected}; | 
| 1568 |  |  |  |  |  |  | # don't copy flattened tags if copying structures too when copying all | 
| 1569 | 1383 | 50 |  |  |  | 3447 | $$opts{NoFlat} = $structOpt eq '2' ? 1 : 0; | 
| 1570 |  |  |  |  |  |  | } else { | 
| 1571 |  |  |  |  |  |  | # allow protected tags to be copied if specified explicitly | 
| 1572 | 33 | 50 |  |  |  | 185 | $$opts{Protected} = 1 unless $dstTag =~ /[?*]/; | 
| 1573 | 33 |  |  |  |  | 93 | delete $$opts{NoFlat}; | 
| 1574 |  |  |  |  |  |  | } | 
| 1575 |  |  |  |  |  |  | # set value(s) for this tag | 
| 1576 | 1416 |  |  |  |  | 6038 | my ($rtn, $wrn) = $self->SetNewValue($dstTag, $val, %$opts); | 
| 1577 |  |  |  |  |  |  | # this was added in version 9.14, and allowed actions like "-subject | 
| 1578 |  |  |  |  |  |  | # write values of multiple tags into a list, but it had the side effect of | 
| 1579 |  |  |  |  |  |  | # duplicating items if there were multiple list tags with the same name | 
| 1580 |  |  |  |  |  |  | # (eg. -use mwg "-creator | 
| 1581 |  |  |  |  |  |  | # $$opts{Replace} = 0;    # accumulate values from tags matching a single argument | 
| 1582 | 1416 | 50 | 66 |  |  | 5559 | if ($wrn and not $noWarn) { | 
| 1583 |  |  |  |  |  |  | # return this warning | 
| 1584 | 0 |  |  |  |  | 0 | $rtnInfo{NextFreeTagKey(\%rtnInfo, 'Warning')} = $wrn; | 
| 1585 | 0 |  |  |  |  | 0 | $noWarn = 1; | 
| 1586 |  |  |  |  |  |  | } | 
| 1587 | 1416 | 100 |  |  |  | 5471 | $rtnInfo{$tag} = $val if $rtn;  # tag was set successfully | 
| 1588 |  |  |  |  |  |  | } | 
| 1589 |  |  |  |  |  |  | } | 
| 1590 | 43 |  |  |  |  | 3208 | return \%rtnInfo;   # return information that we set | 
| 1591 |  |  |  |  |  |  | } | 
| 1592 |  |  |  |  |  |  |  | 
| 1593 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1594 |  |  |  |  |  |  | # Get new value(s) for tag | 
| 1595 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) tag name (or tagInfo or nvHash ref, not public) | 
| 1596 |  |  |  |  |  |  | #         2) optional pointer to return new value hash reference (not part of public API) | 
| 1597 |  |  |  |  |  |  | # Returns: List of new Raw values (list may be empty if tag is being deleted) | 
| 1598 |  |  |  |  |  |  | # Notes: 1) Preferentially returns new value from Extra table if writable Extra tag exists | 
| 1599 |  |  |  |  |  |  | # 2) Must call AFTER IsOverwriting() returns 1 to get proper value for shifted times | 
| 1600 |  |  |  |  |  |  | # 3) Tag name is case sensitive and may be prefixed by family 0 or 1 group name | 
| 1601 |  |  |  |  |  |  | # 4) Value may have been modified by CHECK_PROC routine after ValueConv | 
| 1602 |  |  |  |  |  |  | sub GetNewValue($$;$) | 
| 1603 |  |  |  |  |  |  | { | 
| 1604 | 6705 |  |  | 6705 | 1 | 11867 | local $_; | 
| 1605 | 6705 |  |  |  |  | 11307 | my $self = shift; | 
| 1606 | 6705 |  |  |  |  | 11159 | my $tag = shift; | 
| 1607 | 6705 |  |  |  |  | 10752 | my $nvHash; | 
| 1608 | 6705 | 100 | 100 |  |  | 32449 | if ((ref $tag eq 'HASH' and $$tag{IsNVH}) or not defined $tag) { | 
|  |  |  | 100 |  |  |  |  | 
| 1609 | 3990 |  |  |  |  | 7316 | $nvHash = $tag; | 
| 1610 |  |  |  |  |  |  | } else { | 
| 1611 | 2715 |  |  |  |  | 4427 | my $newValueHashPt = shift; | 
| 1612 | 2715 | 100 |  |  |  | 6784 | if ($$self{NEW_VALUE}) { | 
| 1613 | 2598 |  |  |  |  | 4602 | my ($group, $tagInfo); | 
| 1614 | 2598 | 100 | 66 |  |  | 13542 | if (ref $tag) { | 
|  |  | 100 |  |  |  |  |  | 
| 1615 | 49 |  |  |  |  | 178 | $nvHash = $self->GetNewValueHash($tag); | 
| 1616 |  |  |  |  |  |  | } elsif (defined($tagInfo = $Image::ExifTool::Extra{$tag}) and | 
| 1617 |  |  |  |  |  |  | $$tagInfo{Writable}) | 
| 1618 |  |  |  |  |  |  | { | 
| 1619 | 1471 |  |  |  |  | 3596 | $nvHash = $self->GetNewValueHash($tagInfo); | 
| 1620 |  |  |  |  |  |  | } else { | 
| 1621 |  |  |  |  |  |  | # separate group from tag name | 
| 1622 | 1078 |  |  |  |  | 1873 | my @groups; | 
| 1623 | 1078 | 100 |  |  |  | 3327 | @groups = split ':', $1 if $tag =~ s/(.*)://; | 
| 1624 | 1078 |  |  |  |  | 3906 | my @tagInfoList = FindTagInfo($tag); | 
| 1625 |  |  |  |  |  |  | # decide which tag we want | 
| 1626 | 1078 |  |  |  |  | 2359 | GNV_TagInfo:    foreach $tagInfo (@tagInfoList) { | 
| 1627 | 1082 | 100 |  |  |  | 2733 | my $nvh = $self->GetNewValueHash($tagInfo) or next; | 
| 1628 |  |  |  |  |  |  | # select tag in specified group(s) if necessary | 
| 1629 | 4 |  |  |  |  | 10 | foreach (@groups) { | 
| 1630 | 2 | 50 |  |  |  | 14 | next if $_ eq $$nvh{WriteGroup}; | 
| 1631 | 2 |  |  |  |  | 39 | my @grps = $self->GetGroup($tagInfo); | 
| 1632 | 2 | 50 |  |  |  | 9 | if ($grps[0] eq $$nvh{WriteGroup}) { | 
| 1633 |  |  |  |  |  |  | # check family 1 group only if WriteGroup is not specific | 
| 1634 | 0 | 0 |  |  |  | 0 | next if $_ eq $grps[1]; | 
| 1635 |  |  |  |  |  |  | } else { | 
| 1636 |  |  |  |  |  |  | # otherwise check family 0 group | 
| 1637 | 2 | 50 |  |  |  | 9 | next if $_ eq $grps[0]; | 
| 1638 |  |  |  |  |  |  | } | 
| 1639 |  |  |  |  |  |  | # also check family 7 | 
| 1640 | 0 | 0 | 0 |  |  | 0 | next if /^ID-(.*)/i and IsSameID($$tagInfo{TagID}, $1); | 
| 1641 |  |  |  |  |  |  | # step to next entry in list | 
| 1642 | 0 | 0 |  |  |  | 0 | $nvh = $$nvh{Next} or next GNV_TagInfo; | 
| 1643 |  |  |  |  |  |  | } | 
| 1644 | 4 |  |  |  |  | 10 | $nvHash = $nvh; | 
| 1645 |  |  |  |  |  |  | # give priority to the one we are creating | 
| 1646 | 4 | 100 |  |  |  | 25 | last if defined $$nvHash{IsCreating}; | 
| 1647 |  |  |  |  |  |  | } | 
| 1648 |  |  |  |  |  |  | } | 
| 1649 |  |  |  |  |  |  | } | 
| 1650 |  |  |  |  |  |  | # return new value hash if requested | 
| 1651 | 2715 | 100 |  |  |  | 7424 | $newValueHashPt and $$newValueHashPt = $nvHash; | 
| 1652 |  |  |  |  |  |  | } | 
| 1653 | 6705 | 100 | 100 |  |  | 22853 | unless ($nvHash and $$nvHash{Value}) { | 
| 1654 | 4435 | 100 |  |  |  | 17000 | return () if wantarray;  # return empty list | 
| 1655 | 2662 |  |  |  |  | 7304 | return undef; | 
| 1656 |  |  |  |  |  |  | } | 
| 1657 | 2270 |  |  |  |  | 4368 | my $vals = $$nvHash{Value}; | 
| 1658 |  |  |  |  |  |  | # do inverse raw conversion if necessary | 
| 1659 |  |  |  |  |  |  | # - must also check after doing a Shift | 
| 1660 | 2270 | 100 | 100 |  |  | 9403 | if ($$nvHash{TagInfo}{RawConvInv} or $$nvHash{Shift}) { | 
| 1661 | 60 |  |  |  |  | 254 | my @copyVals = @$vals;  # modify a copy of the values | 
| 1662 | 60 |  |  |  |  | 166 | $vals = \@copyVals; | 
| 1663 | 60 |  |  |  |  | 187 | my $tagInfo = $$nvHash{TagInfo}; | 
| 1664 | 60 |  |  |  |  | 149 | my $conv = $$tagInfo{RawConvInv}; | 
| 1665 | 60 |  |  |  |  | 142 | my $table = $$tagInfo{Table}; | 
| 1666 | 60 |  |  |  |  | 134 | my ($val, $checkProc); | 
| 1667 | 60 | 100 | 66 |  |  | 296 | $checkProc = $$table{CHECK_PROC} if $$nvHash{Shift} and $table; | 
| 1668 | 60 |  |  |  |  | 340 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 1669 | 60 |  |  |  |  | 182 | undef $evalWarning; | 
| 1670 | 60 |  |  |  |  | 170 | foreach $val (@$vals) { | 
| 1671 |  |  |  |  |  |  | # must check value now if it was shifted | 
| 1672 | 60 | 100 |  |  |  | 203 | if ($checkProc) { | 
| 1673 | 26 |  |  |  |  | 102 | my $err = &$checkProc($self, $tagInfo, \$val); | 
| 1674 | 26 | 50 | 33 |  |  | 133 | if ($err or not defined $val) { | 
| 1675 | 0 | 0 |  |  |  | 0 | $err or $err = 'Error generating raw value'; | 
| 1676 | 0 |  |  |  |  | 0 | $self->WarnOnce("$err for $$tagInfo{Name}"); | 
| 1677 | 0 |  |  |  |  | 0 | @$vals = (); | 
| 1678 | 0 |  |  |  |  | 0 | last; | 
| 1679 |  |  |  |  |  |  | } | 
| 1680 | 26 | 50 |  |  |  | 136 | next unless $conv; | 
| 1681 |  |  |  |  |  |  | } else { | 
| 1682 | 34 | 50 |  |  |  | 120 | last unless $conv; | 
| 1683 |  |  |  |  |  |  | } | 
| 1684 |  |  |  |  |  |  | # do inverse raw conversion | 
| 1685 | 34 | 100 |  |  |  | 133 | if (ref($conv) eq 'CODE') { | 
| 1686 | 2 |  |  |  |  | 9 | $val = &$conv($val, $self); | 
| 1687 |  |  |  |  |  |  | } else { | 
| 1688 |  |  |  |  |  |  | #### eval RawConvInv ($self, $val, $tagInfo) | 
| 1689 | 32 |  |  |  |  | 3777 | $val = eval $conv; | 
| 1690 | 32 | 50 |  |  |  | 215 | $@ and $evalWarning = $@; | 
| 1691 |  |  |  |  |  |  | } | 
| 1692 | 34 | 50 |  |  |  | 231 | if ($evalWarning) { | 
| 1693 |  |  |  |  |  |  | # an empty warning ("\n") ignores tag with no error | 
| 1694 | 0 | 0 |  |  |  | 0 | if ($evalWarning ne "\n") { | 
| 1695 | 0 |  |  |  |  | 0 | my $err = CleanWarning() . " in $$tagInfo{Name} (RawConvInv)"; | 
| 1696 | 0 |  |  |  |  | 0 | $self->WarnOnce($err); | 
| 1697 |  |  |  |  |  |  | } | 
| 1698 | 0 |  |  |  |  | 0 | @$vals = (); | 
| 1699 | 0 |  |  |  |  | 0 | last; | 
| 1700 |  |  |  |  |  |  | } | 
| 1701 |  |  |  |  |  |  | } | 
| 1702 |  |  |  |  |  |  | } | 
| 1703 |  |  |  |  |  |  | # return our value(s) | 
| 1704 | 2270 | 100 |  |  |  | 9185 | return @$vals if wantarray; | 
| 1705 | 1160 |  |  |  |  | 4905 | return $$vals[0]; | 
| 1706 |  |  |  |  |  |  | } | 
| 1707 |  |  |  |  |  |  |  | 
| 1708 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1709 |  |  |  |  |  |  | # Return the total number of new values set | 
| 1710 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 1711 |  |  |  |  |  |  | # Returns: Scalar context) Number of new values that have been set (incl pseudo) | 
| 1712 |  |  |  |  |  |  | #          List context) Number of new values (incl pseudo), number of "pseudo" values | 
| 1713 |  |  |  |  |  |  | # ("pseudo" values are those which don't require rewriting the file to change) | 
| 1714 |  |  |  |  |  |  | sub CountNewValues($) | 
| 1715 |  |  |  |  |  |  | { | 
| 1716 | 235 |  |  | 235 | 1 | 629 | my $self = shift; | 
| 1717 | 235 |  |  |  |  | 694 | my $newVal = $$self{NEW_VALUE}; | 
| 1718 | 235 |  |  |  |  | 772 | my ($num, $pseudo) = (0, 0); | 
| 1719 | 235 | 100 |  |  |  | 871 | if ($newVal) { | 
| 1720 | 216 |  |  |  |  | 1003 | $num = scalar keys %$newVal; | 
| 1721 | 216 |  |  |  |  | 484 | my $nv; | 
| 1722 | 216 |  |  |  |  | 2750 | foreach $nv (values %$newVal) { | 
| 1723 | 19092 |  |  |  |  | 43822 | my $tagInfo = $$nv{TagInfo}; | 
| 1724 |  |  |  |  |  |  | # don't count tags that don't write anything | 
| 1725 | 19092 | 100 |  |  |  | 41705 | $$tagInfo{WriteNothing} and --$num, next; | 
| 1726 |  |  |  |  |  |  | # count the number of pseudo tags included | 
| 1727 | 19077 | 100 |  |  |  | 42901 | $$tagInfo{WritePseudo} and ++$pseudo; | 
| 1728 |  |  |  |  |  |  | } | 
| 1729 |  |  |  |  |  |  | } | 
| 1730 | 235 |  |  |  |  | 722 | $num += scalar keys %{$$self{DEL_GROUP}}; | 
|  | 235 |  |  |  |  | 1033 |  | 
| 1731 | 235 | 50 |  |  |  | 1056 | return $num unless wantarray; | 
| 1732 | 235 |  |  |  |  | 925 | return ($num, $pseudo); | 
| 1733 |  |  |  |  |  |  | } | 
| 1734 |  |  |  |  |  |  |  | 
| 1735 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1736 |  |  |  |  |  |  | # Save new values for subsequent restore | 
| 1737 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 1738 |  |  |  |  |  |  | # Returns: Number of times new values have been saved | 
| 1739 |  |  |  |  |  |  | # Notes: increments SAVE_COUNT flag each time routine is called | 
| 1740 |  |  |  |  |  |  | sub SaveNewValues($) | 
| 1741 |  |  |  |  |  |  | { | 
| 1742 | 1 |  |  | 1 | 1 | 12 | my $self = shift; | 
| 1743 | 1 |  |  |  |  | 5 | my $newValues = $$self{NEW_VALUE}; | 
| 1744 | 1 |  |  |  |  | 4 | my $saveCount = ++$$self{SAVE_COUNT}; | 
| 1745 | 1 |  |  |  |  | 3 | my $key; | 
| 1746 | 1 |  |  |  |  | 28 | foreach $key (keys %$newValues) { | 
| 1747 | 112 |  |  |  |  | 170 | my $nvHash = $$newValues{$key}; | 
| 1748 | 112 |  |  |  |  | 194 | while ($nvHash) { | 
| 1749 |  |  |  |  |  |  | # set Save count if not done already | 
| 1750 | 113 | 50 |  |  |  | 284 | $$nvHash{Save} or $$nvHash{Save} = $saveCount; | 
| 1751 | 113 |  |  |  |  | 222 | $nvHash = $$nvHash{Next}; | 
| 1752 |  |  |  |  |  |  | } | 
| 1753 |  |  |  |  |  |  | } | 
| 1754 |  |  |  |  |  |  | # initialize hash for saving overwritten new values | 
| 1755 | 1 |  |  |  |  | 11 | $$self{SAVE_NEW_VALUE} = { }; | 
| 1756 |  |  |  |  |  |  | # make a copy of the delete group hash | 
| 1757 | 1 |  |  |  |  | 3 | my %delGrp = %{$$self{DEL_GROUP}}; | 
|  | 1 |  |  |  |  | 4 |  | 
| 1758 | 1 |  |  |  |  | 6 | $$self{SAVE_DEL_GROUP} = \%delGrp; | 
| 1759 | 1 |  |  |  |  | 4 | return $saveCount; | 
| 1760 |  |  |  |  |  |  | } | 
| 1761 |  |  |  |  |  |  |  | 
| 1762 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1763 |  |  |  |  |  |  | # Restore new values to last saved state | 
| 1764 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 1765 |  |  |  |  |  |  | # Notes: Restores saved new values, but currently doesn't restore them in the | 
| 1766 |  |  |  |  |  |  | # original order, so there may be some minor side-effects when restoring tags | 
| 1767 |  |  |  |  |  |  | # with overlapping groups. eg) XMP:Identifier, XMP-dc:Identifier | 
| 1768 |  |  |  |  |  |  | # Also, this doesn't do the right thing for list-type tags which accumulate | 
| 1769 |  |  |  |  |  |  | # values across a save point | 
| 1770 |  |  |  |  |  |  | sub RestoreNewValues($) | 
| 1771 |  |  |  |  |  |  | { | 
| 1772 | 1 |  |  | 1 | 1 | 12 | my $self = shift; | 
| 1773 | 1 |  |  |  |  | 3 | my $newValues = $$self{NEW_VALUE}; | 
| 1774 | 1 |  |  |  |  | 5 | my $savedValues = $$self{SAVE_NEW_VALUE}; | 
| 1775 | 1 |  |  |  |  | 3 | my $key; | 
| 1776 |  |  |  |  |  |  | # 1) remove any new values which don't have the Save flag set | 
| 1777 | 1 | 50 |  |  |  | 6 | if ($newValues) { | 
| 1778 | 1 |  |  |  |  | 193 | my @keys = keys %$newValues; | 
| 1779 | 1 |  |  |  |  | 8 | foreach $key (@keys) { | 
| 1780 | 572 |  |  |  |  | 717 | my $lastHash; | 
| 1781 | 572 |  |  |  |  | 1025 | my $nvHash = $$newValues{$key}; | 
| 1782 | 572 |  |  |  |  | 900 | while ($nvHash) { | 
| 1783 | 573 | 100 |  |  |  | 1188 | if ($$nvHash{Save}) { | 
| 1784 | 24 |  |  |  |  | 39 | $lastHash = $nvHash; | 
| 1785 |  |  |  |  |  |  | } else { | 
| 1786 |  |  |  |  |  |  | # remove this entry from the list | 
| 1787 | 549 | 50 |  |  |  | 1050 | if ($lastHash) { | 
|  |  | 100 |  |  |  |  |  | 
| 1788 | 0 |  |  |  |  | 0 | $$lastHash{Next} = $$nvHash{Next}; | 
| 1789 |  |  |  |  |  |  | } elsif ($$nvHash{Next}) { | 
| 1790 | 1 |  |  |  |  | 5 | $$newValues{$key} = $$nvHash{Next}; | 
| 1791 |  |  |  |  |  |  | } else { | 
| 1792 | 548 |  |  |  |  | 785 | delete $$newValues{$key}; | 
| 1793 |  |  |  |  |  |  | } | 
| 1794 |  |  |  |  |  |  | } | 
| 1795 | 573 |  |  |  |  | 2552 | $nvHash = $$nvHash{Next}; | 
| 1796 |  |  |  |  |  |  | } | 
| 1797 |  |  |  |  |  |  | } | 
| 1798 |  |  |  |  |  |  | } | 
| 1799 |  |  |  |  |  |  | # 2) restore saved new values | 
| 1800 | 1 | 50 |  |  |  | 7 | if ($savedValues) { | 
| 1801 | 1 | 50 |  |  |  | 5 | $newValues or $newValues = $$self{NEW_VALUE} = { }; | 
| 1802 | 1 |  |  |  |  | 204 | foreach $key (keys %$savedValues) { | 
| 1803 | 89 | 100 |  |  |  | 154 | if ($$newValues{$key}) { | 
| 1804 |  |  |  |  |  |  | # add saved values to end of list | 
| 1805 | 1 |  |  |  |  | 10 | my $nvHash = LastInList($$newValues{$key}); | 
| 1806 | 1 |  |  |  |  | 5 | $$nvHash{Next} = $$savedValues{$key}; | 
| 1807 |  |  |  |  |  |  | } else { | 
| 1808 | 88 |  |  |  |  | 183 | $$newValues{$key} = $$savedValues{$key}; | 
| 1809 |  |  |  |  |  |  | } | 
| 1810 |  |  |  |  |  |  | } | 
| 1811 | 1 |  |  |  |  | 10 | $$self{SAVE_NEW_VALUE} = { };  # reset saved new values | 
| 1812 |  |  |  |  |  |  | } | 
| 1813 |  |  |  |  |  |  | # 3) restore delete groups | 
| 1814 | 1 |  |  |  |  | 6 | my %delGrp = %{$$self{SAVE_DEL_GROUP}}; | 
|  | 1 |  |  |  |  | 8 |  | 
| 1815 | 1 |  |  |  |  | 14 | $$self{DEL_GROUP} = \%delGrp; | 
| 1816 |  |  |  |  |  |  | } | 
| 1817 |  |  |  |  |  |  |  | 
| 1818 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1819 |  |  |  |  |  |  | # Set filesystem time from from FileModifyDate or FileCreateDate tag | 
| 1820 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) file name or file ref | 
| 1821 |  |  |  |  |  |  | #         2) time (-M or -C) of original file (used for shift; obtained from file if not given) | 
| 1822 |  |  |  |  |  |  | #         3) tag name to write (undef for 'FileModifyDate') | 
| 1823 |  |  |  |  |  |  | #         4) flag set if argument 2 has already been converted to Unix seconds | 
| 1824 |  |  |  |  |  |  | # Returns: 1=time changed OK, 0=nothing done, -1=error setting time | 
| 1825 |  |  |  |  |  |  | #          (increments CHANGED flag and sets corresponding WRITTEN tag) | 
| 1826 |  |  |  |  |  |  | sub SetFileModifyDate($$;$$$) | 
| 1827 |  |  |  |  |  |  | { | 
| 1828 | 0 |  |  | 0 | 1 | 0 | my ($self, $file, $originalTime, $tag, $isUnixTime) = @_; | 
| 1829 | 0 |  |  |  |  | 0 | my $nvHash; | 
| 1830 | 0 | 0 |  |  |  | 0 | $tag = 'FileModifyDate' unless defined $tag; | 
| 1831 | 0 |  |  |  |  | 0 | my $val = $self->GetNewValue($tag, \$nvHash); | 
| 1832 | 0 | 0 |  |  |  | 0 | return 0 unless defined $val; | 
| 1833 | 0 |  |  |  |  | 0 | my $isOverwriting = $self->IsOverwriting($nvHash); | 
| 1834 | 0 | 0 |  |  |  | 0 | return 0 unless $isOverwriting; | 
| 1835 |  |  |  |  |  |  | # can currently only set creation date on Windows systems | 
| 1836 |  |  |  |  |  |  | # (and Mac now too, but that is handled with the MacOS tags) | 
| 1837 | 0 | 0 | 0 |  |  | 0 | return 0 if $tag eq 'FileCreateDate' and $^O ne 'MSWin32'; | 
| 1838 | 0 | 0 |  |  |  | 0 | if ($isOverwriting < 0) {  # are we shifting time? | 
| 1839 |  |  |  |  |  |  | # use original time of this file if not specified | 
| 1840 | 0 | 0 |  |  |  | 0 | unless (defined $originalTime) { | 
| 1841 | 0 |  |  |  |  | 0 | my ($aTime, $mTime, $cTime) = $self->GetFileTime($file); | 
| 1842 | 0 | 0 |  |  |  | 0 | $originalTime = ($tag eq 'FileCreateDate') ? $cTime : $mTime; | 
| 1843 | 0 | 0 |  |  |  | 0 | return 0 unless defined $originalTime; | 
| 1844 | 0 |  |  |  |  | 0 | $isUnixTime = 1; | 
| 1845 |  |  |  |  |  |  | } | 
| 1846 | 0 | 0 |  |  |  | 0 | $originalTime = int($^T - $originalTime*(24*3600) + 0.5) unless $isUnixTime; | 
| 1847 | 0 | 0 |  |  |  | 0 | return 0 unless $self->IsOverwriting($nvHash, $originalTime); | 
| 1848 | 0 |  |  |  |  | 0 | $val = $$nvHash{Value}[0]; # get shifted value | 
| 1849 |  |  |  |  |  |  | } | 
| 1850 | 0 |  |  |  |  | 0 | my ($aTime, $mTime, $cTime); | 
| 1851 | 0 | 0 |  |  |  | 0 | if ($tag eq 'FileCreateDate') { | 
| 1852 | 0 | 0 |  |  |  | 0 | eval { require Win32::API } or $self->WarnOnce("Install Win32::API to set $tag"), return -1; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1853 | 0 | 0 |  |  |  | 0 | eval { require Win32API::File } or $self->WarnOnce("Install Win32API::File to set $tag"), return -1; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1854 | 0 |  |  |  |  | 0 | $cTime = $val; | 
| 1855 |  |  |  |  |  |  | } else { | 
| 1856 | 0 |  |  |  |  | 0 | $aTime = $mTime = $val; | 
| 1857 |  |  |  |  |  |  | } | 
| 1858 | 0 | 0 |  |  |  | 0 | $self->SetFileTime($file, $aTime, $mTime, $cTime, 1) or $self->Warn("Error setting $tag"), return -1; | 
| 1859 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 1860 | 0 |  |  |  |  | 0 | $$self{WRITTEN}{$tag} = $val;   # remember that we wrote this tag | 
| 1861 | 0 |  |  |  |  | 0 | $self->VerboseValue("+ $tag", $val); | 
| 1862 | 0 |  |  |  |  | 0 | return 1; | 
| 1863 |  |  |  |  |  |  | } | 
| 1864 |  |  |  |  |  |  |  | 
| 1865 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1866 |  |  |  |  |  |  | # Change file name and/or directory from FileName and Directory tags | 
| 1867 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) current file name (including path) | 
| 1868 |  |  |  |  |  |  | #         2) new name (or undef to build from FileName and Directory tags) | 
| 1869 |  |  |  |  |  |  | #         3) option: 'HardLink'/'SymLink' to create hard/symbolic link instead of renaming | 
| 1870 |  |  |  |  |  |  | #                    'Test' to only print new file name | 
| 1871 |  |  |  |  |  |  | #         4) 0 to indicate that a file will no longer exist (used for 'Test' only) | 
| 1872 |  |  |  |  |  |  | # Returns: 1=name changed OK, 0=nothing changed, -1=error changing name | 
| 1873 |  |  |  |  |  |  | #          (and increments CHANGED flag if filename changed) | 
| 1874 |  |  |  |  |  |  | # Notes: Will not overwrite existing file.  Creates directories as necessary. | 
| 1875 |  |  |  |  |  |  | sub SetFileName($$;$$$) | 
| 1876 |  |  |  |  |  |  | { | 
| 1877 | 1 |  |  | 1 | 1 | 10 | my ($self, $file, $newName, $opt, $usedFlag) = @_; | 
| 1878 | 1 |  |  |  |  | 3 | my ($nvHash, $doName, $doDir); | 
| 1879 |  |  |  |  |  |  |  | 
| 1880 | 1 | 50 |  |  |  | 7 | $opt or $opt = ''; | 
| 1881 |  |  |  |  |  |  | # determine the new file name | 
| 1882 | 1 | 50 |  |  |  | 6 | unless (defined $newName) { | 
| 1883 | 1 | 50 |  |  |  | 4 | if ($opt) { | 
| 1884 | 0 | 0 | 0 |  |  | 0 | if ($opt eq 'HardLink' or $opt eq 'Link') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1885 | 0 |  |  |  |  | 0 | $newName = $self->GetNewValue('HardLink'); | 
| 1886 |  |  |  |  |  |  | } elsif ($opt eq 'SymLink') { | 
| 1887 | 0 |  |  |  |  | 0 | $newName = $self->GetNewValue('SymLink'); | 
| 1888 |  |  |  |  |  |  | } elsif ($opt eq 'Test') { | 
| 1889 | 0 |  |  |  |  | 0 | $newName = $self->GetNewValue('TestName'); | 
| 1890 |  |  |  |  |  |  | } | 
| 1891 | 0 | 0 |  |  |  | 0 | return 0 unless defined $newName; | 
| 1892 |  |  |  |  |  |  | } else { | 
| 1893 | 1 |  |  |  |  | 7 | my $filename = $self->GetNewValue('FileName', \$nvHash); | 
| 1894 | 1 | 50 | 33 |  |  | 8 | $doName = 1 if defined $filename and $self->IsOverwriting($nvHash, $file); | 
| 1895 | 1 |  |  |  |  | 5 | my $dir = $self->GetNewValue('Directory', \$nvHash); | 
| 1896 | 1 | 50 | 33 |  |  | 10 | $doDir = 1 if defined $dir and $self->IsOverwriting($nvHash, $file); | 
| 1897 | 1 | 50 | 33 |  |  | 6 | return 0 unless $doName or $doDir;  # nothing to do | 
| 1898 | 1 | 50 |  |  |  | 4 | if ($doName) { | 
| 1899 | 1 |  |  |  |  | 5 | $newName = GetNewFileName($file, $filename); | 
| 1900 | 1 | 50 |  |  |  | 6 | $newName = GetNewFileName($newName, $dir) if $doDir; | 
| 1901 |  |  |  |  |  |  | } else { | 
| 1902 | 0 |  |  |  |  | 0 | $newName = GetNewFileName($file, $dir); | 
| 1903 |  |  |  |  |  |  | } | 
| 1904 |  |  |  |  |  |  | } | 
| 1905 |  |  |  |  |  |  | } | 
| 1906 |  |  |  |  |  |  | # validate new file name in Windows | 
| 1907 | 1 | 50 |  |  |  | 9 | if ($^O eq 'MSWin32') { | 
| 1908 | 0 | 0 |  |  |  | 0 | if ($newName =~ /[\0-\x1f<>"|*]/) { | 
| 1909 | 0 |  |  |  |  | 0 | $self->Warn('New file name not allowed in Windows (contains reserved characters)'); | 
| 1910 | 0 |  |  |  |  | 0 | return -1; | 
| 1911 |  |  |  |  |  |  | } | 
| 1912 | 0 | 0 | 0 |  |  | 0 | if ($newName =~ /:/ and $newName !~ /^[A-Z]:[^:]*$/i) { | 
| 1913 | 0 |  |  |  |  | 0 | $self->Warn("New file name not allowed in Windows (contains ':')"); | 
| 1914 | 0 |  |  |  |  | 0 | return -1; | 
| 1915 |  |  |  |  |  |  | } | 
| 1916 | 0 | 0 | 0 |  |  | 0 | if ($newName =~ /\?/ and $newName !~ m{^[\\/]{2}\?[\\/][^?]*$}) { | 
| 1917 | 0 |  |  |  |  | 0 | $self->Warn("New file name not allowed in Windows (contains '?')"); | 
| 1918 | 0 |  |  |  |  | 0 | return -1; | 
| 1919 |  |  |  |  |  |  | } | 
| 1920 | 0 | 0 |  |  |  | 0 | if ($newName =~ m{(^|[\\/])(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\.[^.]*)?$}i) { | 
| 1921 | 0 |  |  |  |  | 0 | $self->Warn('New file name not allowed in Windows (reserved device name)'); | 
| 1922 | 0 |  |  |  |  | 0 | return -1; | 
| 1923 |  |  |  |  |  |  | } | 
| 1924 | 0 | 0 |  |  |  | 0 | if ($newName =~ /([. ])$/) { | 
| 1925 | 0 | 0 |  |  |  | 0 | $self->Warn("New file name not recommended for Windows (ends with '${1}')", 2) and return -1; | 
| 1926 |  |  |  |  |  |  | } | 
| 1927 | 0 | 0 | 0 |  |  | 0 | if (length $newName > 259 and $newName !~ /\?/) { | 
| 1928 | 0 | 0 |  |  |  | 0 | $self->Warn('New file name not recommended for Windows (exceeds 260 chars)', 2) and return -1; | 
| 1929 |  |  |  |  |  |  | } | 
| 1930 |  |  |  |  |  |  | } else { | 
| 1931 | 1 |  |  |  |  | 7 | $newName =~ tr/\0//d;   # make sure name doesn't contain nulls | 
| 1932 |  |  |  |  |  |  | } | 
| 1933 |  |  |  |  |  |  | # protect against empty file name | 
| 1934 | 1 | 50 |  |  |  | 5 | length $newName or $self->Warn('New file name is empty'), return -1; | 
| 1935 |  |  |  |  |  |  | # don't replace existing file | 
| 1936 | 1 | 0 | 0 |  |  | 9 | if ($self->Exists($newName) and (not defined $usedFlag or $usedFlag)) { | 
|  |  |  | 33 |  |  |  |  | 
| 1937 | 0 | 0 | 0 |  |  | 0 | if ($file ne $newName or $opt =~ /Link$/) { | 
| 1938 |  |  |  |  |  |  | # allow for case-insensitive filesystem | 
| 1939 | 0 | 0 | 0 |  |  | 0 | if ($opt =~ /Link$/ or not $self->IsSameFile($file, $newName)) { | 
| 1940 | 0 |  |  |  |  | 0 | $self->Warn("File '${newName}' already exists"); | 
| 1941 | 0 |  |  |  |  | 0 | return -1; | 
| 1942 |  |  |  |  |  |  | } | 
| 1943 |  |  |  |  |  |  | } else { | 
| 1944 | 0 |  |  |  |  | 0 | $self->Warn('File name is unchanged'); | 
| 1945 | 0 |  |  |  |  | 0 | return 0; | 
| 1946 |  |  |  |  |  |  | } | 
| 1947 |  |  |  |  |  |  | } | 
| 1948 | 1 | 50 |  |  |  | 8 | if ($opt eq 'Test') { | 
| 1949 | 0 |  |  |  |  | 0 | my $out = $$self{OPTIONS}{TextOut}; | 
| 1950 | 0 |  |  |  |  | 0 | print $out "'${file}' --> '${newName}'\n"; | 
| 1951 | 0 |  |  |  |  | 0 | return 1; | 
| 1952 |  |  |  |  |  |  | } | 
| 1953 |  |  |  |  |  |  | # create directory for new file if necessary | 
| 1954 | 1 |  |  |  |  | 5 | my $result; | 
| 1955 | 1 | 50 |  |  |  | 10 | if (($result = $self->CreateDirectory($newName)) != 0) { | 
| 1956 | 0 | 0 |  |  |  | 0 | if ($result < 0) { | 
| 1957 | 0 |  |  |  |  | 0 | $self->Warn("Error creating directory for '${newName}'"); | 
| 1958 | 0 |  |  |  |  | 0 | return -1; | 
| 1959 |  |  |  |  |  |  | } | 
| 1960 | 0 |  |  |  |  | 0 | $self->VPrint(0, "Created directory for '${newName}'\n"); | 
| 1961 |  |  |  |  |  |  | } | 
| 1962 | 1 | 50 | 33 |  |  | 14 | if ($opt eq 'HardLink' or $opt eq 'Link') { | 
|  |  | 50 |  |  |  |  |  | 
| 1963 | 0 | 0 |  |  |  | 0 | unless (link $file, $newName) { | 
| 1964 | 0 |  |  |  |  | 0 | $self->Warn("Error creating hard link '${newName}'"); | 
| 1965 | 0 |  |  |  |  | 0 | return -1; | 
| 1966 |  |  |  |  |  |  | } | 
| 1967 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 1968 | 0 |  |  |  |  | 0 | $self->VerboseValue('+ HardLink', $newName); | 
| 1969 | 0 |  |  |  |  | 0 | return 1; | 
| 1970 |  |  |  |  |  |  | } elsif ($opt eq 'SymLink') { | 
| 1971 | 0 | 0 |  |  |  | 0 | $^O eq 'MSWin32' and $self->Warn('SymLink not supported in Windows'), return -1; | 
| 1972 | 0 |  |  |  |  | 0 | $newName =~ s(^\./)();  # remove leading "./" from link name if it exists | 
| 1973 |  |  |  |  |  |  | # path to linked file must be relative to the $newName directory, but $file | 
| 1974 |  |  |  |  |  |  | # is relative to the current directory, so convert it to an absolute path | 
| 1975 |  |  |  |  |  |  | # if using a relative directory and $newName isn't in the current directory | 
| 1976 | 0 | 0 | 0 |  |  | 0 | if ($file !~ m(^/) and $newName =~ m(/)) { | 
| 1977 | 0 | 0 |  |  |  | 0 | unless (eval { require Cwd }) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 1978 | 0 |  |  |  |  | 0 | $self->Warn('Install Cwd to make symlinks to other directories'); | 
| 1979 | 0 |  |  |  |  | 0 | return -1; | 
| 1980 |  |  |  |  |  |  | } | 
| 1981 | 0 |  |  |  |  | 0 | $file = eval { Cwd::abs_path($file) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1982 | 0 | 0 |  |  |  | 0 | unless (defined $file) { | 
| 1983 | 0 |  |  |  |  | 0 | $self->Warn('Error in Cwd::abs_path when creating symlink'); | 
| 1984 | 0 |  |  |  |  | 0 | return -1; | 
| 1985 |  |  |  |  |  |  | } | 
| 1986 |  |  |  |  |  |  | } | 
| 1987 | 0 | 0 |  |  |  | 0 | unless (eval { symlink $file, $newName } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 1988 | 0 |  |  |  |  | 0 | $self->Warn("Error creating symbolic link '${newName}'"); | 
| 1989 | 0 |  |  |  |  | 0 | return -1; | 
| 1990 |  |  |  |  |  |  | } | 
| 1991 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 1992 | 0 |  |  |  |  | 0 | $self->VerboseValue('+ SymLink', $newName); | 
| 1993 | 0 |  |  |  |  | 0 | return 1; | 
| 1994 |  |  |  |  |  |  | } | 
| 1995 |  |  |  |  |  |  | # attempt to rename the file | 
| 1996 | 1 | 50 |  |  |  | 10 | unless ($self->Rename($file, $newName)) { | 
| 1997 | 0 |  |  |  |  | 0 | local (*EXIFTOOL_SFN_IN, *EXIFTOOL_SFN_OUT); | 
| 1998 |  |  |  |  |  |  | # renaming didn't work, so copy the file instead | 
| 1999 | 0 | 0 |  |  |  | 0 | unless ($self->Open(\*EXIFTOOL_SFN_IN, $file)) { | 
| 2000 | 0 |  |  |  |  | 0 | $self->Error("Error opening '${file}'"); | 
| 2001 | 0 |  |  |  |  | 0 | return -1; | 
| 2002 |  |  |  |  |  |  | } | 
| 2003 | 0 | 0 |  |  |  | 0 | unless ($self->Open(\*EXIFTOOL_SFN_OUT, $newName, '>')) { | 
| 2004 | 0 |  |  |  |  | 0 | close EXIFTOOL_SFN_IN; | 
| 2005 | 0 |  |  |  |  | 0 | $self->Error("Error creating '${newName}'"); | 
| 2006 | 0 |  |  |  |  | 0 | return -1; | 
| 2007 |  |  |  |  |  |  | } | 
| 2008 | 0 |  |  |  |  | 0 | binmode EXIFTOOL_SFN_IN; | 
| 2009 | 0 |  |  |  |  | 0 | binmode EXIFTOOL_SFN_OUT; | 
| 2010 | 0 |  |  |  |  | 0 | my ($buff, $err); | 
| 2011 | 0 |  |  |  |  | 0 | while (read EXIFTOOL_SFN_IN, $buff, 65536) { | 
| 2012 | 0 | 0 |  |  |  | 0 | print EXIFTOOL_SFN_OUT $buff or $err = 1; | 
| 2013 |  |  |  |  |  |  | } | 
| 2014 | 0 | 0 |  |  |  | 0 | close EXIFTOOL_SFN_OUT or $err = 1; | 
| 2015 | 0 |  |  |  |  | 0 | close EXIFTOOL_SFN_IN; | 
| 2016 | 0 | 0 |  |  |  | 0 | if ($err) { | 
| 2017 | 0 |  |  |  |  | 0 | $self->Unlink($newName);    # erase bad output file | 
| 2018 | 0 |  |  |  |  | 0 | $self->Error("Error writing '${newName}'"); | 
| 2019 | 0 |  |  |  |  | 0 | return -1; | 
| 2020 |  |  |  |  |  |  | } | 
| 2021 |  |  |  |  |  |  | # preserve modification time | 
| 2022 | 0 |  |  |  |  | 0 | my ($aTime, $mTime, $cTime) = $self->GetFileTime($file); | 
| 2023 | 0 |  |  |  |  | 0 | $self->SetFileTime($newName, $aTime, $mTime, $cTime); | 
| 2024 |  |  |  |  |  |  | # remove the original file | 
| 2025 | 0 | 0 |  |  |  | 0 | $self->Unlink($file) or $self->Warn('Error removing old file'); | 
| 2026 |  |  |  |  |  |  | } | 
| 2027 | 1 |  |  |  |  | 7 | $$self{NewName} = $newName; # remember new file name | 
| 2028 | 1 |  |  |  |  | 4 | ++$$self{CHANGED}; | 
| 2029 | 1 |  |  |  |  | 7 | $self->VerboseValue('+ FileName', $newName); | 
| 2030 | 1 |  |  |  |  | 4 | return 1; | 
| 2031 |  |  |  |  |  |  | } | 
| 2032 |  |  |  |  |  |  |  | 
| 2033 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2034 |  |  |  |  |  |  | # Set file permissions, group/user id and various MDItem tags from new tag values | 
| 2035 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) file name or glob (must be a name for MDItem tags) | 
| 2036 |  |  |  |  |  |  | # Returns: 1=something was set OK, 0=didn't try, -1=error (and warning set) | 
| 2037 |  |  |  |  |  |  | # Notes: There may be errors even if 1 is returned | 
| 2038 |  |  |  |  |  |  | sub SetSystemTags($$) | 
| 2039 |  |  |  |  |  |  | { | 
| 2040 | 222 |  |  | 222 | 0 | 898 | my ($self, $file) = @_; | 
| 2041 | 222 |  |  |  |  | 642 | my $result = 0; | 
| 2042 |  |  |  |  |  |  |  | 
| 2043 | 222 |  |  |  |  | 932 | my $perm = $self->GetNewValue('FilePermissions'); | 
| 2044 | 222 | 50 |  |  |  | 1030 | if (defined $perm) { | 
| 2045 | 0 | 0 |  |  |  | 0 | if (eval { chmod($perm & 07777, $file) }) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 2046 | 0 |  |  |  |  | 0 | $self->VerboseValue('+ FilePermissions', $perm); | 
| 2047 | 0 |  |  |  |  | 0 | $result = 1; | 
| 2048 |  |  |  |  |  |  | } else { | 
| 2049 | 0 |  |  |  |  | 0 | $self->WarnOnce('Error setting FilePermissions'); | 
| 2050 | 0 |  |  |  |  | 0 | $result = -1; | 
| 2051 |  |  |  |  |  |  | } | 
| 2052 |  |  |  |  |  |  | } | 
| 2053 | 222 |  |  |  |  | 901 | my $uid = $self->GetNewValue('FileUserID'); | 
| 2054 | 222 |  |  |  |  | 1073 | my $gid = $self->GetNewValue('FileGroupID'); | 
| 2055 | 222 | 50 | 33 |  |  | 1933 | if (defined $uid or defined $gid) { | 
| 2056 | 0 | 0 |  |  |  | 0 | defined $uid or $uid = -1; | 
| 2057 | 0 | 0 |  |  |  | 0 | defined $gid or $gid = -1; | 
| 2058 | 0 | 0 |  |  |  | 0 | if (eval { chown($uid, $gid, $file) }) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 2059 | 0 | 0 |  |  |  | 0 | $self->VerboseValue('+ FileUserID', $uid) if $uid >= 0; | 
| 2060 | 0 | 0 |  |  |  | 0 | $self->VerboseValue('+ FileGroupID', $gid) if $gid >= 0; | 
| 2061 | 0 |  |  |  |  | 0 | $result = 1; | 
| 2062 |  |  |  |  |  |  | } else { | 
| 2063 | 0 |  |  |  |  | 0 | $self->WarnOnce('Error setting FileGroup/UserID'); | 
| 2064 | 0 | 0 |  |  |  | 0 | $result = -1 unless $result; | 
| 2065 |  |  |  |  |  |  | } | 
| 2066 |  |  |  |  |  |  | } | 
| 2067 | 222 |  |  |  |  | 668 | my $tag; | 
| 2068 | 222 |  |  |  |  | 834 | foreach $tag (@writableMacOSTags) { | 
| 2069 | 1332 |  |  |  |  | 1984 | my $nvHash; | 
| 2070 | 1332 |  |  |  |  | 3263 | my $val = $self->GetNewValue($tag, \$nvHash); | 
| 2071 | 1332 | 50 |  |  |  | 4702 | next unless $nvHash; | 
| 2072 | 0 | 0 |  |  |  | 0 | if ($^O eq 'darwin') { | 
|  |  | 0 |  |  |  |  |  | 
| 2073 | 0 | 0 |  |  |  | 0 | ref $file and $self->Warn('Setting MDItem tags requires a file name'), last; | 
| 2074 | 0 |  |  |  |  | 0 | require Image::ExifTool::MacOS; | 
| 2075 | 0 |  |  |  |  | 0 | my $res = Image::ExifTool::MacOS::SetMacOSTags($self, $file, \@writableMacOSTags); | 
| 2076 | 0 | 0 | 0 |  |  | 0 | $result = $res if $res == 1 or not $result; | 
| 2077 | 0 |  |  |  |  | 0 | last; | 
| 2078 |  |  |  |  |  |  | } elsif ($tag ne 'FileCreateDate') { | 
| 2079 | 0 |  |  |  |  | 0 | $self->WarnOnce('Can only set MDItem tags on OS X'); | 
| 2080 | 0 |  |  |  |  | 0 | last; | 
| 2081 |  |  |  |  |  |  | } | 
| 2082 |  |  |  |  |  |  | } | 
| 2083 |  |  |  |  |  |  | # delete Windows Zone.Identifier if specified | 
| 2084 | 222 |  |  |  |  | 1386 | my $zhash = $self->GetNewValueHash($Image::ExifTool::Extra{ZoneIdentifier}); | 
| 2085 | 222 | 50 |  |  |  | 1294 | if ($zhash) { | 
| 2086 | 0 |  |  |  |  | 0 | my $res = -1; | 
| 2087 | 0 | 0 |  |  |  | 0 | if ($^O ne 'MSWin32') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 2088 | 0 |  |  |  |  | 0 | $self->Warn('ZoneIdentifer is a Windows-only tag'); | 
| 2089 |  |  |  |  |  |  | } elsif (ref $file) { | 
| 2090 | 0 |  |  |  |  | 0 | $self->Warn('Writing ZoneIdentifer requires a file name'); | 
| 2091 |  |  |  |  |  |  | } elsif (defined $self->GetNewValue('ZoneIdentifier', \$zhash)) { | 
| 2092 | 0 |  |  |  |  | 0 | $self->Warn('ZoneIndentifier may only be delted'); | 
| 2093 | 0 |  |  |  |  | 0 | } elsif (not eval { require Win32API::File }) { | 
| 2094 | 0 |  |  |  |  | 0 | $self->Warn('Install Win32API::File to write ZoneIdentifier'); | 
| 2095 |  |  |  |  |  |  | } else { | 
| 2096 | 0 |  |  |  |  | 0 | my ($wattr, $wide); | 
| 2097 | 0 |  |  |  |  | 0 | my $zfile = "${file}:Zone.Identifier"; | 
| 2098 | 0 | 0 |  |  |  | 0 | if ($self->EncodeFileName($zfile)) { | 
| 2099 | 0 |  |  |  |  | 0 | $wide = 1; | 
| 2100 | 0 |  |  |  |  | 0 | $wattr = eval { Win32API::File::GetFileAttributesW($zfile) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2101 |  |  |  |  |  |  | } else { | 
| 2102 | 0 |  |  |  |  | 0 | $wattr = eval { Win32API::File::GetFileAttributes($zfile) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2103 |  |  |  |  |  |  | } | 
| 2104 | 0 | 0 |  |  |  | 0 | if ($wattr == Win32API::File::INVALID_FILE_ATTRIBUTES()) { | 
|  |  | 0 |  |  |  |  |  | 
| 2105 | 0 |  |  |  |  | 0 | $res = 0; # file doesn't exist, nothing to do | 
| 2106 |  |  |  |  |  |  | } elsif ($wattr & Win32API::File::FILE_ATTRIBUTE_READONLY()) { | 
| 2107 | 0 |  |  |  |  | 0 | $self->Warn('Zone.Identifier stream is read-only'); | 
| 2108 |  |  |  |  |  |  | } else { | 
| 2109 | 0 | 0 |  |  |  | 0 | if ($wide) { | 
| 2110 | 0 | 0 |  |  |  | 0 | $res = 1 if eval { Win32API::File::DeleteFileW($zfile) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2111 |  |  |  |  |  |  | } else { | 
| 2112 | 0 | 0 |  |  |  | 0 | $res = 1 if eval { Win32API::File::DeleteFile($zfile) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2113 |  |  |  |  |  |  | } | 
| 2114 | 0 | 0 |  |  |  | 0 | if ($res > 0) { | 
| 2115 | 0 |  |  |  |  | 0 | $self->VPrint(0, "  Deleting Zone.Identifier stream\n"); | 
| 2116 |  |  |  |  |  |  | } else { | 
| 2117 | 0 |  |  |  |  | 0 | $self->Warn('Error deleting Zone.Identifier stream'); | 
| 2118 |  |  |  |  |  |  | } | 
| 2119 |  |  |  |  |  |  | } | 
| 2120 |  |  |  |  |  |  | } | 
| 2121 | 0 | 0 | 0 |  |  | 0 | $result = $res if $res == 1 or not $result; | 
| 2122 |  |  |  |  |  |  | } | 
| 2123 | 222 |  |  |  |  | 1148 | return $result; | 
| 2124 |  |  |  |  |  |  | } | 
| 2125 |  |  |  |  |  |  |  | 
| 2126 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2127 |  |  |  |  |  |  | # Write information back to file | 
| 2128 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, | 
| 2129 |  |  |  |  |  |  | #         1) input filename, file ref, RAF ref, or scalar ref (or '' or undef to create from scratch) | 
| 2130 |  |  |  |  |  |  | #         2) output filename, file ref, or scalar ref (or undef to overwrite) | 
| 2131 |  |  |  |  |  |  | #         3) optional output file type (required only if input file is not specified | 
| 2132 |  |  |  |  |  |  | #            and output file is a reference) | 
| 2133 |  |  |  |  |  |  | # Returns: 1=file written OK, 2=file written but no changes made, 0=file write error | 
| 2134 |  |  |  |  |  |  | sub WriteInfo($$;$$) | 
| 2135 |  |  |  |  |  |  | { | 
| 2136 | 235 |  |  | 235 | 1 | 20793 | local ($_, *EXIFTOOL_FILE2, *EXIFTOOL_OUTFILE); | 
| 2137 | 235 |  |  |  |  | 1090 | my ($self, $infile, $outfile, $outType) = @_; | 
| 2138 | 235 |  |  |  |  | 1558 | my (@fileTypeList, $fileType, $tiffType, $hdr, $seekErr, $type, $tmpfile); | 
| 2139 | 235 |  |  |  |  | 0 | my ($inRef, $outRef, $closeIn, $closeOut, $outPos, $outBuff, $eraseIn, $raf, $fileExt); | 
| 2140 | 235 |  |  |  |  | 0 | my ($hardLink, $symLink, $testName); | 
| 2141 | 235 |  |  |  |  | 811 | my $oldRaf = $$self{RAF}; | 
| 2142 | 235 |  |  |  |  | 605 | my $rtnVal = 0; | 
| 2143 |  |  |  |  |  |  |  | 
| 2144 |  |  |  |  |  |  | # initialize member variables | 
| 2145 | 235 |  |  |  |  | 1488 | $self->Init(); | 
| 2146 | 235 |  |  |  |  | 986 | $$self{IsWriting} = 1; | 
| 2147 |  |  |  |  |  |  |  | 
| 2148 |  |  |  |  |  |  | # first, save original file modify date if necessary | 
| 2149 |  |  |  |  |  |  | # (do this now in case we are modifying file in place and shifting date) | 
| 2150 | 235 |  |  |  |  | 676 | my ($nvHash, $nvHash2, $originalTime, $createTime); | 
| 2151 | 235 |  |  |  |  | 1309 | my $setModDate = defined $self->GetNewValue('FileModifyDate', \$nvHash); | 
| 2152 | 235 |  |  |  |  | 1068 | my $setCreateDate = defined $self->GetNewValue('FileCreateDate', \$nvHash2); | 
| 2153 | 235 |  |  |  |  | 897 | my ($aTime, $mTime, $cTime); | 
| 2154 | 235 | 0 | 33 |  |  | 1284 | if ($setModDate and $self->IsOverwriting($nvHash) < 0 and | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 2155 |  |  |  |  |  |  | defined $infile and ref $infile ne 'SCALAR') | 
| 2156 |  |  |  |  |  |  | { | 
| 2157 | 0 |  |  |  |  | 0 | ($aTime, $mTime, $cTime) = $self->GetFileTime($infile); | 
| 2158 | 0 |  |  |  |  | 0 | $originalTime = $mTime; | 
| 2159 |  |  |  |  |  |  | } | 
| 2160 | 235 | 0 | 33 |  |  | 1114 | if ($setCreateDate and $self->IsOverwriting($nvHash2) < 0 and | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 2161 |  |  |  |  |  |  | defined $infile and ref $infile ne 'SCALAR') | 
| 2162 |  |  |  |  |  |  | { | 
| 2163 | 0 | 0 |  |  |  | 0 | ($aTime, $mTime, $cTime) = $self->GetFileTime($infile) unless defined $cTime; | 
| 2164 | 0 |  |  |  |  | 0 | $createTime = $cTime; | 
| 2165 |  |  |  |  |  |  | } | 
| 2166 |  |  |  |  |  |  | # | 
| 2167 |  |  |  |  |  |  | # do quick in-place change of file dir/name or date if that is all we are doing | 
| 2168 |  |  |  |  |  |  | # | 
| 2169 | 235 |  |  |  |  | 1220 | my ($numNew, $numPseudo) = $self->CountNewValues(); | 
| 2170 | 235 | 100 | 66 |  |  | 1252 | if (not defined $outfile and defined $infile) { | 
| 2171 | 4 |  |  |  |  | 16 | $hardLink = $self->GetNewValue('HardLink'); | 
| 2172 | 4 |  |  |  |  | 27 | $symLink = $self->GetNewValue('SymLink'); | 
| 2173 | 4 |  |  |  |  | 33 | $testName = $self->GetNewValue('TestName'); | 
| 2174 | 4 | 50 | 33 |  |  | 31 | undef $hardLink if defined $hardLink and not length $hardLink; | 
| 2175 | 4 | 50 | 33 |  |  | 19 | undef $symLink if defined $symLink and not length $symLink; | 
| 2176 | 4 | 50 | 33 |  |  | 22 | undef $testName if defined $testName and not length $testName; | 
| 2177 | 4 |  |  |  |  | 13 | my $newFileName =  $self->GetNewValue('FileName', \$nvHash); | 
| 2178 | 4 |  |  |  |  | 22 | my $newDir = $self->GetNewValue('Directory'); | 
| 2179 | 4 | 50 | 33 |  |  | 30 | if (defined $newDir and length $newDir) { | 
| 2180 | 0 | 0 |  |  |  | 0 | $newDir .= '/' unless $newDir =~ m{/$}; | 
| 2181 |  |  |  |  |  |  | } else { | 
| 2182 | 4 |  |  |  |  | 10 | undef $newDir; | 
| 2183 |  |  |  |  |  |  | } | 
| 2184 | 4 | 100 | 33 |  |  | 37 | if ($numNew == $numPseudo) { | 
|  |  | 50 |  |  |  |  |  | 
| 2185 | 1 |  |  |  |  | 3 | $rtnVal = 2; | 
| 2186 | 1 | 50 | 33 |  |  | 11 | if ((defined $newFileName or defined $newDir) and not ref $infile) { | 
|  |  |  | 33 |  |  |  |  | 
| 2187 | 1 |  |  |  |  | 7 | my $result = $self->SetFileName($infile); | 
| 2188 | 1 | 50 |  |  |  | 4 | if ($result > 0) { | 
|  |  | 0 |  |  |  |  |  | 
| 2189 | 1 |  |  |  |  | 5 | $infile = $$self{NewName};  # file name changed | 
| 2190 | 1 |  |  |  |  | 3 | $rtnVal = 1; | 
| 2191 |  |  |  |  |  |  | } elsif ($result < 0) { | 
| 2192 | 0 |  |  |  |  | 0 | return 0;   # don't try to do anything else | 
| 2193 |  |  |  |  |  |  | } | 
| 2194 |  |  |  |  |  |  | } | 
| 2195 | 1 | 50 | 33 |  |  | 6 | if (not ref $infile or UNIVERSAL::isa($infile,'GLOB')) { | 
| 2196 | 1 | 50 | 0 |  |  | 3 | $self->SetFileModifyDate($infile) > 0 and $rtnVal = 1 if $setModDate; | 
| 2197 | 1 | 50 | 0 |  |  | 3 | $self->SetFileModifyDate($infile, undef, 'FileCreateDate') > 0 and $rtnVal = 1 if $setCreateDate; | 
| 2198 | 1 | 50 |  |  |  | 5 | $self->SetSystemTags($infile) > 0 and $rtnVal = 1; | 
| 2199 |  |  |  |  |  |  | } | 
| 2200 | 1 | 50 | 33 |  |  | 12 | if (defined $hardLink or defined $symLink or defined $testName) { | 
|  |  |  | 33 |  |  |  |  | 
| 2201 | 0 | 0 | 0 |  |  | 0 | $hardLink and $self->SetFileName($infile, $hardLink, 'HardLink') and $rtnVal = 1; | 
| 2202 | 0 | 0 | 0 |  |  | 0 | $symLink and $self->SetFileName($infile, $symLink, 'SymLink') and $rtnVal = 1; | 
| 2203 | 0 | 0 | 0 |  |  | 0 | $testName and $self->SetFileName($infile, $testName, 'Test') and $rtnVal = 1; | 
| 2204 |  |  |  |  |  |  | } | 
| 2205 | 1 |  |  |  |  | 8 | return $rtnVal; | 
| 2206 |  |  |  |  |  |  | } elsif (defined $newFileName and length $newFileName) { | 
| 2207 |  |  |  |  |  |  | # can't simply rename file, so just set the output name if new FileName | 
| 2208 |  |  |  |  |  |  | # --> in this case, must erase original copy | 
| 2209 | 0 | 0 |  |  |  | 0 | if (ref $infile) { | 
|  |  | 0 |  |  |  |  |  | 
| 2210 | 0 |  |  |  |  | 0 | $outfile = $newFileName; | 
| 2211 |  |  |  |  |  |  | # can't delete original | 
| 2212 |  |  |  |  |  |  | } elsif ($self->IsOverwriting($nvHash, $infile)) { | 
| 2213 | 0 |  |  |  |  | 0 | $outfile = GetNewFileName($infile, $newFileName); | 
| 2214 | 0 |  |  |  |  | 0 | $eraseIn = 1; # delete original | 
| 2215 |  |  |  |  |  |  | } | 
| 2216 |  |  |  |  |  |  | } | 
| 2217 |  |  |  |  |  |  | # set new directory if specified | 
| 2218 | 3 | 50 |  |  |  | 15 | if (defined $newDir) { | 
| 2219 | 0 | 0 | 0 |  |  | 0 | $outfile = $infile unless defined $outfile or ref $infile; | 
| 2220 | 0 | 0 |  |  |  | 0 | if (defined $outfile) { | 
| 2221 | 0 |  |  |  |  | 0 | $outfile = GetNewFileName($outfile, $newDir); | 
| 2222 | 0 | 0 |  |  |  | 0 | $eraseIn = 1 unless ref $infile; | 
| 2223 |  |  |  |  |  |  | } | 
| 2224 |  |  |  |  |  |  | } | 
| 2225 |  |  |  |  |  |  | } | 
| 2226 |  |  |  |  |  |  | # | 
| 2227 |  |  |  |  |  |  | # set up input file | 
| 2228 |  |  |  |  |  |  | # | 
| 2229 | 234 | 100 | 66 |  |  | 5342 | if (ref $infile) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 2230 | 5 |  |  |  |  | 14 | $inRef = $infile; | 
| 2231 | 5 | 100 | 33 |  |  | 61 | if (UNIVERSAL::isa($inRef,'GLOB')) { | 
|  |  | 50 | 33 |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 2232 | 1 |  |  |  |  | 12 | seek($inRef, 0, 0); # make sure we are at the start of the file | 
| 2233 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($inRef,'File::RandomAccess')) { | 
| 2234 | 0 |  |  |  |  | 0 | $inRef->Seek(0); | 
| 2235 | 0 |  |  |  |  | 0 | $raf = $inRef; | 
| 2236 |  |  |  |  |  |  | } elsif ($] >= 5.006 and (eval { require Encode; Encode::is_utf8($$inRef) } or $@)) { | 
| 2237 |  |  |  |  |  |  | # convert image data from UTF-8 to character stream if necessary | 
| 2238 | 0 | 0 |  |  |  | 0 | my $buff = $@ ? pack('C*',unpack($] < 5.010000 ? 'U0C*' : 'C0C*',$$inRef)) : Encode::encode('utf8',$$inRef); | 
|  |  | 0 |  |  |  |  |  | 
| 2239 | 0 | 0 |  |  |  | 0 | if (defined $outfile) { | 
| 2240 | 0 |  |  |  |  | 0 | $inRef = \$buff; | 
| 2241 |  |  |  |  |  |  | } else { | 
| 2242 | 0 |  |  |  |  | 0 | $$inRef = $buff; | 
| 2243 |  |  |  |  |  |  | } | 
| 2244 |  |  |  |  |  |  | } | 
| 2245 |  |  |  |  |  |  | } elsif (defined $infile and $infile ne '') { | 
| 2246 |  |  |  |  |  |  | # write to a temporary file if no output file given | 
| 2247 | 206 | 100 |  |  |  | 843 | $outfile = $tmpfile = "${infile}_exiftool_tmp" unless defined $outfile; | 
| 2248 | 206 | 50 |  |  |  | 1576 | if ($self->Open(\*EXIFTOOL_FILE2, $infile)) { | 
| 2249 | 206 |  |  |  |  | 1671 | $fileExt = GetFileExtension($infile); | 
| 2250 | 206 |  |  |  |  | 1232 | $fileType = GetFileType($infile); | 
| 2251 | 206 |  |  |  |  | 831 | @fileTypeList = GetFileType($infile); | 
| 2252 | 206 |  |  |  |  | 900 | $tiffType = $$self{FILE_EXT} = GetFileExtension($infile); | 
| 2253 | 206 |  |  |  |  | 1864 | $self->VPrint(0, "Rewriting $infile...\n"); | 
| 2254 | 206 |  |  |  |  | 640 | $inRef = \*EXIFTOOL_FILE2; | 
| 2255 | 206 |  |  |  |  | 658 | $closeIn = 1;   # we must close the file since we opened it | 
| 2256 |  |  |  |  |  |  | } else { | 
| 2257 | 0 |  |  |  |  | 0 | $self->Error('Error opening file'); | 
| 2258 | 0 |  |  |  |  | 0 | return 0; | 
| 2259 |  |  |  |  |  |  | } | 
| 2260 |  |  |  |  |  |  | } elsif (not defined $outfile) { | 
| 2261 | 0 |  |  |  |  | 0 | $self->Error("WriteInfo(): Must specify infile or outfile\n"); | 
| 2262 | 0 |  |  |  |  | 0 | return 0; | 
| 2263 |  |  |  |  |  |  | } else { | 
| 2264 |  |  |  |  |  |  | # create file from scratch | 
| 2265 | 23 | 100 | 66 |  |  | 252 | $outType = GetFileExtension($outfile) unless $outType or ref $outfile; | 
| 2266 | 23 | 50 |  |  |  | 142 | if (CanCreate($outType)) { | 
|  |  | 0 |  |  |  |  |  | 
| 2267 | 23 | 50 |  |  |  | 186 | if ($$self{OPTIONS}{WriteMode} =~ /g/i) { | 
| 2268 | 23 |  |  |  |  | 79 | $fileType = $tiffType = $outType;   # use output file type if no input file | 
| 2269 | 23 |  |  |  |  | 83 | $infile = "$fileType file";         # make bogus file name | 
| 2270 | 23 |  |  |  |  | 179 | $self->VPrint(0, "Creating $infile...\n"); | 
| 2271 | 23 |  |  |  |  | 98 | $inRef = \ '';      # set $inRef to reference to empty data | 
| 2272 |  |  |  |  |  |  | } else { | 
| 2273 | 0 |  |  |  |  | 0 | $self->Error("Not creating new $outType file (disallowed by WriteMode)"); | 
| 2274 | 0 |  |  |  |  | 0 | return 0; | 
| 2275 |  |  |  |  |  |  | } | 
| 2276 |  |  |  |  |  |  | } elsif ($outType) { | 
| 2277 | 0 |  |  |  |  | 0 | $self->Error("Can't create $outType files"); | 
| 2278 | 0 |  |  |  |  | 0 | return 0; | 
| 2279 |  |  |  |  |  |  | } else { | 
| 2280 | 0 |  |  |  |  | 0 | $self->Error("Can't create file (unknown type)"); | 
| 2281 | 0 |  |  |  |  | 0 | return 0; | 
| 2282 |  |  |  |  |  |  | } | 
| 2283 |  |  |  |  |  |  | } | 
| 2284 | 234 | 100 |  |  |  | 1077 | unless (@fileTypeList) { | 
| 2285 | 29 | 100 |  |  |  | 104 | if ($fileType) { | 
| 2286 | 23 |  |  |  |  | 84 | @fileTypeList = ( $fileType ); | 
| 2287 |  |  |  |  |  |  | } else { | 
| 2288 | 6 |  |  |  |  | 121 | @fileTypeList = @fileTypes; | 
| 2289 | 6 |  |  |  |  | 18 | $tiffType = 'TIFF'; | 
| 2290 |  |  |  |  |  |  | } | 
| 2291 |  |  |  |  |  |  | } | 
| 2292 |  |  |  |  |  |  | # | 
| 2293 |  |  |  |  |  |  | # set up output file | 
| 2294 |  |  |  |  |  |  | # | 
| 2295 | 234 | 100 |  |  |  | 2272 | if (ref $outfile) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 2296 | 13 |  |  |  |  | 49 | $outRef = $outfile; | 
| 2297 | 13 | 50 |  |  |  | 79 | if (UNIVERSAL::isa($outRef,'GLOB')) { | 
| 2298 | 0 |  |  |  |  | 0 | binmode($outRef); | 
| 2299 | 0 |  |  |  |  | 0 | $outPos = tell($outRef); | 
| 2300 |  |  |  |  |  |  | } else { | 
| 2301 |  |  |  |  |  |  | # initialize our output buffer if necessary | 
| 2302 | 13 | 50 |  |  |  | 61 | defined $$outRef or $$outRef = ''; | 
| 2303 | 13 |  |  |  |  | 38 | $outPos = length($$outRef); | 
| 2304 |  |  |  |  |  |  | } | 
| 2305 |  |  |  |  |  |  | } elsif (not defined $outfile) { | 
| 2306 |  |  |  |  |  |  | # editing in place, so write to memory first | 
| 2307 |  |  |  |  |  |  | # (only when infile is a file ref or scalar ref) | 
| 2308 | 1 | 50 |  |  |  | 5 | if ($raf) { | 
| 2309 | 0 |  |  |  |  | 0 | $self->Error("Can't edit File::RandomAccess object in place"); | 
| 2310 | 0 |  |  |  |  | 0 | return 0; | 
| 2311 |  |  |  |  |  |  | } | 
| 2312 | 1 |  |  |  |  | 7 | $outBuff = ''; | 
| 2313 | 1 |  |  |  |  | 4 | $outRef = \$outBuff; | 
| 2314 | 1 |  |  |  |  | 3 | $outPos = 0; | 
| 2315 |  |  |  |  |  |  | } elsif ($self->Exists($outfile)) { | 
| 2316 | 0 |  |  |  |  | 0 | $self->Error("File already exists: $outfile"); | 
| 2317 |  |  |  |  |  |  | } elsif ($self->Open(\*EXIFTOOL_OUTFILE, $outfile, '>')) { | 
| 2318 | 220 |  |  |  |  | 1306 | $outRef = \*EXIFTOOL_OUTFILE; | 
| 2319 | 220 |  |  |  |  | 761 | $closeOut = 1;  # we must close $outRef | 
| 2320 | 220 |  |  |  |  | 1014 | binmode($outRef); | 
| 2321 | 220 |  |  |  |  | 640 | $outPos = 0; | 
| 2322 |  |  |  |  |  |  | } else { | 
| 2323 | 0 | 0 |  |  |  | 0 | my $tmp = $tmpfile ? ' temporary' : ''; | 
| 2324 | 0 |  |  |  |  | 0 | $self->Error("Error creating$tmp file: $outfile"); | 
| 2325 |  |  |  |  |  |  | } | 
| 2326 |  |  |  |  |  |  | # | 
| 2327 |  |  |  |  |  |  | # write the file | 
| 2328 |  |  |  |  |  |  | # | 
| 2329 | 234 |  |  |  |  | 1443 | until ($$self{VALUE}{Error}) { | 
| 2330 |  |  |  |  |  |  | # create random access file object (disable seek test in case of straight copy) | 
| 2331 | 234 | 50 |  |  |  | 3050 | $raf or $raf = new File::RandomAccess($inRef, 1); | 
| 2332 | 234 |  |  |  |  | 1360 | $raf->BinMode(); | 
| 2333 | 234 | 100 | 33 |  |  | 3123 | if ($numNew == $numPseudo) { | 
|  |  | 50 | 66 |  |  |  |  | 
| 2334 | 1 |  |  |  |  | 3 | $rtnVal = 1; | 
| 2335 |  |  |  |  |  |  | # just do a straight copy of the file (no "real" tags are being changed) | 
| 2336 | 1 |  |  |  |  | 2 | my $buff; | 
| 2337 | 1 |  |  |  |  | 8 | while ($raf->Read($buff, 65536)) { | 
| 2338 | 1 | 50 |  |  |  | 6 | Write($outRef, $buff) or $rtnVal = -1, last; | 
| 2339 |  |  |  |  |  |  | } | 
| 2340 | 1 |  |  |  |  | 4 | last; | 
| 2341 |  |  |  |  |  |  | } elsif (not ref $infile and ($infile eq '-' or $infile =~ /\|$/)) { | 
| 2342 |  |  |  |  |  |  | # patch for Windows command shell pipe | 
| 2343 | 0 |  |  |  |  | 0 | $$raf{TESTED} = -1; # force buffering | 
| 2344 |  |  |  |  |  |  | } else { | 
| 2345 | 233 |  |  |  |  | 1168 | $raf->SeekTest(); | 
| 2346 |  |  |  |  |  |  | } | 
| 2347 |  |  |  |  |  |  | # $raf->Debug() and warn "  RAF debugging enabled!\n"; | 
| 2348 | 233 |  |  |  |  | 1244 | my $inPos = $raf->Tell(); | 
| 2349 | 233 |  |  |  |  | 868 | $$self{RAF} = $raf; | 
| 2350 | 233 |  |  |  |  | 1280 | my %dirInfo = ( | 
| 2351 |  |  |  |  |  |  | RAF => $raf, | 
| 2352 |  |  |  |  |  |  | OutFile => $outRef, | 
| 2353 |  |  |  |  |  |  | ); | 
| 2354 | 233 | 100 |  |  |  | 1204 | $raf->Read($hdr, 1024) or $hdr = ''; | 
| 2355 | 233 | 50 |  |  |  | 1535 | $raf->Seek($inPos, 0) or $seekErr = 1; | 
| 2356 | 233 |  |  |  |  | 876 | my $wrongType; | 
| 2357 | 233 |  |  |  |  | 1036 | until ($seekErr) { | 
| 2358 | 268 |  |  |  |  | 775 | $type = shift @fileTypeList; | 
| 2359 |  |  |  |  |  |  | # do quick test to see if this is the right file type | 
| 2360 | 268 | 100 | 66 |  |  | 7164 | if ($magicNumber{$type} and length($hdr) and $hdr !~ /^$magicNumber{$type}/s) { | 
|  |  |  | 100 |  |  |  |  | 
| 2361 | 35 | 50 |  |  |  | 126 | next if @fileTypeList; | 
| 2362 | 0 |  |  |  |  | 0 | $wrongType = 1; | 
| 2363 | 0 |  |  |  |  | 0 | last; | 
| 2364 |  |  |  |  |  |  | } | 
| 2365 |  |  |  |  |  |  | # save file type in member variable | 
| 2366 | 233 |  |  |  |  | 1930 | $dirInfo{Parent} = $$self{FILE_TYPE} = $$self{PATH}[0] = $type; | 
| 2367 |  |  |  |  |  |  | # determine which directories we must write for this file type | 
| 2368 | 233 |  |  |  |  | 1732 | $self->InitWriteDirs($type); | 
| 2369 | 233 | 100 | 100 |  |  | 1992 | if ($type eq 'JPEG' or $type eq 'EXV') { | 
|  |  | 100 | 33 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 2370 | 106 |  |  |  |  | 688 | $rtnVal = $self->WriteJPEG(\%dirInfo); | 
| 2371 |  |  |  |  |  |  | } elsif ($type eq 'TIFF') { | 
| 2372 |  |  |  |  |  |  | # disallow writing of some TIFF-based RAW images: | 
| 2373 | 13 | 50 |  |  |  | 33 | if (grep /^$tiffType$/, @{$noWriteFile{TIFF}}) { | 
|  | 13 |  |  |  |  | 297 |  | 
| 2374 | 0 |  |  |  |  | 0 | $fileType = $tiffType; | 
| 2375 | 0 |  |  |  |  | 0 | undef $rtnVal; | 
| 2376 |  |  |  |  |  |  | } else { | 
| 2377 | 13 | 50 |  |  |  | 66 | if ($tiffType eq 'FFF') { | 
| 2378 |  |  |  |  |  |  | # (see https://exiftool.org/forum/index.php?topic=10848.0) | 
| 2379 | 0 |  |  |  |  | 0 | $self->Error('Phocus may not properly update previews of edited FFF images', 1); | 
| 2380 |  |  |  |  |  |  | } | 
| 2381 | 13 |  |  |  |  | 47 | $dirInfo{Parent} = $tiffType; | 
| 2382 | 13 |  |  |  |  | 94 | $rtnVal = $self->ProcessTIFF(\%dirInfo); | 
| 2383 |  |  |  |  |  |  | } | 
| 2384 | 0 |  |  |  |  | 0 | } elsif (exists $writableType{$type}) { | 
| 2385 | 112 |  |  |  |  | 320 | my ($module, $func); | 
| 2386 | 112 | 100 |  |  |  | 520 | if (ref $writableType{$type} eq 'ARRAY') { | 
| 2387 | 85 |  | 66 |  |  | 492 | $module = $writableType{$type}[0] || $type; | 
| 2388 | 85 |  |  |  |  | 323 | $func = $writableType{$type}[1]; | 
| 2389 |  |  |  |  |  |  | } else { | 
| 2390 | 27 |  | 66 |  |  | 146 | $module = $writableType{$type} || $type; | 
| 2391 |  |  |  |  |  |  | } | 
| 2392 | 112 |  |  |  |  | 1636 | require "Image/ExifTool/$module.pm"; | 
| 2393 | 112 |  | 66 |  |  | 718 | $func = "Image::ExifTool::${module}::" . ($func || "Process$type"); | 
| 2394 | 58 |  |  | 58 |  | 645 | no strict 'refs'; | 
|  | 58 |  |  |  |  | 153 |  | 
|  | 58 |  |  |  |  | 3299 |  | 
| 2395 | 112 |  |  |  |  | 1104 | $rtnVal = &$func($self, \%dirInfo); | 
| 2396 | 58 |  |  | 58 |  | 417 | use strict 'refs'; | 
|  | 58 |  |  |  |  | 320 |  | 
|  | 58 |  |  |  |  | 368356 |  | 
| 2397 |  |  |  |  |  |  | } elsif ($type eq 'ORF' or $type eq 'RAW') { | 
| 2398 | 0 |  |  |  |  | 0 | $rtnVal = $self->ProcessTIFF(\%dirInfo); | 
| 2399 |  |  |  |  |  |  | } elsif ($type eq 'EXIF') { | 
| 2400 |  |  |  |  |  |  | # go through WriteDirectory so block writes, etc are handled | 
| 2401 | 2 |  |  |  |  | 12 | my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main'); | 
| 2402 | 2 |  |  |  |  | 21 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF); | 
| 2403 | 2 | 50 |  |  |  | 13 | if (defined $buff) { | 
| 2404 | 2 | 50 |  |  |  | 10 | $rtnVal = Write($outRef, $buff) ? 1 : -1; | 
| 2405 |  |  |  |  |  |  | } else { | 
| 2406 | 0 |  |  |  |  | 0 | $rtnVal = 0; | 
| 2407 |  |  |  |  |  |  | } | 
| 2408 |  |  |  |  |  |  | } else { | 
| 2409 | 0 |  |  |  |  | 0 | undef $rtnVal;  # flag that we don't write this type of file | 
| 2410 |  |  |  |  |  |  | } | 
| 2411 |  |  |  |  |  |  | # all done unless we got the wrong type | 
| 2412 | 233 | 50 |  |  |  | 1255 | last if $rtnVal; | 
| 2413 | 0 | 0 |  |  |  | 0 | last unless @fileTypeList; | 
| 2414 |  |  |  |  |  |  | # seek back to original position in files for next try | 
| 2415 | 0 | 0 |  |  |  | 0 | $raf->Seek($inPos, 0) or $seekErr = 1, last; | 
| 2416 | 0 | 0 |  |  |  | 0 | if (UNIVERSAL::isa($outRef,'GLOB')) { | 
| 2417 | 0 |  |  |  |  | 0 | seek($outRef, 0, $outPos); | 
| 2418 |  |  |  |  |  |  | } else { | 
| 2419 | 0 |  |  |  |  | 0 | $$outRef = substr($$outRef, 0, $outPos); | 
| 2420 |  |  |  |  |  |  | } | 
| 2421 |  |  |  |  |  |  | } | 
| 2422 |  |  |  |  |  |  | # print file format errors | 
| 2423 | 233 | 50 |  |  |  | 941 | unless ($rtnVal) { | 
| 2424 | 0 |  |  |  |  | 0 | my $err; | 
| 2425 | 0 | 0 | 0 |  |  | 0 | if ($seekErr) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 2426 | 0 |  |  |  |  | 0 | $err = 'Error seeking in file'; | 
| 2427 |  |  |  |  |  |  | } elsif ($fileType and defined $rtnVal) { | 
| 2428 | 0 | 0 |  |  |  | 0 | if ($$self{VALUE}{Error}) { | 
|  |  | 0 |  |  |  |  |  | 
| 2429 |  |  |  |  |  |  | # existing error message will do | 
| 2430 |  |  |  |  |  |  | } elsif ($fileType eq 'RAW') { | 
| 2431 | 0 |  |  |  |  | 0 | $err = 'Writing this type of RAW file is not supported'; | 
| 2432 |  |  |  |  |  |  | } else { | 
| 2433 | 0 | 0 |  |  |  | 0 | if ($wrongType) { | 
| 2434 | 0 |  | 0 |  |  | 0 | my $type = $fileExt || ($fileType eq 'TIFF' ? $tiffType : $fileType); | 
| 2435 | 0 |  |  |  |  | 0 | $err = "Not a valid $type"; | 
| 2436 |  |  |  |  |  |  | # do a quick check to see what this file looks like | 
| 2437 | 0 |  |  |  |  | 0 | foreach $type (@fileTypes) { | 
| 2438 | 0 | 0 |  |  |  | 0 | next unless $magicNumber{$type}; | 
| 2439 | 0 | 0 |  |  |  | 0 | next unless $hdr =~ /^$magicNumber{$type}/s; | 
| 2440 | 0 |  |  |  |  | 0 | $err .= " (looks more like a $type)"; | 
| 2441 | 0 |  |  |  |  | 0 | last; | 
| 2442 |  |  |  |  |  |  | } | 
| 2443 |  |  |  |  |  |  | } else { | 
| 2444 | 0 |  |  |  |  | 0 | $err = 'Format error in file'; | 
| 2445 |  |  |  |  |  |  | } | 
| 2446 |  |  |  |  |  |  | } | 
| 2447 |  |  |  |  |  |  | } elsif ($fileType) { | 
| 2448 |  |  |  |  |  |  | # get specific type of file from extension | 
| 2449 | 0 | 0 | 0 |  |  | 0 | $fileType = GetFileExtension($infile) if $infile and GetFileType($infile); | 
| 2450 | 0 |  |  |  |  | 0 | $err = "Writing of $fileType files is not yet supported"; | 
| 2451 |  |  |  |  |  |  | } else { | 
| 2452 | 0 |  |  |  |  | 0 | $err = 'Writing of this type of file is not supported'; | 
| 2453 |  |  |  |  |  |  | } | 
| 2454 | 0 | 0 |  |  |  | 0 | $self->Error($err) if $err; | 
| 2455 | 0 |  |  |  |  | 0 | $rtnVal = 0;    # (in case it was undef) | 
| 2456 |  |  |  |  |  |  | } | 
| 2457 |  |  |  |  |  |  | # $raf->Close();  # only used to force debug output | 
| 2458 | 233 |  |  |  |  | 919 | last;   # (didn't really want to loop) | 
| 2459 |  |  |  |  |  |  | } | 
| 2460 |  |  |  |  |  |  | # don't return success code if any error occurred | 
| 2461 | 234 | 50 |  |  |  | 966 | if ($rtnVal > 0) { | 
| 2462 | 234 | 50 | 66 |  |  | 1422 | if ($outType and $type and $outType ne $type) { | 
|  |  |  | 66 |  |  |  |  | 
| 2463 | 0 |  |  |  |  | 0 | my @types = GetFileType($outType); | 
| 2464 | 0 | 0 |  |  |  | 0 | unless (grep /^$type$/, @types) { | 
| 2465 | 0 |  |  |  |  | 0 | $self->Error("Can't create $outType file from $type"); | 
| 2466 | 0 |  |  |  |  | 0 | $rtnVal = 0; | 
| 2467 |  |  |  |  |  |  | } | 
| 2468 |  |  |  |  |  |  | } | 
| 2469 | 234 | 50 | 33 |  |  | 1664 | if ($rtnVal > 0 and not Tell($outRef) and not $$self{VALUE}{Error}) { | 
|  |  |  | 33 |  |  |  |  | 
| 2470 |  |  |  |  |  |  | # don't write a file with zero length | 
| 2471 | 0 | 0 | 0 |  |  | 0 | if (defined $hdr and length $hdr) { | 
| 2472 | 0 | 0 |  |  |  | 0 | $type = '' unless defined $type; | 
| 2473 | 0 |  |  |  |  | 0 | $self->Error("Can't delete all meta information from $type file"); | 
| 2474 |  |  |  |  |  |  | } else { | 
| 2475 | 0 |  |  |  |  | 0 | $self->Error('Nothing to write'); | 
| 2476 |  |  |  |  |  |  | } | 
| 2477 |  |  |  |  |  |  | } | 
| 2478 | 234 | 50 |  |  |  | 1331 | $rtnVal = 0 if $$self{VALUE}{Error}; | 
| 2479 |  |  |  |  |  |  | } | 
| 2480 |  |  |  |  |  |  |  | 
| 2481 |  |  |  |  |  |  | # rewrite original file in place if required | 
| 2482 | 234 | 100 |  |  |  | 950 | if (defined $outBuff) { | 
| 2483 | 1 | 50 | 33 |  |  | 12 | if ($rtnVal <= 0 or not $$self{CHANGED}) { | 
|  |  | 50 |  |  |  |  |  | 
| 2484 |  |  |  |  |  |  | # nothing changed, so no need to write $outBuff | 
| 2485 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($inRef,'GLOB')) { | 
| 2486 | 1 |  |  |  |  | 3 | my $len = length($outBuff); | 
| 2487 | 1 |  |  |  |  | 3 | my $size; | 
| 2488 |  |  |  |  |  |  | $rtnVal = -1 unless | 
| 2489 |  |  |  |  |  |  | seek($inRef, 0, 2) and          # seek to the end of file | 
| 2490 |  |  |  |  |  |  | ($size = tell $inRef) >= 0 and  # get the file size | 
| 2491 |  |  |  |  |  |  | seek($inRef, 0, 0) and          # seek back to the start | 
| 2492 |  |  |  |  |  |  | print $inRef $outBuff and       # write the new data | 
| 2493 |  |  |  |  |  |  | ($len >= $size or               # if necessary: | 
| 2494 | 1 | 50 | 33 |  |  | 38 | eval { truncate($inRef, $len) }); #  shorten output file | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 2495 |  |  |  |  |  |  | } else { | 
| 2496 | 0 |  |  |  |  | 0 | $$inRef = $outBuff;                 # replace original data | 
| 2497 |  |  |  |  |  |  | } | 
| 2498 | 1 |  |  |  |  | 6 | $outBuff = '';  # free memory but leave $outBuff defined | 
| 2499 |  |  |  |  |  |  | } | 
| 2500 |  |  |  |  |  |  | # close input file if we opened it | 
| 2501 | 234 | 100 |  |  |  | 810 | if ($closeIn) { | 
| 2502 |  |  |  |  |  |  | # errors on input file are significant if we edited the file in place | 
| 2503 | 206 | 50 | 0 |  |  | 4681 | $rtnVal and $rtnVal = -1 unless close($inRef) or not defined $outBuff; | 
|  |  |  | 33 |  |  |  |  | 
| 2504 | 206 | 50 |  |  |  | 1091 | if ($rtnVal > 0) { | 
| 2505 |  |  |  |  |  |  | # copy Mac OS resource fork if it exists | 
| 2506 | 206 | 50 | 33 |  |  | 1365 | if ($^O eq 'darwin' and -s "$infile/..namedfork/rsrc") { | 
| 2507 | 0 | 0 |  |  |  | 0 | if ($$self{DEL_GROUP}{RSRC}) { | 
| 2508 | 0 |  |  |  |  | 0 | $self->VPrint(0,"Deleting Mac OS resource fork\n"); | 
| 2509 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 2510 |  |  |  |  |  |  | } else { | 
| 2511 | 0 |  |  |  |  | 0 | $self->VPrint(0,"Copying Mac OS resource fork\n"); | 
| 2512 | 0 |  |  |  |  | 0 | my ($buf, $err); | 
| 2513 | 0 |  |  |  |  | 0 | local (*SRC, *DST); | 
| 2514 | 0 | 0 |  |  |  | 0 | if ($self->Open(\*SRC, "$infile/..namedfork/rsrc")) { | 
| 2515 | 0 | 0 |  |  |  | 0 | if ($self->Open(\*DST, "$outfile/..namedfork/rsrc", '>')) { | 
| 2516 | 0 |  |  |  |  | 0 | binmode SRC; # (not necessary for Darwin, but let's be thorough) | 
| 2517 | 0 |  |  |  |  | 0 | binmode DST; | 
| 2518 | 0 |  |  |  |  | 0 | while (read SRC, $buf, 65536) { | 
| 2519 | 0 | 0 |  |  |  | 0 | print DST $buf or $err = 'copying', last; | 
| 2520 |  |  |  |  |  |  | } | 
| 2521 | 0 | 0 | 0 |  |  | 0 | close DST or $err or $err = 'closing'; | 
| 2522 |  |  |  |  |  |  | } else { | 
| 2523 |  |  |  |  |  |  | # (this is normal if the destination filesystem isn't Mac OS) | 
| 2524 | 0 |  |  |  |  | 0 | $self->Warn('Error creating Mac OS resource fork'); | 
| 2525 |  |  |  |  |  |  | } | 
| 2526 | 0 |  |  |  |  | 0 | close SRC; | 
| 2527 |  |  |  |  |  |  | } else { | 
| 2528 | 0 |  |  |  |  | 0 | $err = 'opening'; | 
| 2529 |  |  |  |  |  |  | } | 
| 2530 | 0 | 0 | 0 |  |  | 0 | $rtnVal = 0 if $err and $self->Error("Error $err Mac OS resource fork", 2); | 
| 2531 |  |  |  |  |  |  | } | 
| 2532 |  |  |  |  |  |  | } | 
| 2533 |  |  |  |  |  |  | # erase input file if renaming while editing information in place | 
| 2534 | 206 | 50 | 0 |  |  | 790 | $self->Unlink($infile) or $self->Warn('Error erasing original file') if $eraseIn; | 
| 2535 |  |  |  |  |  |  | } | 
| 2536 |  |  |  |  |  |  | } | 
| 2537 |  |  |  |  |  |  | # close output file if we created it | 
| 2538 | 234 | 100 |  |  |  | 774 | if ($closeOut) { | 
| 2539 |  |  |  |  |  |  | # close file and set $rtnVal to -1 if there was an error | 
| 2540 | 220 | 50 | 0 |  |  | 13679 | $rtnVal and $rtnVal = -1 unless close($outRef); | 
| 2541 |  |  |  |  |  |  | # erase the output file if we weren't successful | 
| 2542 | 220 | 50 |  |  |  | 1758 | if ($rtnVal <= 0) { | 
|  |  | 100 |  |  |  |  |  | 
| 2543 | 0 |  |  |  |  | 0 | $self->Unlink($outfile); | 
| 2544 |  |  |  |  |  |  | # else rename temporary file if necessary | 
| 2545 |  |  |  |  |  |  | } elsif ($tmpfile) { | 
| 2546 | 2 |  |  |  |  | 18 | $self->CopyFileAttrs($infile, $tmpfile);    # copy attributes to new file | 
| 2547 | 2 | 50 |  |  |  | 16 | unless ($self->Rename($tmpfile, $infile)) { | 
| 2548 |  |  |  |  |  |  | # some filesystems won't overwrite with 'rename', so try erasing original | 
| 2549 | 0 | 0 |  |  |  | 0 | if (not $self->Unlink($infile)) { | 
|  |  | 0 |  |  |  |  |  | 
| 2550 | 0 |  |  |  |  | 0 | $self->Unlink($tmpfile); | 
| 2551 | 0 |  |  |  |  | 0 | $self->Error('Error renaming temporary file'); | 
| 2552 | 0 |  |  |  |  | 0 | $rtnVal = 0; | 
| 2553 |  |  |  |  |  |  | } elsif (not $self->Rename($tmpfile, $infile)) { | 
| 2554 | 0 |  |  |  |  | 0 | $self->Error('Error renaming temporary file after deleting original'); | 
| 2555 | 0 |  |  |  |  | 0 | $rtnVal = 0; | 
| 2556 |  |  |  |  |  |  | } | 
| 2557 |  |  |  |  |  |  | } | 
| 2558 |  |  |  |  |  |  | # the output file should now have the name of the original infile | 
| 2559 | 2 | 50 |  |  |  | 12 | $outfile = $infile if $rtnVal > 0; | 
| 2560 |  |  |  |  |  |  | } | 
| 2561 |  |  |  |  |  |  | } | 
| 2562 |  |  |  |  |  |  | # set filesystem attributes if requested (and if possible!) | 
| 2563 | 234 | 50 | 100 |  |  | 1868 | if ($rtnVal > 0 and ($closeOut or (defined $outBuff and ($closeIn or UNIVERSAL::isa($infile,'GLOB'))))) { | 
|  |  |  | 66 |  |  |  |  | 
| 2564 | 221 | 100 |  |  |  | 938 | my $target = $closeOut ? $outfile : $infile; | 
| 2565 |  |  |  |  |  |  | # set file permissions if requested | 
| 2566 | 221 | 50 |  |  |  | 1460 | ++$$self{CHANGED} if $self->SetSystemTags($target) > 0; | 
| 2567 | 221 | 100 |  |  |  | 865 | if ($closeIn) { # (no use setting file times unless the input file is closed) | 
| 2568 | 197 | 50 | 33 |  |  | 926 | ++$$self{CHANGED} if $setModDate and $self->SetFileModifyDate($target, $originalTime, undef, 1) > 0; | 
| 2569 |  |  |  |  |  |  | # set FileCreateDate if requested (and if possible!) | 
| 2570 | 197 | 50 | 33 |  |  | 911 | ++$$self{CHANGED} if $setCreateDate and $self->SetFileModifyDate($target, $createTime, 'FileCreateDate', 1) > 0; | 
| 2571 |  |  |  |  |  |  | # create hard link if requested and no output filename specified (and if possible!) | 
| 2572 | 197 | 50 | 33 |  |  | 925 | ++$$self{CHANGED} if defined $hardLink and $self->SetFileName($target, $hardLink, 'HardLink'); | 
| 2573 | 197 | 50 | 33 |  |  | 904 | ++$$self{CHANGED} if defined $symLink and $self->SetFileName($target, $symLink, 'SymLink'); | 
| 2574 | 197 | 50 |  |  |  | 730 | defined $testName and $self->SetFileName($target, $testName, 'Test'); | 
| 2575 |  |  |  |  |  |  | } | 
| 2576 |  |  |  |  |  |  | } | 
| 2577 |  |  |  |  |  |  | # check for write error and set appropriate error message and return value | 
| 2578 | 234 | 50 |  |  |  | 1316 | if ($rtnVal < 0) { | 
|  |  | 50 |  |  |  |  |  | 
| 2579 | 0 | 0 |  |  |  | 0 | $self->Error('Error writing output file') unless $$self{VALUE}{Error}; | 
| 2580 | 0 |  |  |  |  | 0 | $rtnVal = 0;    # return 0 on failure | 
| 2581 |  |  |  |  |  |  | } elsif ($rtnVal > 0) { | 
| 2582 | 234 | 100 |  |  |  | 991 | ++$rtnVal unless $$self{CHANGED}; | 
| 2583 |  |  |  |  |  |  | } | 
| 2584 |  |  |  |  |  |  | # set things back to the way they were | 
| 2585 | 234 |  |  |  |  | 728 | $$self{RAF} = $oldRaf; | 
| 2586 |  |  |  |  |  |  |  | 
| 2587 | 234 |  |  |  |  | 2655 | return $rtnVal; | 
| 2588 |  |  |  |  |  |  | } | 
| 2589 |  |  |  |  |  |  |  | 
| 2590 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2591 |  |  |  |  |  |  | # Get list of all available tags for specified group | 
| 2592 |  |  |  |  |  |  | # Inputs: 0) optional group name (or string of names separated by colons) | 
| 2593 |  |  |  |  |  |  | # Returns: tag list (sorted alphabetically) | 
| 2594 |  |  |  |  |  |  | # Notes: Can't get tags for specific IFD | 
| 2595 |  |  |  |  |  |  | sub GetAllTags(;$) | 
| 2596 |  |  |  |  |  |  | { | 
| 2597 | 0 |  |  | 0 | 1 | 0 | local $_; | 
| 2598 | 0 |  |  |  |  | 0 | my $group = shift; | 
| 2599 | 0 |  |  |  |  | 0 | my (%allTags, @groups); | 
| 2600 | 0 | 0 |  |  |  | 0 | @groups = split ':', $group if $group; | 
| 2601 |  |  |  |  |  |  |  | 
| 2602 | 0 |  |  |  |  | 0 | my $et = new Image::ExifTool; | 
| 2603 | 0 |  |  |  |  | 0 | LoadAllTables();    # first load all our tables | 
| 2604 | 0 |  |  |  |  | 0 | my @tableNames = keys %allTables; | 
| 2605 |  |  |  |  |  |  |  | 
| 2606 |  |  |  |  |  |  | # loop through all tables and save tag names to %allTags hash | 
| 2607 | 0 |  |  |  |  | 0 | while (@tableNames) { | 
| 2608 | 0 |  |  |  |  | 0 | my $table = GetTagTable(pop @tableNames); | 
| 2609 |  |  |  |  |  |  | # generate flattened tag names for structure fields if this is an XMP table | 
| 2610 | 0 | 0 | 0 |  |  | 0 | if ($$table{GROUPS} and $$table{GROUPS}{0} eq 'XMP') { | 
| 2611 | 0 |  |  |  |  | 0 | Image::ExifTool::XMP::AddFlattenedTags($table); | 
| 2612 |  |  |  |  |  |  | } | 
| 2613 | 0 |  |  |  |  | 0 | my $tagID; | 
| 2614 | 0 |  |  |  |  | 0 | foreach $tagID (TagTableKeys($table)) { | 
| 2615 | 0 |  |  |  |  | 0 | my @infoArray = GetTagInfoList($table,$tagID); | 
| 2616 | 0 |  |  |  |  | 0 | my $tagInfo; | 
| 2617 | 0 |  |  |  |  | 0 | GATInfo:    foreach $tagInfo (@infoArray) { | 
| 2618 | 0 |  |  |  |  | 0 | my $tag = $$tagInfo{Name}; | 
| 2619 | 0 | 0 |  |  |  | 0 | $tag or warn("no name for tag!\n"), next; | 
| 2620 |  |  |  |  |  |  | # don't list subdirectories unless they are writable | 
| 2621 | 0 | 0 | 0 |  |  | 0 | next if $$tagInfo{SubDirectory} and not $$tagInfo{Writable}; | 
| 2622 | 0 | 0 |  |  |  | 0 | next if $$tagInfo{Hidden};  # ignore hidden tags | 
| 2623 | 0 | 0 |  |  |  | 0 | if (@groups) { | 
| 2624 | 0 |  |  |  |  | 0 | my @tg = $et->GetGroup($tagInfo); | 
| 2625 | 0 |  |  |  |  | 0 | foreach $group (@groups) { | 
| 2626 | 0 | 0 |  |  |  | 0 | next GATInfo unless grep /^$group$/i, @tg; | 
| 2627 |  |  |  |  |  |  | } | 
| 2628 |  |  |  |  |  |  | } | 
| 2629 | 0 |  |  |  |  | 0 | $allTags{$tag} = 1; | 
| 2630 |  |  |  |  |  |  | } | 
| 2631 |  |  |  |  |  |  | } | 
| 2632 |  |  |  |  |  |  | } | 
| 2633 | 0 |  |  |  |  | 0 | return sort keys %allTags; | 
| 2634 |  |  |  |  |  |  | } | 
| 2635 |  |  |  |  |  |  |  | 
| 2636 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2637 |  |  |  |  |  |  | # Get list of all writable tags | 
| 2638 |  |  |  |  |  |  | # Inputs: 0) optional group name (or names separated by colons) | 
| 2639 |  |  |  |  |  |  | # Returns: tag list (sorted alphabetically) | 
| 2640 |  |  |  |  |  |  | sub GetWritableTags(;$) | 
| 2641 |  |  |  |  |  |  | { | 
| 2642 | 0 |  |  | 0 | 1 | 0 | local $_; | 
| 2643 | 0 |  |  |  |  | 0 | my $group = shift; | 
| 2644 | 0 |  |  |  |  | 0 | my (%writableTags, @groups); | 
| 2645 | 0 | 0 |  |  |  | 0 | @groups = split ':', $group if $group; | 
| 2646 |  |  |  |  |  |  |  | 
| 2647 | 0 |  |  |  |  | 0 | my $et = new Image::ExifTool; | 
| 2648 | 0 |  |  |  |  | 0 | LoadAllTables(); | 
| 2649 | 0 |  |  |  |  | 0 | my @tableNames = keys %allTables; | 
| 2650 |  |  |  |  |  |  |  | 
| 2651 | 0 |  |  |  |  | 0 | while (@tableNames) { | 
| 2652 | 0 |  |  |  |  | 0 | my $tableName = pop @tableNames; | 
| 2653 | 0 |  |  |  |  | 0 | my $table = GetTagTable($tableName); | 
| 2654 |  |  |  |  |  |  | # generate flattened tag names for structure fields if this is an XMP table | 
| 2655 | 0 | 0 | 0 |  |  | 0 | if ($$table{GROUPS} and $$table{GROUPS}{0} eq 'XMP') { | 
| 2656 | 0 |  |  |  |  | 0 | Image::ExifTool::XMP::AddFlattenedTags($table); | 
| 2657 |  |  |  |  |  |  | } | 
| 2658 |  |  |  |  |  |  | # attempt to load Write tables if autoloaded | 
| 2659 | 0 |  |  |  |  | 0 | my @parts = split(/::/,$tableName); | 
| 2660 | 0 | 0 |  |  |  | 0 | if (@parts > 3) { | 
| 2661 | 0 |  |  |  |  | 0 | my $i = $#parts - 1; | 
| 2662 | 0 |  |  |  |  | 0 | $parts[$i] = "Write$parts[$i]";   # add 'Write' before class name | 
| 2663 | 0 |  |  |  |  | 0 | my $module = join('::',@parts[0..$i]); | 
| 2664 | 0 |  |  |  |  | 0 | eval { require $module }; # (fails silently if nothing loaded) | 
|  | 0 |  |  |  |  | 0 |  | 
| 2665 |  |  |  |  |  |  | } | 
| 2666 | 0 |  |  |  |  | 0 | my $tagID; | 
| 2667 | 0 |  |  |  |  | 0 | foreach $tagID (TagTableKeys($table)) { | 
| 2668 | 0 |  |  |  |  | 0 | my @infoArray = GetTagInfoList($table,$tagID); | 
| 2669 | 0 |  |  |  |  | 0 | my $tagInfo; | 
| 2670 | 0 |  |  |  |  | 0 | GWTInfo:    foreach $tagInfo (@infoArray) { | 
| 2671 | 0 |  |  |  |  | 0 | my $tag = $$tagInfo{Name}; | 
| 2672 | 0 | 0 |  |  |  | 0 | $tag or warn("no name for tag!\n"), next; | 
| 2673 | 0 |  |  |  |  | 0 | my $writable = $$tagInfo{Writable}; | 
| 2674 |  |  |  |  |  |  | next unless $writable or ($$table{WRITABLE} and | 
| 2675 | 0 | 0 | 0 |  |  | 0 | not defined $writable and not $$tagInfo{SubDirectory}); | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 2676 | 0 | 0 |  |  |  | 0 | next if $$tagInfo{Hidden};  # ignore hidden tags | 
| 2677 | 0 | 0 |  |  |  | 0 | if (@groups) { | 
| 2678 | 0 |  |  |  |  | 0 | my @tg = $et->GetGroup($tagInfo); | 
| 2679 | 0 |  |  |  |  | 0 | foreach $group (@groups) { | 
| 2680 | 0 | 0 |  |  |  | 0 | next GWTInfo unless grep /^$group$/i, @tg; | 
| 2681 |  |  |  |  |  |  | } | 
| 2682 |  |  |  |  |  |  | } | 
| 2683 | 0 |  |  |  |  | 0 | $writableTags{$tag} = 1; | 
| 2684 |  |  |  |  |  |  | } | 
| 2685 |  |  |  |  |  |  | } | 
| 2686 |  |  |  |  |  |  | } | 
| 2687 | 0 |  |  |  |  | 0 | return sort keys %writableTags; | 
| 2688 |  |  |  |  |  |  | } | 
| 2689 |  |  |  |  |  |  |  | 
| 2690 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2691 |  |  |  |  |  |  | # Get list of all group names | 
| 2692 |  |  |  |  |  |  | # Inputs: 0) [optional] ExifTool ref, 1) Group family number | 
| 2693 |  |  |  |  |  |  | # Returns: List of group names (sorted alphabetically) | 
| 2694 |  |  |  |  |  |  | sub GetAllGroups($;$) | 
| 2695 |  |  |  |  |  |  | { | 
| 2696 | 0 |  |  | 0 | 1 | 0 | local $_; | 
| 2697 | 0 |  | 0 |  |  | 0 | my $family = shift || 0; | 
| 2698 | 0 |  |  |  |  | 0 | my $self; | 
| 2699 | 0 | 0 | 0 |  |  | 0 | ref $family and $self = $family, $family = shift || 0; | 
| 2700 |  |  |  |  |  |  |  | 
| 2701 | 0 | 0 |  |  |  | 0 | $family == 3 and return('Doc#', 'Main'); | 
| 2702 | 0 | 0 |  |  |  | 0 | $family == 4 and return('Copy#'); | 
| 2703 | 0 | 0 |  |  |  | 0 | $family == 5 and return('[too many possibilities to list]'); | 
| 2704 | 0 | 0 |  |  |  | 0 | $family == 6 and return(@Image::ExifTool::Exif::formatName[1..$#Image::ExifTool::Exif::formatName]); | 
| 2705 |  |  |  |  |  |  |  | 
| 2706 | 0 |  |  |  |  | 0 | LoadAllTables();    # first load all our tables | 
| 2707 |  |  |  |  |  |  |  | 
| 2708 | 0 |  |  |  |  | 0 | my @tableNames = keys %allTables; | 
| 2709 |  |  |  |  |  |  |  | 
| 2710 |  |  |  |  |  |  | # loop through all tag tables and get all group names | 
| 2711 | 0 |  |  |  |  | 0 | my %allGroups; | 
| 2712 | 0 |  |  |  |  | 0 | while (@tableNames) { | 
| 2713 | 0 |  |  |  |  | 0 | my $table = GetTagTable(pop @tableNames); | 
| 2714 | 0 |  |  |  |  | 0 | my ($grps, $grp, $tag, $tagInfo); | 
| 2715 | 0 | 0 | 0 |  |  | 0 | $allGroups{$grp} = 1 if ($grps = $$table{GROUPS}) and ($grp = $$grps{$family}); | 
| 2716 | 0 |  |  |  |  | 0 | foreach $tag (TagTableKeys($table)) { | 
| 2717 | 0 |  |  |  |  | 0 | my @infoArray = GetTagInfoList($table, $tag); | 
| 2718 | 0 | 0 |  |  |  | 0 | if ($family == 7) { | 
| 2719 | 0 |  |  |  |  | 0 | foreach $tagInfo (@infoArray) { | 
| 2720 | 0 |  |  |  |  | 0 | my $id = $$tagInfo{TagID}; | 
| 2721 | 0 | 0 |  |  |  | 0 | if (not defined $id) { | 
|  |  | 0 |  |  |  |  |  | 
| 2722 | 0 |  |  |  |  | 0 | $id = '';   # (just to be safe) | 
| 2723 |  |  |  |  |  |  | } elsif ($id =~ /^\d+$/) { | 
| 2724 | 0 | 0 | 0 |  |  | 0 | $id = sprintf('0x%x', $id) if $self and $$self{OPTIONS}{HexTagIDs}; | 
| 2725 |  |  |  |  |  |  | } else { | 
| 2726 | 0 |  |  |  |  | 0 | $id =~ s/([^-_A-Za-z0-9])/sprintf('%.2x',ord $1)/ge; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2727 |  |  |  |  |  |  | } | 
| 2728 | 0 |  |  |  |  | 0 | $allGroups{'ID-' . $id} = 1; | 
| 2729 |  |  |  |  |  |  | } | 
| 2730 |  |  |  |  |  |  | } else { | 
| 2731 | 0 |  |  |  |  | 0 | foreach $tagInfo (@infoArray) { | 
| 2732 | 0 | 0 | 0 |  |  | 0 | next unless ($grps = $$tagInfo{Groups}) and ($grp = $$grps{$family}); | 
| 2733 | 0 |  |  |  |  | 0 | $allGroups{$grp} = 1; | 
| 2734 |  |  |  |  |  |  | } | 
| 2735 |  |  |  |  |  |  | } | 
| 2736 |  |  |  |  |  |  | } | 
| 2737 |  |  |  |  |  |  | } | 
| 2738 | 0 |  |  |  |  | 0 | delete $allGroups{'*'};     # (not a real group) | 
| 2739 | 0 |  |  |  |  | 0 | return sort keys %allGroups; | 
| 2740 |  |  |  |  |  |  | } | 
| 2741 |  |  |  |  |  |  |  | 
| 2742 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2743 |  |  |  |  |  |  | # get priority group list for new values | 
| 2744 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 2745 |  |  |  |  |  |  | # Returns: List of group names | 
| 2746 |  |  |  |  |  |  | sub GetNewGroups($) | 
| 2747 |  |  |  |  |  |  | { | 
| 2748 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 2749 | 0 |  |  |  |  | 0 | return @{$$self{WRITE_GROUPS}}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2750 |  |  |  |  |  |  | } | 
| 2751 |  |  |  |  |  |  |  | 
| 2752 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2753 |  |  |  |  |  |  | # Get list of all deletable group names | 
| 2754 |  |  |  |  |  |  | # Returns: List of group names (sorted alphabetically) | 
| 2755 |  |  |  |  |  |  | sub GetDeleteGroups() | 
| 2756 |  |  |  |  |  |  | { | 
| 2757 | 0 |  |  | 0 | 1 | 0 | return sort @delGroups, @delGroup2; | 
| 2758 |  |  |  |  |  |  | } | 
| 2759 |  |  |  |  |  |  |  | 
| 2760 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2761 |  |  |  |  |  |  | # Add user-defined tags at run time | 
| 2762 |  |  |  |  |  |  | # Inputs: 0) destination table name, 1) tagID/tagInfo pairs for tags to add | 
| 2763 |  |  |  |  |  |  | # Returns: number of tags added | 
| 2764 |  |  |  |  |  |  | # Notes: will replace existing tags | 
| 2765 |  |  |  |  |  |  | sub AddUserDefinedTags($%) | 
| 2766 |  |  |  |  |  |  | { | 
| 2767 | 1 |  |  | 1 | 1 | 239 | local $_; | 
| 2768 | 1 |  |  |  |  | 8 | my ($tableName, %addTags) = @_; | 
| 2769 | 1 | 50 |  |  |  | 5 | my $table = GetTagTable($tableName) or return 0; | 
| 2770 |  |  |  |  |  |  | # add tags to writer lookup | 
| 2771 | 1 |  |  |  |  | 9 | Image::ExifTool::TagLookup::AddTags(\%addTags, $tableName); | 
| 2772 | 1 |  |  |  |  | 3 | my $tagID; | 
| 2773 | 1 |  |  |  |  | 7 | my $num = 0; | 
| 2774 | 1 |  |  |  |  | 3 | foreach $tagID (keys %addTags) { | 
| 2775 | 1 | 50 |  |  |  | 8 | next if $specialTags{$tagID}; | 
| 2776 | 1 |  |  |  |  | 4 | delete $$table{$tagID}; # delete old entry if it existed | 
| 2777 | 1 |  |  |  |  | 13 | AddTagToTable($table, $tagID, $addTags{$tagID}, 1); | 
| 2778 | 1 |  |  |  |  | 4 | ++$num; | 
| 2779 |  |  |  |  |  |  | } | 
| 2780 | 1 |  |  |  |  | 10 | return $num; | 
| 2781 |  |  |  |  |  |  | } | 
| 2782 |  |  |  |  |  |  |  | 
| 2783 |  |  |  |  |  |  | #============================================================================== | 
| 2784 |  |  |  |  |  |  | # Functions below this are not part of the public API | 
| 2785 |  |  |  |  |  |  |  | 
| 2786 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2787 |  |  |  |  |  |  | # Maintain backward compatibility for old GetNewValues function name | 
| 2788 |  |  |  |  |  |  | sub GetNewValues($$;$) | 
| 2789 |  |  |  |  |  |  | { | 
| 2790 | 0 |  |  | 0 | 0 | 0 | my ($self, $tag, $nvHashPt) = @_; | 
| 2791 | 0 |  |  |  |  | 0 | return $self->GetNewValue($tag, $nvHashPt); | 
| 2792 |  |  |  |  |  |  | } | 
| 2793 |  |  |  |  |  |  |  | 
| 2794 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2795 |  |  |  |  |  |  | # Un-escape string according to options settings and clear UTF-8 flag | 
| 2796 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) string ref or string ref ref | 
| 2797 |  |  |  |  |  |  | # Notes: also de-references SCALAR values | 
| 2798 |  |  |  |  |  |  | sub Sanitize($$) | 
| 2799 |  |  |  |  |  |  | { | 
| 2800 | 5409 |  |  | 5409 | 0 | 12284 | my ($self, $valPt) = @_; | 
| 2801 |  |  |  |  |  |  | # de-reference SCALAR references | 
| 2802 | 5409 | 50 |  |  |  | 13399 | $$valPt = $$$valPt if ref $$valPt eq 'SCALAR'; | 
| 2803 |  |  |  |  |  |  | # make sure the Perl UTF-8 flag is OFF for the value if perl 5.6 or greater | 
| 2804 |  |  |  |  |  |  | # (otherwise our byte manipulations get corrupted!!) | 
| 2805 | 5409 | 50 | 33 |  |  | 15232 | if ($] >= 5.006 and (eval { require Encode; Encode::is_utf8($$valPt) } or $@)) { | 
|  |  |  | 33 |  |  |  |  | 
| 2806 | 0 |  |  |  |  | 0 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 2807 |  |  |  |  |  |  | # repack by hand if Encode isn't available | 
| 2808 | 0 | 0 |  |  |  | 0 | $$valPt = $@ ? pack('C*',unpack($] < 5.010000 ? 'U0C*' : 'C0C*',$$valPt)) : Encode::encode('utf8',$$valPt); | 
|  |  | 0 |  |  |  |  |  | 
| 2809 |  |  |  |  |  |  | } | 
| 2810 |  |  |  |  |  |  | # un-escape value if necessary | 
| 2811 | 5409 | 100 |  |  |  | 19766 | if ($$self{OPTIONS}{Escape}) { | 
| 2812 |  |  |  |  |  |  | # (XMP.pm and HTML.pm were require'd as necessary when option was set) | 
| 2813 | 92 | 50 |  |  |  | 325 | if ($$self{OPTIONS}{Escape} eq 'XML') { | 
|  |  | 50 |  |  |  |  |  | 
| 2814 | 0 |  |  |  |  | 0 | $$valPt = Image::ExifTool::XMP::UnescapeXML($$valPt); | 
| 2815 |  |  |  |  |  |  | } elsif ($$self{OPTIONS}{Escape} eq 'HTML') { | 
| 2816 | 92 |  |  |  |  | 297 | $$valPt = Image::ExifTool::HTML::UnescapeHTML($$valPt, $$self{OPTIONS}{Charset}); | 
| 2817 |  |  |  |  |  |  | } | 
| 2818 |  |  |  |  |  |  | } | 
| 2819 |  |  |  |  |  |  | } | 
| 2820 |  |  |  |  |  |  |  | 
| 2821 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2822 |  |  |  |  |  |  | # Apply inverse conversions | 
| 2823 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) value, 2) tagInfo (or Struct item) ref, | 
| 2824 |  |  |  |  |  |  | #         3) tag name, 4) group 1 name, 5) conversion type (or undef), | 
| 2825 |  |  |  |  |  |  | #         6) [optional] want group ("" for structure field) | 
| 2826 |  |  |  |  |  |  | # Returns: 0) converted value, 1) error string (or undef on success) | 
| 2827 |  |  |  |  |  |  | # Notes: | 
| 2828 |  |  |  |  |  |  | # - uses ExifTool "ConvType" member when conversion type is undef | 
| 2829 |  |  |  |  |  |  | # - conversion types other than 'ValueConv' and 'PrintConv' are treated as 'Raw' | 
| 2830 |  |  |  |  |  |  | sub ConvInv($$$$$;$$) | 
| 2831 |  |  |  |  |  |  | { | 
| 2832 | 27960 |  |  | 27960 | 0 | 71000 | my ($self, $val, $tagInfo, $tag, $wgrp1, $convType, $wantGroup) = @_; | 
| 2833 | 27960 |  |  |  |  | 43381 | my ($err, $type); | 
| 2834 |  |  |  |  |  |  |  | 
| 2835 | 27960 | 100 | 50 |  |  | 63506 | $convType or $convType = $$self{ConvType} || 'PrintConv'; | 
| 2836 |  |  |  |  |  |  |  | 
| 2837 | 27960 |  |  |  |  | 46561 | Conv: for (;;) { | 
| 2838 | 72507 | 100 |  |  |  | 168952 | if (not defined $type) { | 
|  |  | 100 |  |  |  |  |  | 
| 2839 |  |  |  |  |  |  | # split value into list if necessary | 
| 2840 | 27960 | 100 |  |  |  | 64919 | if ($$tagInfo{List}) { | 
| 2841 | 569 |  | 100 |  |  | 2883 | my $listSplit = $$tagInfo{AutoSplit} || $$self{OPTIONS}{ListSplit}; | 
| 2842 | 569 | 50 | 100 |  |  | 2330 | if (defined $listSplit and not $$tagInfo{Struct} and | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 2843 |  |  |  |  |  |  | ($wantGroup or not defined $wantGroup)) | 
| 2844 |  |  |  |  |  |  | { | 
| 2845 | 74 | 50 | 66 |  |  | 575 | $listSplit = ',?\s+' if $listSplit eq '1' and $$tagInfo{AutoSplit}; | 
| 2846 | 74 |  |  |  |  | 844 | my @splitVal = split /$listSplit/, $val, -1; | 
| 2847 | 74 | 50 |  |  |  | 467 | $val = @splitVal > 1 ? \@splitVal : @splitVal ? $splitVal[0] : ''; | 
|  |  | 100 |  |  |  |  |  | 
| 2848 |  |  |  |  |  |  | } | 
| 2849 |  |  |  |  |  |  | } | 
| 2850 | 27960 |  |  |  |  | 45817 | $type = $convType; | 
| 2851 |  |  |  |  |  |  | } elsif ($type eq 'PrintConv') { | 
| 2852 | 21604 |  |  |  |  | 40282 | $type = 'ValueConv'; | 
| 2853 |  |  |  |  |  |  | } else { | 
| 2854 |  |  |  |  |  |  | # split raw value if necessary | 
| 2855 | 22943 | 50 | 66 |  |  | 55425 | if ($$tagInfo{RawJoin} and $$tagInfo{List} and not ref $val) { | 
|  |  |  | 33 |  |  |  |  | 
| 2856 | 13 |  |  |  |  | 84 | my @splitVal = split ' ', $val; | 
| 2857 | 13 | 50 |  |  |  | 82 | $val = \@splitVal if @splitVal > 1; | 
| 2858 |  |  |  |  |  |  | } | 
| 2859 |  |  |  |  |  |  | # finally, do our value check | 
| 2860 | 22943 |  |  |  |  | 38203 | my ($err2, $v); | 
| 2861 | 22943 | 100 |  |  |  | 51855 | if ($$tagInfo{WriteCheck}) { | 
| 2862 |  |  |  |  |  |  | #### eval WriteCheck ($self, $tagInfo, $val) | 
| 2863 | 275 |  |  |  |  | 21795 | $err2 = eval $$tagInfo{WriteCheck}; | 
| 2864 | 275 | 50 |  |  |  | 1428 | $@ and warn($@), $err2 = 'Error evaluating WriteCheck'; | 
| 2865 |  |  |  |  |  |  | } | 
| 2866 | 22943 | 100 |  |  |  | 46382 | unless ($err2) { | 
| 2867 | 22909 |  |  |  |  | 42900 | my $table = $$tagInfo{Table}; | 
| 2868 | 22909 | 100 | 100 |  |  | 130381 | if ($table and $$table{CHECK_PROC} and not $$tagInfo{RawConvInv}) { | 
|  |  |  | 100 |  |  |  |  | 
| 2869 | 22040 |  |  |  |  | 38494 | my $checkProc = $$table{CHECK_PROC}; | 
| 2870 | 22040 | 100 |  |  |  | 44264 | if (ref $val eq 'ARRAY') { | 
| 2871 |  |  |  |  |  |  | # loop through array values | 
| 2872 | 46 |  |  |  |  | 167 | foreach $v (@$val) { | 
| 2873 | 136 |  |  |  |  | 404 | $err2 = &$checkProc($self, $tagInfo, \$v, $convType); | 
| 2874 | 136 | 50 |  |  |  | 414 | last if $err2; | 
| 2875 |  |  |  |  |  |  | } | 
| 2876 |  |  |  |  |  |  | } else { | 
| 2877 | 21994 |  |  |  |  | 67718 | $err2 = &$checkProc($self, $tagInfo, \$val, $convType); | 
| 2878 |  |  |  |  |  |  | } | 
| 2879 |  |  |  |  |  |  | } | 
| 2880 |  |  |  |  |  |  | } | 
| 2881 | 22943 | 100 |  |  |  | 60280 | if (defined $err2) { | 
| 2882 | 3315 | 100 |  |  |  | 7179 | if ($err2) { | 
| 2883 | 3307 |  |  |  |  | 8556 | $err = "$err2 for $wgrp1:$tag"; | 
| 2884 | 3307 |  |  |  |  | 13974 | $self->VPrint(2, "$err\n"); | 
| 2885 | 3307 |  |  |  |  | 7252 | undef $val;     # value was invalid | 
| 2886 |  |  |  |  |  |  | } else { | 
| 2887 | 8 |  |  |  |  | 18 | $err = $err2;   # empty error (quietly don't write tag) | 
| 2888 |  |  |  |  |  |  | } | 
| 2889 |  |  |  |  |  |  | } | 
| 2890 | 22943 |  |  |  |  | 42954 | last; | 
| 2891 |  |  |  |  |  |  | } | 
| 2892 | 49564 |  |  |  |  | 95463 | my $conv = $$tagInfo{$type}; | 
| 2893 | 49564 |  |  |  |  | 114084 | my $convInv = $$tagInfo{"${type}Inv"}; | 
| 2894 |  |  |  |  |  |  | # nothing to do at this level if no conversion defined | 
| 2895 | 49564 | 100 | 100 |  |  | 140621 | next unless defined $conv or defined $convInv; | 
| 2896 |  |  |  |  |  |  |  | 
| 2897 | 22410 |  |  |  |  | 40426 | my (@valList, $index, $convList, $convInvList); | 
| 2898 | 22410 | 100 | 66 |  |  | 92957 | if (ref $val eq 'ARRAY') { | 
|  |  | 100 |  |  |  |  |  | 
| 2899 |  |  |  |  |  |  | # handle ValueConv of ListSplit and AutoSplit values | 
| 2900 | 12 |  |  |  |  | 62 | @valList = @$val; | 
| 2901 | 12 |  |  |  |  | 60 | $val = $valList[$index = 0]; | 
| 2902 |  |  |  |  |  |  | } elsif (ref $conv eq 'ARRAY' or ref $convInv eq 'ARRAY') { | 
| 2903 |  |  |  |  |  |  | # handle conversion lists | 
| 2904 | 153 |  |  |  |  | 1454 | @valList = split /$listSep{$type}/, $val; | 
| 2905 | 153 |  |  |  |  | 436 | $val = $valList[$index = 0]; | 
| 2906 | 153 | 50 |  |  |  | 579 | if (ref $conv eq 'ARRAY') { | 
| 2907 | 153 |  |  |  |  | 345 | $convList = $conv; | 
| 2908 | 153 |  |  |  |  | 480 | $conv = $$conv[0]; | 
| 2909 |  |  |  |  |  |  | } | 
| 2910 | 153 | 100 |  |  |  | 531 | if (ref $convInv eq 'ARRAY') { | 
| 2911 | 29 |  |  |  |  | 61 | $convInvList = $convInv; | 
| 2912 | 29 |  |  |  |  | 73 | $convInv = $$convInv[0]; | 
| 2913 |  |  |  |  |  |  | } | 
| 2914 |  |  |  |  |  |  | } | 
| 2915 |  |  |  |  |  |  | # loop through multiple values if necessary | 
| 2916 | 22410 |  |  |  |  | 32834 | for (;;) { | 
| 2917 | 22462 | 100 |  |  |  | 49613 | if ($convInv) { | 
|  |  | 100 |  |  |  |  |  | 
| 2918 |  |  |  |  |  |  | # capture eval warnings too | 
| 2919 | 13651 |  |  |  |  | 61516 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 2920 | 13651 |  |  |  |  | 28234 | undef $evalWarning; | 
| 2921 | 13651 | 100 |  |  |  | 27577 | if (ref($convInv) eq 'CODE') { | 
| 2922 | 131 |  |  |  |  | 657 | $val = &$convInv($val, $self); | 
| 2923 |  |  |  |  |  |  | } else { | 
| 2924 |  |  |  |  |  |  | #### eval PrintConvInv/ValueConvInv ($val, $self, $wantGroup) | 
| 2925 | 13520 |  |  |  |  | 875125 | $val = eval $convInv; | 
| 2926 | 13520 | 100 |  |  |  | 57876 | $@ and $evalWarning = $@; | 
| 2927 |  |  |  |  |  |  | } | 
| 2928 | 13651 | 100 |  |  |  | 60334 | if ($evalWarning) { | 
|  |  | 100 |  |  |  |  |  | 
| 2929 |  |  |  |  |  |  | # an empty warning ("\n") ignores tag with no error | 
| 2930 | 223 | 100 |  |  |  | 681 | if ($evalWarning eq "\n") { | 
| 2931 | 9 | 50 |  |  |  | 40 | $err = '' unless defined $err; | 
| 2932 |  |  |  |  |  |  | } else { | 
| 2933 | 214 |  |  |  |  | 785 | $err = CleanWarning() . " in $wgrp1:$tag (${type}Inv)"; | 
| 2934 | 214 |  |  |  |  | 1013 | $self->VPrint(2, "$err\n"); | 
| 2935 |  |  |  |  |  |  | } | 
| 2936 | 223 |  |  |  |  | 511 | undef $val; | 
| 2937 | 223 |  |  |  |  | 1155 | last Conv; | 
| 2938 |  |  |  |  |  |  | } elsif (not defined $val) { | 
| 2939 | 124 |  |  |  |  | 609 | $err = "Error converting value for $wgrp1:$tag (${type}Inv)"; | 
| 2940 | 124 |  |  |  |  | 705 | $self->VPrint(2, "$err\n"); | 
| 2941 | 124 |  |  |  |  | 584 | last Conv; | 
| 2942 |  |  |  |  |  |  | } | 
| 2943 |  |  |  |  |  |  | } elsif ($conv) { | 
| 2944 | 8808 | 100 | 66 |  |  | 44912 | if (ref $conv eq 'HASH' and (not exists $$tagInfo{"${type}Inv"} or $convInvList)) { | 
|  |  | 100 | 66 |  |  |  |  | 
| 2945 | 8620 |  |  |  |  | 16922 | my ($multi, $lc); | 
| 2946 |  |  |  |  |  |  | # insert alternate language print conversions if required | 
| 2947 | 8620 | 0 | 33 |  |  | 24350 | if ($$self{CUR_LANG} and $type eq 'PrintConv' and | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 2948 |  |  |  |  |  |  | ref($lc = $$self{CUR_LANG}{$tag}) eq 'HASH' and | 
| 2949 |  |  |  |  |  |  | ($lc = $$lc{PrintConv})) | 
| 2950 |  |  |  |  |  |  | { | 
| 2951 | 0 |  |  |  |  | 0 | my %newConv; | 
| 2952 | 0 |  |  |  |  | 0 | foreach (keys %$conv) { | 
| 2953 | 0 |  |  |  |  | 0 | my $val = $$conv{$_}; | 
| 2954 | 0 | 0 |  |  |  | 0 | defined $$lc{$val} or $newConv{$_} = $val, next; | 
| 2955 | 0 |  |  |  |  | 0 | $newConv{$_} = $self->Decode($$lc{$val}, 'UTF8'); | 
| 2956 |  |  |  |  |  |  | } | 
| 2957 | 0 | 0 |  |  |  | 0 | if ($$conv{BITMASK}) { | 
| 2958 | 0 |  |  |  |  | 0 | foreach (keys %{$$conv{BITMASK}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 2959 | 0 |  |  |  |  | 0 | my $val = $$conv{BITMASK}{$_}; | 
| 2960 | 0 | 0 |  |  |  | 0 | defined $$lc{$val} or $newConv{BITMASK}{$_} = $val, next; | 
| 2961 | 0 |  |  |  |  | 0 | $newConv{BITMASK}{$_} = $self->Decode($$lc{$val}, 'UTF8'); | 
| 2962 |  |  |  |  |  |  | } | 
| 2963 |  |  |  |  |  |  | } | 
| 2964 | 0 |  |  |  |  | 0 | $conv = \%newConv; | 
| 2965 |  |  |  |  |  |  | } | 
| 2966 | 8620 |  |  |  |  | 13759 | undef $evalWarning; | 
| 2967 | 8620 | 100 |  |  |  | 22801 | if ($$conv{BITMASK}) { | 
| 2968 | 100 |  |  |  |  | 315 | my $lookupBits = $$conv{BITMASK}; | 
| 2969 | 100 |  |  |  |  | 347 | my ($wbits, $tbits) = @$tagInfo{'BitsPerWord','BitsTotal'}; | 
| 2970 | 100 |  |  |  |  | 378 | my ($val2, $err2) = EncodeBits($val, $lookupBits, $wbits, $tbits); | 
| 2971 | 100 | 100 |  |  |  | 386 | if ($err2) { | 
|  |  | 100 |  |  |  |  |  | 
| 2972 |  |  |  |  |  |  | # ok, try matching a straight value | 
| 2973 | 2 |  |  |  |  | 10 | ($val, $multi) = ReverseLookup($val, $conv); | 
| 2974 | 2 | 50 |  |  |  | 18 | unless (defined $val) { | 
| 2975 | 2 |  |  |  |  | 13 | $err = "Can't encode $wgrp1:$tag ($err2)"; | 
| 2976 | 2 |  |  |  |  | 18 | $self->VPrint(2, "$err\n"); | 
| 2977 | 2 |  |  |  |  | 10 | last Conv; | 
| 2978 |  |  |  |  |  |  | } | 
| 2979 |  |  |  |  |  |  | } elsif (defined $val2) { | 
| 2980 | 67 |  |  |  |  | 162 | $val = $val2; | 
| 2981 |  |  |  |  |  |  | } else { | 
| 2982 | 31 |  |  |  |  | 83 | delete $$conv{BITMASK}; | 
| 2983 | 31 |  |  |  |  | 97 | ($val, $multi) = ReverseLookup($val, $conv); | 
| 2984 | 31 |  |  |  |  | 108 | $$conv{BITMASK} = $lookupBits; | 
| 2985 |  |  |  |  |  |  | } | 
| 2986 |  |  |  |  |  |  | } else { | 
| 2987 | 8520 |  |  |  |  | 19001 | ($val, $multi) = ReverseLookup($val, $conv); | 
| 2988 |  |  |  |  |  |  | } | 
| 2989 | 8618 | 100 |  |  |  | 23945 | if (not defined $val) { | 
|  |  | 50 |  |  |  |  |  | 
| 2990 | 4518 | 100 |  |  |  | 14388 | my $prob = $evalWarning ? lcfirst CleanWarning() : ($multi ? 'matches more than one ' : 'not in ') . $type; | 
|  |  | 50 |  |  |  |  |  | 
| 2991 | 4518 |  |  |  |  | 12539 | $err = "Can't convert $wgrp1:$tag ($prob)"; | 
| 2992 | 4518 |  |  |  |  | 19978 | $self->VPrint(2, "$err\n"); | 
| 2993 | 4518 |  |  |  |  | 11626 | last Conv; | 
| 2994 |  |  |  |  |  |  | } elsif ($evalWarning) { | 
| 2995 | 0 |  |  |  |  | 0 | $self->VPrint(2, CleanWarning() . " for $wgrp1:$tag\n"); | 
| 2996 |  |  |  |  |  |  | } | 
| 2997 |  |  |  |  |  |  | } elsif (not $$tagInfo{WriteAlso}) { | 
| 2998 | 150 |  |  |  |  | 669 | $err = "Can't convert value for $wgrp1:$tag (no ${type}Inv)"; | 
| 2999 | 150 |  |  |  |  | 806 | $self->VPrint(2, "$err\n"); | 
| 3000 | 150 |  |  |  |  | 365 | undef $val; | 
| 3001 | 150 |  |  |  |  | 385 | last Conv; | 
| 3002 |  |  |  |  |  |  | } | 
| 3003 |  |  |  |  |  |  | } | 
| 3004 | 17445 | 100 |  |  |  | 55271 | last unless @valList; | 
| 3005 | 125 |  |  |  |  | 381 | $valList[$index] = $val; | 
| 3006 | 125 | 100 |  |  |  | 415 | if (++$index >= @valList) { | 
| 3007 |  |  |  |  |  |  | # leave AutoSplit lists in ARRAY form, or join conversion lists | 
| 3008 | 73 | 100 |  |  |  | 482 | $val = $$tagInfo{List} ? \@valList : join ' ', @valList; | 
| 3009 | 73 |  |  |  |  | 257 | last; | 
| 3010 |  |  |  |  |  |  | } | 
| 3011 | 52 | 100 |  |  |  | 165 | $conv = $$convList[$index] if $convList; | 
| 3012 | 52 | 100 |  |  |  | 131 | $convInv = $$convInvList[$index] if $convInvList; | 
| 3013 | 52 |  |  |  |  | 120 | $val = $valList[$index]; | 
| 3014 |  |  |  |  |  |  | } | 
| 3015 |  |  |  |  |  |  | } # end ValueConv/PrintConv loop | 
| 3016 |  |  |  |  |  |  |  | 
| 3017 | 27960 |  |  |  |  | 80324 | return($val, $err); | 
| 3018 |  |  |  |  |  |  | } | 
| 3019 |  |  |  |  |  |  |  | 
| 3020 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3021 |  |  |  |  |  |  | # Convert tag names to values or variables in a string | 
| 3022 |  |  |  |  |  |  | # (eg. '${EXIF:ISO}x $$' --> '100x $' without hash ref, or "$info{'EXIF:ISO'}x $" with) | 
| 3023 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) reference to list of found tags | 
| 3024 |  |  |  |  |  |  | #         2) string with embedded tag names, 3) Options: | 
| 3025 |  |  |  |  |  |  | #               undef    - set missing tags to '' | 
| 3026 |  |  |  |  |  |  | #              'Error'   - issue minor error on missing tag (and return undef) | 
| 3027 |  |  |  |  |  |  | #              'Warn'    - issue minor warning on missing tag (and return undef) | 
| 3028 |  |  |  |  |  |  | #              'Silent'  - just return undef on missing tag (no errors/warnings) | 
| 3029 |  |  |  |  |  |  | #               Hash ref - defined to interpolate as variables in string instead of values | 
| 3030 |  |  |  |  |  |  | #                          --> receives tag/value pairs for interpolation of the variables | 
| 3031 |  |  |  |  |  |  | #         4) document group name if extracting from a specific document | 
| 3032 |  |  |  |  |  |  | #         5) hash ref to cache tag keys for subsequent calls in document loop | 
| 3033 |  |  |  |  |  |  | # Returns: string with embedded tag values (or '$info{TAGNAME}' entries with Hash ref option) | 
| 3034 |  |  |  |  |  |  | # Notes: | 
| 3035 |  |  |  |  |  |  | # - tag names are not case sensitive and may end with '#' for ValueConv value | 
| 3036 |  |  |  |  |  |  | # - uses MissingTagValue option if set | 
| 3037 |  |  |  |  |  |  | # - '$GROUP:all' evaluates to 1 if any tag from GROUP exists, or 0 otherwise | 
| 3038 |  |  |  |  |  |  | # - advanced feature allows Perl expressions inside braces (eg. '${model;tr/ //d}') | 
| 3039 |  |  |  |  |  |  | # - an error/warning in an advanced expression ("${TAG;EXPR}") generates an error | 
| 3040 |  |  |  |  |  |  | #   if option set to 'Error', or a warning otherwise | 
| 3041 |  |  |  |  |  |  | sub InsertTagValues($$$;$$$) | 
| 3042 |  |  |  |  |  |  | { | 
| 3043 | 6 |  |  | 6 | 0 | 28 | local $_; | 
| 3044 | 6 |  |  |  |  | 30 | my ($self, $foundTags, $line, $opt, $docGrp, $cache) = @_; | 
| 3045 | 6 |  |  |  |  | 22 | my $rtnStr = ''; | 
| 3046 | 6 |  |  |  |  | 15 | my ($docNum, $tag); | 
| 3047 | 6 | 50 |  |  |  | 29 | if ($docGrp) { | 
| 3048 | 0 | 0 |  |  |  | 0 | $docNum = $docGrp =~ /(\d+)$/ ? $1 : 0; | 
| 3049 |  |  |  |  |  |  | } else { | 
| 3050 | 6 |  |  |  |  | 18 | undef $cache;   # no cache if no document groups | 
| 3051 |  |  |  |  |  |  | } | 
| 3052 | 6 |  |  |  |  | 76 | while ($line =~ s/(.*?)\$(\{\s*)?([-\w]*\w|\$|\/)//s) { | 
| 3053 | 9 |  |  |  |  | 69 | my ($pre, $bra, $var) = ($1, $2, $3); | 
| 3054 | 9 |  |  |  |  | 32 | my (@tags, $val, $tg, @val, $type, $expr, $didExpr, $level, $asList); | 
| 3055 |  |  |  |  |  |  | # "$$" represents a "$" symbol, and "$/" is a newline | 
| 3056 | 9 | 50 | 33 |  |  | 63 | if ($var eq '$' or $var eq '/') { | 
| 3057 | 0 | 0 |  |  |  | 0 | $line =~ s/^\s*\}// if $bra; | 
| 3058 | 0 | 0 | 0 |  |  | 0 | if ($var eq '/') { | 
|  |  | 0 |  |  |  |  |  | 
| 3059 | 0 |  |  |  |  | 0 | $var = "\n"; | 
| 3060 |  |  |  |  |  |  | } elsif ($line =~ /^self\b/ and not $rtnStr =~ /\$$/) { | 
| 3061 | 0 |  |  |  |  | 0 | $var = '$$';    # ("$$self{var}" in string) | 
| 3062 |  |  |  |  |  |  | } | 
| 3063 | 0 |  |  |  |  | 0 | $rtnStr .= "$pre$var"; | 
| 3064 | 0 |  |  |  |  | 0 | next; | 
| 3065 |  |  |  |  |  |  | } | 
| 3066 |  |  |  |  |  |  | # allow multiple group names | 
| 3067 | 9 |  |  |  |  | 87 | while ($line =~ /^:([-\w]*\w)(.*)/s) { | 
| 3068 | 4 |  |  |  |  | 12 | my $group = $var; | 
| 3069 | 4 |  |  |  |  | 21 | ($var, $line) = ($1, $2); | 
| 3070 | 4 |  |  |  |  | 22 | $var = "$group:$var"; | 
| 3071 |  |  |  |  |  |  | } | 
| 3072 |  |  |  |  |  |  | # allow trailing '#' to indicate ValueConv value | 
| 3073 | 9 | 50 |  |  |  | 37 | $type = 'ValueConv' if $line =~ s/^#//; | 
| 3074 |  |  |  |  |  |  | # special advanced formatting '@' feature to evaluate list values separately | 
| 3075 | 9 | 100 | 100 |  |  | 81 | if ($bra and $line =~ s/^\@(#)?//) { | 
| 3076 | 1 |  |  |  |  | 5 | $asList = 1; | 
| 3077 | 1 | 50 |  |  |  | 6 | $type = 'ValueConv' if $1; | 
| 3078 |  |  |  |  |  |  | } | 
| 3079 |  |  |  |  |  |  | # remove trailing bracket if there was a leading one | 
| 3080 |  |  |  |  |  |  | # and extract Perl expression from inside brackets if it exists | 
| 3081 | 9 | 100 | 100 |  |  | 184 | if ($bra and $line !~ s/^\s*\}// and $line =~ s/^\s*;\s*(.*?)\s*\}//s) { | 
|  |  |  | 66 |  |  |  |  | 
| 3082 | 3 |  |  |  |  | 13 | my $part = $1; | 
| 3083 | 3 |  |  |  |  | 11 | $expr = ''; | 
| 3084 | 3 |  |  |  |  | 10 | for ($level=0; ; --$level) { | 
| 3085 |  |  |  |  |  |  | # increase nesting level for each opening brace | 
| 3086 | 7 |  |  |  |  | 33 | ++$level while $part =~ /\{/g; | 
| 3087 | 7 |  |  |  |  | 15 | $expr .= $part; | 
| 3088 | 7 | 100 | 66 |  |  | 42 | last unless $level and $line =~ s/^(.*?)\s*\}//s; # get next part | 
| 3089 | 4 |  |  |  |  | 10 | $part = $1; | 
| 3090 | 4 |  |  |  |  | 10 | $expr .= '}';  # this brace was part of the expression | 
| 3091 |  |  |  |  |  |  | } | 
| 3092 |  |  |  |  |  |  | # use default Windows filename filter if expression is empty | 
| 3093 | 3 | 50 |  |  |  | 16 | $expr = 'tr(/\\\\?*:|"<>\\0)()d' unless length $expr; | 
| 3094 |  |  |  |  |  |  | } | 
| 3095 | 9 |  |  |  |  | 34 | push @tags, $var; | 
| 3096 | 9 |  |  |  |  | 58 | ExpandShortcuts(\@tags); | 
| 3097 | 9 | 50 |  |  |  | 36 | @tags or $rtnStr .= $pre, next; | 
| 3098 |  |  |  |  |  |  | # save advanced formatting expression to allow access by user-defined ValueConv | 
| 3099 | 9 |  |  |  |  | 36 | $$self{FMT_EXPR} = $expr; | 
| 3100 |  |  |  |  |  |  |  | 
| 3101 | 9 |  |  |  |  | 23 | for (;;) { | 
| 3102 |  |  |  |  |  |  | # temporarily reset ListJoin option if evaluating list values separately | 
| 3103 | 9 |  |  |  |  | 17 | my $oldListJoin; | 
| 3104 | 9 | 100 |  |  |  | 33 | $oldListJoin = $self->Options(ListJoin => undef) if $asList; | 
| 3105 | 9 |  |  |  |  | 28 | $tag = shift @tags; | 
| 3106 | 9 |  |  |  |  | 30 | my $lcTag = lc $tag; | 
| 3107 | 9 | 50 | 33 |  |  | 59 | if ($cache and $lcTag !~ /(^|:)all$/) { | 
| 3108 |  |  |  |  |  |  | # remove group from tag name (but not lower-case version) | 
| 3109 | 0 |  |  |  |  | 0 | my $group; | 
| 3110 | 0 | 0 |  |  |  | 0 | $tag =~ s/^(.*):// and $group = $1; | 
| 3111 |  |  |  |  |  |  | # cache tag keys to speed processing for a large number of sub-documents | 
| 3112 |  |  |  |  |  |  | # (similar to code in BuildCompositeTags(), but this is case-insensitive) | 
| 3113 | 0 |  |  |  |  | 0 | my $cacheTag = $$cache{$lcTag}; | 
| 3114 | 0 | 0 |  |  |  | 0 | unless ($cacheTag) { | 
| 3115 | 0 |  |  |  |  | 0 | $cacheTag = $$cache{$lcTag} = [ ]; | 
| 3116 |  |  |  |  |  |  | # find all matching keys, organize into groups, and store in cache | 
| 3117 | 0 |  |  |  |  | 0 | my $ex = $$self{TAG_EXTRA}; | 
| 3118 | 0 |  |  |  |  | 0 | my @matches = grep /^$tag(\s|$)/i, @$foundTags; | 
| 3119 | 0 | 0 |  |  |  | 0 | @matches = $self->GroupMatches($group, \@matches) if defined $group; | 
| 3120 | 0 |  |  |  |  | 0 | foreach (@matches) { | 
| 3121 | 0 | 0 | 0 |  |  | 0 | my $doc = $$ex{$_} ? $$ex{$_}{G3} || 0 : 0; | 
| 3122 | 0 | 0 |  |  |  | 0 | if (defined $$cacheTag[$doc]) { | 
| 3123 | 0 | 0 |  |  |  | 0 | next unless $$cacheTag[$doc] =~ / \((\d+)\)$/; | 
| 3124 | 0 |  |  |  |  | 0 | my $cur = $1; | 
| 3125 |  |  |  |  |  |  | # keep the most recently extracted tag | 
| 3126 | 0 | 0 | 0 |  |  | 0 | next if / \((\d+)\)$/ and $1 < $cur; | 
| 3127 |  |  |  |  |  |  | } | 
| 3128 | 0 |  |  |  |  | 0 | $$cacheTag[$doc] = $_; | 
| 3129 |  |  |  |  |  |  | } | 
| 3130 |  |  |  |  |  |  | } | 
| 3131 | 0 | 0 | 0 |  |  | 0 | my $doc = $lcTag =~ /\b(main|doc(\d+)):/ ? ($2 || 0) : $docNum; | 
| 3132 | 0 | 0 |  |  |  | 0 | if ($$cacheTag[$doc]) { | 
| 3133 | 0 |  |  |  |  | 0 | $tag = $$cacheTag[$doc]; | 
| 3134 | 0 |  |  |  |  | 0 | $val = $self->GetValue($tag, $type); | 
| 3135 |  |  |  |  |  |  | } | 
| 3136 |  |  |  |  |  |  | } else { | 
| 3137 |  |  |  |  |  |  | # add document number to tag if specified and it doesn't already exist | 
| 3138 | 9 | 50 | 33 |  |  | 45 | if ($docGrp and $lcTag !~ /\b(main|doc\d+):/) { | 
| 3139 | 0 |  |  |  |  | 0 | $tag = $docGrp . ':' . $tag; | 
| 3140 | 0 |  |  |  |  | 0 | $lcTag = lc $tag; | 
| 3141 |  |  |  |  |  |  | } | 
| 3142 | 9 | 50 |  |  |  | 92 | if ($lcTag eq 'all') { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 3143 | 0 |  |  |  |  | 0 | $val = 1;   # always some tag available | 
| 3144 |  |  |  |  |  |  | } elsif (defined $$self{OPTIONS}{UserParam}{$lcTag}) { | 
| 3145 | 0 |  |  |  |  | 0 | $val = $$self{OPTIONS}{UserParam}{$lcTag}; | 
| 3146 |  |  |  |  |  |  | } elsif ($tag =~ /(.*):(.+)/) { | 
| 3147 | 3 |  |  |  |  | 9 | my $group; | 
| 3148 | 3 |  |  |  |  | 10 | ($group, $tag) = ($1, $2); | 
| 3149 | 3 | 50 |  |  |  | 15 | if (lc $tag eq 'all') { | 
| 3150 |  |  |  |  |  |  | # see if any tag from the specified group exists | 
| 3151 | 0 |  |  |  |  | 0 | my $match = $self->GroupMatches($group, $foundTags); | 
| 3152 | 0 | 0 |  |  |  | 0 | $val = $match ? 1 : 0; | 
| 3153 |  |  |  |  |  |  | } else { | 
| 3154 |  |  |  |  |  |  | # find the specified tag | 
| 3155 | 3 |  |  |  |  | 663 | my @matches = grep /^$tag(\s|$)/i, @$foundTags; | 
| 3156 | 3 |  |  |  |  | 20 | @matches = $self->GroupMatches($group, \@matches); | 
| 3157 | 3 |  |  |  |  | 13 | foreach $tg (@matches) { | 
| 3158 | 3 | 50 | 33 |  |  | 14 | if (defined $val and $tg =~ / \((\d+)\)$/) { | 
| 3159 |  |  |  |  |  |  | # take the most recently extracted tag | 
| 3160 | 0 |  |  |  |  | 0 | my $tagNum = $1; | 
| 3161 | 0 | 0 | 0 |  |  | 0 | next if $tag !~ / \((\d+)\)$/ or $1 > $tagNum; | 
| 3162 |  |  |  |  |  |  | } | 
| 3163 | 3 |  |  |  |  | 15 | $val = $self->GetValue($tg, $type); | 
| 3164 | 3 |  |  |  |  | 9 | $tag = $tg; | 
| 3165 | 3 | 100 |  |  |  | 23 | last unless $tag =~ / /;    # all done if we got our best match | 
| 3166 |  |  |  |  |  |  | } | 
| 3167 |  |  |  |  |  |  | } | 
| 3168 |  |  |  |  |  |  | } elsif ($tag eq 'self') { | 
| 3169 | 0 |  |  |  |  | 0 | $val = $self; # ("$self{var}" or "$self->{var}" in string) | 
| 3170 |  |  |  |  |  |  | } else { | 
| 3171 |  |  |  |  |  |  | # get the tag value | 
| 3172 | 6 |  |  |  |  | 33 | $val = $self->GetValue($tag, $type); | 
| 3173 | 6 | 100 |  |  |  | 34 | unless (defined $val) { | 
| 3174 |  |  |  |  |  |  | # check for tag name with different case | 
| 3175 | 3 |  |  |  |  | 300 | ($tg) = grep /^$tag$/i, @$foundTags; | 
| 3176 | 3 | 50 |  |  |  | 27 | if (defined $tg) { | 
| 3177 | 3 |  |  |  |  | 14 | $val = $self->GetValue($tg, $type); | 
| 3178 | 3 |  |  |  |  | 13 | $tag = $tg; | 
| 3179 |  |  |  |  |  |  | } | 
| 3180 |  |  |  |  |  |  | } | 
| 3181 |  |  |  |  |  |  | } | 
| 3182 |  |  |  |  |  |  | } | 
| 3183 | 9 | 100 |  |  |  | 38 | $self->Options(ListJoin => $oldListJoin) if $asList; | 
| 3184 | 9 | 100 |  |  |  | 75 | if (ref $val eq 'ARRAY') { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 3185 | 1 |  |  |  |  | 6 | push @val, @$val; | 
| 3186 | 1 |  |  |  |  | 3 | undef $val; | 
| 3187 | 1 | 50 |  |  |  | 7 | last unless @tags; | 
| 3188 |  |  |  |  |  |  | } elsif (ref $val eq 'SCALAR') { | 
| 3189 | 0 | 0 | 0 |  |  | 0 | if ($$self{OPTIONS}{Binary} or $$val =~ /^Binary data/) { | 
| 3190 | 0 |  |  |  |  | 0 | $val = $$val; | 
| 3191 |  |  |  |  |  |  | } else { | 
| 3192 | 0 |  |  |  |  | 0 | $val = 'Binary data ' . length($$val) . ' bytes'; | 
| 3193 |  |  |  |  |  |  | } | 
| 3194 |  |  |  |  |  |  | } elsif (ref $val eq 'HASH') { | 
| 3195 | 0 |  |  |  |  | 0 | require 'Image/ExifTool/XMPStruct.pl'; | 
| 3196 | 0 |  |  |  |  | 0 | $val = Image::ExifTool::XMP::SerializeStruct($val); | 
| 3197 |  |  |  |  |  |  | } elsif (not defined $val) { | 
| 3198 | 0 | 0 |  |  |  | 0 | $val = $$self{OPTIONS}{MissingTagValue} if $asList; | 
| 3199 |  |  |  |  |  |  | } | 
| 3200 | 8 | 50 |  |  |  | 32 | last unless @tags; | 
| 3201 | 0 | 0 |  |  |  | 0 | push @val, $val if defined $val; | 
| 3202 | 0 |  |  |  |  | 0 | undef $val; | 
| 3203 |  |  |  |  |  |  | } | 
| 3204 | 9 | 100 |  |  |  | 35 | if (@val) { | 
| 3205 | 1 | 50 |  |  |  | 6 | push @val, $val if defined $val; | 
| 3206 | 1 |  |  |  |  | 9 | $val = join $$self{OPTIONS}{ListSep}, @val; | 
| 3207 |  |  |  |  |  |  | } else { | 
| 3208 | 8 | 50 |  |  |  | 31 | push @val, $val if defined $val; # (so the eval has access to @val if required) | 
| 3209 |  |  |  |  |  |  | } | 
| 3210 |  |  |  |  |  |  | # evaluate advanced formatting expression if given (eg. "${TAG;EXPR}") | 
| 3211 | 9 | 100 | 66 |  |  | 57 | if (defined $expr and defined $val) { | 
| 3212 | 3 |  |  |  |  | 24 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 3213 | 3 |  |  |  |  | 10 | undef $evalWarning; | 
| 3214 | 3 |  |  |  |  | 10 | $advFmtSelf = $self; | 
| 3215 | 3 | 100 |  |  |  | 14 | if ($asList) { | 
| 3216 | 1 |  |  |  |  | 4 | foreach (@val) { | 
| 3217 |  |  |  |  |  |  | #### eval advanced formatting expression ($_, $self, @val, $advFmtSelf) | 
| 3218 | 3 |  |  |  |  | 261 | eval $expr; | 
| 3219 | 3 | 50 |  |  |  | 16 | $@ and $evalWarning = $@; | 
| 3220 |  |  |  |  |  |  | } | 
| 3221 |  |  |  |  |  |  | # join back together if any values are still defined | 
| 3222 | 1 |  |  |  |  | 11 | @val = grep defined, @val; | 
| 3223 | 1 | 50 |  |  |  | 14 | $val = @val ? join $$self{OPTIONS}{ListSep}, @val : undef; | 
| 3224 |  |  |  |  |  |  | } else { | 
| 3225 | 2 |  |  |  |  | 6 | $_ = $val; | 
| 3226 |  |  |  |  |  |  | #### eval advanced formatting expression ($_, $self, @val, $advFmtSelf) | 
| 3227 | 2 |  |  |  |  | 156 | eval $expr; | 
| 3228 | 2 | 50 |  |  |  | 14 | $@ and $evalWarning = $@; | 
| 3229 | 2 | 50 |  |  |  | 12 | $val = ref $_ eq 'ARRAY' ? join($$self{OPTIONS}{ListSep}, @$_): $_; | 
| 3230 |  |  |  |  |  |  | } | 
| 3231 | 3 | 50 |  |  |  | 15 | if ($evalWarning) { | 
| 3232 | 0 | 0 | 0 |  |  | 0 | my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : ''; | 
| 3233 | 0 |  |  |  |  | 0 | my $str = CleanWarning() . " for '$g3${var}'"; | 
| 3234 | 0 | 0 |  |  |  | 0 | if ($opt) { | 
| 3235 | 0 | 0 |  |  |  | 0 | if ($opt eq 'Error') { | 
|  |  | 0 |  |  |  |  |  | 
| 3236 | 0 |  |  |  |  | 0 | $self->Error($str); | 
| 3237 |  |  |  |  |  |  | } elsif ($opt ne 'Silent') { | 
| 3238 | 0 |  |  |  |  | 0 | $self->Warn($str); | 
| 3239 |  |  |  |  |  |  | } | 
| 3240 |  |  |  |  |  |  | } | 
| 3241 |  |  |  |  |  |  | } | 
| 3242 | 3 |  |  |  |  | 10 | undef $advFmtSelf; | 
| 3243 | 3 |  |  |  |  | 13 | $didExpr = 1;   # set flag indicating an expression was evaluated | 
| 3244 |  |  |  |  |  |  | } | 
| 3245 | 9 | 50 | 33 |  |  | 48 | unless (defined $val or ref $opt) { | 
| 3246 | 0 |  |  |  |  | 0 | $val = $$self{OPTIONS}{MissingTagValue}; | 
| 3247 | 0 | 0 |  |  |  | 0 | unless (defined $val) { | 
| 3248 | 0 | 0 | 0 |  |  | 0 | my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : ''; | 
| 3249 | 0 | 0 |  |  |  | 0 | my $msg = $didExpr ? "Advanced formatting expression returned undef for '$g3${var}'" : | 
| 3250 |  |  |  |  |  |  | "Tag '$g3${var}' not defined"; | 
| 3251 | 58 |  |  | 58 |  | 673 | no strict 'refs'; | 
|  | 58 |  |  |  |  | 213 |  | 
|  | 58 |  |  |  |  | 40970 |  | 
| 3252 | 0 | 0 | 0 |  |  | 0 | $opt and ($opt eq 'Silent' or &$opt($self, $msg, 2)) and return $$self{FMT_EXPR} = undef; | 
|  |  |  | 0 |  |  |  |  | 
| 3253 | 0 |  |  |  |  | 0 | $val = ''; | 
| 3254 |  |  |  |  |  |  | } | 
| 3255 |  |  |  |  |  |  | } | 
| 3256 | 9 | 50 |  |  |  | 34 | if (ref $opt eq 'HASH') { | 
| 3257 | 0 | 0 |  |  |  | 0 | $var .= '#' if $type; | 
| 3258 | 0 | 0 |  |  |  | 0 | if (defined $expr) { | 
| 3259 |  |  |  |  |  |  | # generate unique variable name for this modified tag value | 
| 3260 | 0 |  |  |  |  | 0 | my $i = 1; | 
| 3261 | 0 |  |  |  |  | 0 | ++$i while exists $$opt{"$var.expr$i"}; | 
| 3262 | 0 |  |  |  |  | 0 | $var .= '.expr' . $i; | 
| 3263 |  |  |  |  |  |  | } | 
| 3264 | 0 |  |  |  |  | 0 | $rtnStr .= "$pre\$info{'${var}'}"; | 
| 3265 | 0 |  |  |  |  | 0 | $$opt{$var} = $val; | 
| 3266 |  |  |  |  |  |  | } else { | 
| 3267 | 9 |  |  |  |  | 62 | $rtnStr .= "$pre$val"; | 
| 3268 |  |  |  |  |  |  | } | 
| 3269 |  |  |  |  |  |  | } | 
| 3270 | 6 |  |  |  |  | 27 | $$self{FMT_EXPR} = undef; | 
| 3271 | 6 |  |  |  |  | 32 | return $rtnStr . $line; | 
| 3272 |  |  |  |  |  |  | } | 
| 3273 |  |  |  |  |  |  |  | 
| 3274 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3275 |  |  |  |  |  |  | # Reformat date/time value in $_ based on specified format string | 
| 3276 |  |  |  |  |  |  | # Inputs: 0) date/time format string | 
| 3277 |  |  |  |  |  |  | sub DateFmt($) | 
| 3278 |  |  |  |  |  |  | { | 
| 3279 | 0 |  |  | 0 | 0 | 0 | my $et = bless { OPTIONS => { DateFormat => shift, StrictDate => 1 } }; | 
| 3280 | 0 |  |  |  |  | 0 | my $shift; | 
| 3281 | 0 | 0 | 0 |  |  | 0 | if ($advFmtSelf and defined($shift = $$advFmtSelf{OPTIONS}{GlobalTimeShift})) { | 
| 3282 | 0 |  |  |  |  | 0 | $$et{OPTIONS}{GlobalTimeShift} = $shift; | 
| 3283 | 0 |  |  |  |  | 0 | $$et{GLOBAL_TIME_OFFSET} = $$advFmtSelf{GLOBAL_TIME_OFFSET}; | 
| 3284 |  |  |  |  |  |  | } | 
| 3285 | 0 |  |  |  |  | 0 | $_ = $et->ConvertDateTime($_); | 
| 3286 | 0 | 0 |  |  |  | 0 | defined $_ or warn "Error converting date/time\n"; | 
| 3287 | 0 | 0 |  |  |  | 0 | $$advFmtSelf{GLOBAL_TIME_OFFSET} = $$et{GLOBAL_TIME_OFFSET} if $shift; | 
| 3288 |  |  |  |  |  |  | } | 
| 3289 |  |  |  |  |  |  |  | 
| 3290 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3291 |  |  |  |  |  |  | # Utility routine to remove duplicate items from default input string | 
| 3292 |  |  |  |  |  |  | # Inputs: 0) true to set $_ to undef if not changed | 
| 3293 |  |  |  |  |  |  | # Notes: - for use only in advanced formatting expressions | 
| 3294 |  |  |  |  |  |  | sub NoDups | 
| 3295 |  |  |  |  |  |  | { | 
| 3296 | 0 |  |  | 0 | 0 | 0 | my %seen; | 
| 3297 | 0 | 0 |  |  |  | 0 | my $sep = $advFmtSelf ? $$advFmtSelf{OPTIONS}{ListSep} : ', '; | 
| 3298 | 0 |  |  |  |  | 0 | my $new = join $sep, grep { !$seen{$_}++ } split /\Q$sep\E/, $_; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3299 | 0 | 0 | 0 |  |  | 0 | $_ = ($_[0] and $new eq $_) ? undef : $new; | 
| 3300 |  |  |  |  |  |  | } | 
| 3301 |  |  |  |  |  |  |  | 
| 3302 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3303 |  |  |  |  |  |  | # Is specified tag writable | 
| 3304 |  |  |  |  |  |  | # Inputs: 0) tag name, case insensitive (optional group name currently ignored) | 
| 3305 |  |  |  |  |  |  | # Returns: 0=exists but not writable, 1=writable, undef=doesn't exist | 
| 3306 |  |  |  |  |  |  | sub IsWritable($) | 
| 3307 |  |  |  |  |  |  | { | 
| 3308 | 0 |  |  | 0 | 0 | 0 | my $tag = shift; | 
| 3309 | 0 |  |  |  |  | 0 | $tag =~ s/^(.*)://; # ignore group name | 
| 3310 | 0 |  |  |  |  | 0 | my @tagInfo = FindTagInfo($tag); | 
| 3311 | 0 | 0 |  |  |  | 0 | unless (@tagInfo) { | 
| 3312 | 0 | 0 |  |  |  | 0 | return 0 if TagExists($tag); | 
| 3313 | 0 |  |  |  |  | 0 | return undef; | 
| 3314 |  |  |  |  |  |  | } | 
| 3315 | 0 |  |  |  |  | 0 | my $tagInfo; | 
| 3316 | 0 |  |  |  |  | 0 | foreach $tagInfo (@tagInfo) { | 
| 3317 | 0 | 0 |  |  |  | 0 | return $$tagInfo{Writable} ? 1 : 0 if defined $$tagInfo{Writable}; | 
|  |  | 0 |  |  |  |  |  | 
| 3318 | 0 | 0 |  |  |  | 0 | return 1 if $$tagInfo{Table}{WRITABLE}; | 
| 3319 |  |  |  |  |  |  | # must call WRITE_PROC to autoload writer because this may set the writable tag | 
| 3320 | 0 |  |  |  |  | 0 | my $writeProc = $$tagInfo{Table}{WRITE_PROC}; | 
| 3321 | 0 | 0 |  |  |  | 0 | if ($writeProc) { | 
| 3322 | 58 |  |  | 58 |  | 518 | no strict 'refs'; | 
|  | 58 |  |  |  |  | 175 |  | 
|  | 58 |  |  |  |  | 17919 |  | 
| 3323 | 0 |  |  |  |  | 0 | &$writeProc();  # dummy call to autoload writer | 
| 3324 | 0 | 0 |  |  |  | 0 | return 1 if $$tagInfo{Writable}; | 
| 3325 |  |  |  |  |  |  | } | 
| 3326 |  |  |  |  |  |  | } | 
| 3327 | 0 |  |  |  |  | 0 | return 0; | 
| 3328 |  |  |  |  |  |  | } | 
| 3329 |  |  |  |  |  |  |  | 
| 3330 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3331 |  |  |  |  |  |  | # Check to see if these are the same file | 
| 3332 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) first file name, 2) second file name | 
| 3333 |  |  |  |  |  |  | # Returns: true if file names reference the same file | 
| 3334 |  |  |  |  |  |  | sub IsSameFile($$$) | 
| 3335 |  |  |  |  |  |  | { | 
| 3336 | 0 |  |  | 0 | 0 | 0 | my ($self, $file, $file2) = @_; | 
| 3337 | 0 | 0 |  |  |  | 0 | return 0 unless lc $file eq lc $file2;  # (only looking for differences in case) | 
| 3338 | 0 |  |  |  |  | 0 | my ($isSame, $interrupted); | 
| 3339 | 0 |  |  |  |  | 0 | my $tmp1 = "${file}_ExifTool_tmp_$$"; | 
| 3340 | 0 |  |  |  |  | 0 | my $tmp2 = "${file2}_ExifTool_tmp_$$"; | 
| 3341 |  |  |  |  |  |  | { | 
| 3342 | 0 |  |  |  |  | 0 | local *TMP1; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3343 | 0 |  |  | 0 |  | 0 | local $SIG{INT} = sub { $interrupted = 1 }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3344 | 0 | 0 |  |  |  | 0 | if ($self->Open(\*TMP1, $tmp1, '>')) { | 
| 3345 | 0 |  |  |  |  | 0 | close TMP1; | 
| 3346 | 0 | 0 |  |  |  | 0 | $isSame = 1 if $self->Exists($tmp2); | 
| 3347 | 0 |  |  |  |  | 0 | $self->Unlink($tmp1); | 
| 3348 |  |  |  |  |  |  | } | 
| 3349 |  |  |  |  |  |  | } | 
| 3350 | 0 | 0 | 0 |  |  | 0 | if ($interrupted and $SIG{INT}) { | 
| 3351 | 58 |  |  | 58 |  | 500 | no strict 'refs'; | 
|  | 58 |  |  |  |  | 204 |  | 
|  | 58 |  |  |  |  | 146064 |  | 
| 3352 | 0 |  |  |  |  | 0 | &{$SIG{INT}}(); | 
|  | 0 |  |  |  |  | 0 |  | 
| 3353 |  |  |  |  |  |  | } | 
| 3354 | 0 |  |  |  |  | 0 | return $isSame; | 
| 3355 |  |  |  |  |  |  | } | 
| 3356 |  |  |  |  |  |  |  | 
| 3357 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3358 |  |  |  |  |  |  | # Is this a raw file type? | 
| 3359 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref | 
| 3360 |  |  |  |  |  |  | # Returns: true if FileType is a type of RAW image | 
| 3361 |  |  |  |  |  |  | sub IsRawType($) | 
| 3362 |  |  |  |  |  |  | { | 
| 3363 | 12 |  |  | 12 | 0 | 37 | my $self = shift; | 
| 3364 | 12 |  |  |  |  | 114 | return $rawType{$$self{FileType}}; | 
| 3365 |  |  |  |  |  |  | } | 
| 3366 |  |  |  |  |  |  |  | 
| 3367 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3368 |  |  |  |  |  |  | # Create directory for specified file | 
| 3369 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) complete file name including path | 
| 3370 |  |  |  |  |  |  | # Returns: 1 = directory created, 0 = nothing done, -1 = error | 
| 3371 |  |  |  |  |  |  | my $k32CreateDir; | 
| 3372 |  |  |  |  |  |  | sub CreateDirectory($$) | 
| 3373 |  |  |  |  |  |  | { | 
| 3374 | 1 |  |  | 1 | 0 | 2 | local $_; | 
| 3375 | 1 |  |  |  |  | 4 | my ($self, $file) = @_; | 
| 3376 | 1 |  |  |  |  | 5 | my $rtnVal = 0; | 
| 3377 | 1 |  |  |  |  | 8 | my $enc = $$self{OPTIONS}{CharsetFileName}; | 
| 3378 | 1 |  |  |  |  | 2 | my $dir; | 
| 3379 | 1 |  |  |  |  | 9 | ($dir = $file) =~ s/[^\/]*$//;  # remove filename from path specification | 
| 3380 |  |  |  |  |  |  | # recode as UTF-8 if necessary | 
| 3381 | 1 | 50 | 33 |  |  | 12 | if ($dir and not $self->IsDirectory($dir)) { | 
| 3382 | 0 |  |  |  |  | 0 | my @parts = split /\//, $dir; | 
| 3383 | 0 |  |  |  |  | 0 | $dir = ''; | 
| 3384 | 0 |  |  |  |  | 0 | foreach (@parts) { | 
| 3385 | 0 |  |  |  |  | 0 | $dir .= $_; | 
| 3386 | 0 | 0 | 0 |  |  | 0 | if (length $dir and not $self->IsDirectory($dir)) { | 
| 3387 |  |  |  |  |  |  | # create directory since it doesn't exist | 
| 3388 | 0 |  |  |  |  | 0 | my $d2 = $dir; # (must make a copy in case EncodeFileName recodes it) | 
| 3389 | 0 | 0 |  |  |  | 0 | if ($self->EncodeFileName($d2)) { | 
| 3390 |  |  |  |  |  |  | # handle Windows Unicode directory names | 
| 3391 | 0 | 0 |  |  |  | 0 | unless (eval { require Win32::API }) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 3392 | 0 |  |  |  |  | 0 | $self->Warn('Install Win32::API to create directories with Unicode names'); | 
| 3393 | 0 |  |  |  |  | 0 | return -1; | 
| 3394 |  |  |  |  |  |  | } | 
| 3395 | 0 | 0 |  |  |  | 0 | unless ($k32CreateDir) { | 
| 3396 | 0 | 0 |  |  |  | 0 | return -1 if defined $k32CreateDir; | 
| 3397 | 0 |  |  |  |  | 0 | $k32CreateDir = new Win32::API('KERNEL32', 'CreateDirectoryW', 'PP', 'I'); | 
| 3398 | 0 | 0 |  |  |  | 0 | unless ($k32CreateDir) { | 
| 3399 | 0 |  |  |  |  | 0 | $self->Warn('Error calling Win32::API::CreateDirectoryW'); | 
| 3400 | 0 |  |  |  |  | 0 | $k32CreateDir = 0; | 
| 3401 | 0 |  |  |  |  | 0 | return -1; | 
| 3402 |  |  |  |  |  |  | } | 
| 3403 |  |  |  |  |  |  | } | 
| 3404 | 0 | 0 |  |  |  | 0 | $k32CreateDir->Call($d2, 0) or return -1; | 
| 3405 |  |  |  |  |  |  | } else { | 
| 3406 | 0 | 0 |  |  |  | 0 | mkdir($d2, 0777) or return -1; | 
| 3407 |  |  |  |  |  |  | } | 
| 3408 | 0 |  |  |  |  | 0 | $rtnVal = 1; | 
| 3409 |  |  |  |  |  |  | } | 
| 3410 | 0 |  |  |  |  | 0 | $dir .= '/'; | 
| 3411 |  |  |  |  |  |  | } | 
| 3412 |  |  |  |  |  |  | } | 
| 3413 | 1 |  |  |  |  | 13 | return $rtnVal; | 
| 3414 |  |  |  |  |  |  | } | 
| 3415 |  |  |  |  |  |  |  | 
| 3416 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3417 |  |  |  |  |  |  | # Copy file attributes from one file to another | 
| 3418 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) source file name, 2) destination file name | 
| 3419 |  |  |  |  |  |  | # Notes: eventually add support for extended attributes? | 
| 3420 |  |  |  |  |  |  | sub CopyFileAttrs($$$) | 
| 3421 |  |  |  |  |  |  | { | 
| 3422 | 2 |  |  | 2 | 0 | 10 | my ($self, $src, $dst) = @_; | 
| 3423 | 2 |  |  |  |  | 51 | my ($mode, $uid, $gid) = (stat($src))[2, 4, 5]; | 
| 3424 |  |  |  |  |  |  | # copy file attributes unless we already set them | 
| 3425 | 2 | 50 | 33 |  |  | 22 | if (defined $mode and not defined $self->GetNewValue('FilePermissions')) { | 
| 3426 | 2 |  |  |  |  | 8 | eval { chmod($mode & 07777, $dst) }; | 
|  | 2 |  |  |  |  | 59 |  | 
| 3427 |  |  |  |  |  |  | } | 
| 3428 | 2 |  |  |  |  | 16 | my $newUid = $self->GetNewValue('FileUserID'); | 
| 3429 | 2 |  |  |  |  | 8 | my $newGid = $self->GetNewValue('FileGroupID'); | 
| 3430 | 2 | 50 | 33 |  |  | 25 | if (defined $uid and defined $gid and (not defined $newUid or not defined $newGid)) { | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 3431 | 2 | 50 |  |  |  | 11 | defined $newGid and $gid = $newGid; | 
| 3432 | 2 | 50 |  |  |  | 10 | defined $newUid and $uid = $newUid; | 
| 3433 | 2 |  |  |  |  | 5 | eval { chown($uid, $gid, $dst) }; | 
|  | 2 |  |  |  |  | 51 |  | 
| 3434 |  |  |  |  |  |  | } | 
| 3435 |  |  |  |  |  |  | } | 
| 3436 |  |  |  |  |  |  |  | 
| 3437 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3438 |  |  |  |  |  |  | # Get new file path name | 
| 3439 |  |  |  |  |  |  | # Inputs: 0) existing name (may contain directory), | 
| 3440 |  |  |  |  |  |  | #         1) new file name, new directory, or new path (dir+name) | 
| 3441 |  |  |  |  |  |  | # Returns: new file path name | 
| 3442 |  |  |  |  |  |  | sub GetNewFileName($$) | 
| 3443 |  |  |  |  |  |  | { | 
| 3444 | 1 |  |  | 1 | 0 | 4 | my ($oldName, $newName) = @_; | 
| 3445 | 1 |  |  |  |  | 9 | my ($dir, $name) = ($oldName =~ m{(.*/)(.*)}); | 
| 3446 | 1 | 50 |  |  |  | 4 | ($dir, $name) = ('', $oldName) unless defined $dir; | 
| 3447 | 1 | 50 |  |  |  | 11 | if ($newName =~ m{/$}) { | 
|  |  | 50 |  |  |  |  |  | 
| 3448 | 0 |  |  |  |  | 0 | $newName = "$newName$name"; # change dir only | 
| 3449 |  |  |  |  |  |  | } elsif ($newName !~ m{/}) { | 
| 3450 | 1 |  |  |  |  | 4 | $newName = "$dir$newName";  # change name only if newname doesn't specify dir | 
| 3451 |  |  |  |  |  |  | }                               # else change dir and name | 
| 3452 | 1 |  |  |  |  | 4 | return $newName; | 
| 3453 |  |  |  |  |  |  | } | 
| 3454 |  |  |  |  |  |  |  | 
| 3455 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3456 |  |  |  |  |  |  | # Get next available tag key | 
| 3457 |  |  |  |  |  |  | # Inputs: 0) hash reference (keys are tag keys), 1) tag name | 
| 3458 |  |  |  |  |  |  | # Returns: next available tag key | 
| 3459 |  |  |  |  |  |  | sub NextFreeTagKey($$) | 
| 3460 |  |  |  |  |  |  | { | 
| 3461 | 0 |  |  | 0 | 0 | 0 | my ($info, $tag) = @_; | 
| 3462 | 0 | 0 |  |  |  | 0 | return $tag unless exists $$info{$tag}; | 
| 3463 | 0 |  |  |  |  | 0 | my $i; | 
| 3464 | 0 |  |  |  |  | 0 | for ($i=1; ; ++$i) { | 
| 3465 | 0 |  |  |  |  | 0 | my $key = "$tag ($i)"; | 
| 3466 | 0 | 0 |  |  |  | 0 | return $key unless exists $$info{$key}; | 
| 3467 |  |  |  |  |  |  | } | 
| 3468 |  |  |  |  |  |  | } | 
| 3469 |  |  |  |  |  |  |  | 
| 3470 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3471 |  |  |  |  |  |  | # Reverse hash lookup | 
| 3472 |  |  |  |  |  |  | # Inputs: 0) value, 1) hash reference | 
| 3473 |  |  |  |  |  |  | # Returns: Hash key or undef if not found (plus flag for multiple matches in list context) | 
| 3474 |  |  |  |  |  |  | sub ReverseLookup($$) | 
| 3475 |  |  |  |  |  |  | { | 
| 3476 | 8620 |  |  | 8620 | 0 | 18214 | my ($val, $conv) = @_; | 
| 3477 | 8620 | 100 |  |  |  | 18109 | return undef unless defined $val; | 
| 3478 | 8559 |  |  |  |  | 12612 | my $multi; | 
| 3479 | 8559 | 100 |  |  |  | 18042 | if ($val =~ /^Unknown\s*\((.*)\)$/i) { | 
| 3480 | 40 |  |  |  |  | 122 | $val = $1;    # was unknown | 
| 3481 | 40 | 50 |  |  |  | 116 | if ($val =~ /^0x([\da-fA-F]+)$/) { | 
| 3482 |  |  |  |  |  |  | # disable "Hexadecimal number > 0xffffffff non-portable" warning | 
| 3483 | 0 |  |  | 0 |  | 0 | local $SIG{'__WARN__'} = sub { }; | 
| 3484 | 0 |  |  |  |  | 0 | $val = hex($val);   # convert hex value | 
| 3485 |  |  |  |  |  |  | } | 
| 3486 |  |  |  |  |  |  | } else { | 
| 3487 | 8519 |  |  |  |  | 13969 | my $qval = $val; | 
| 3488 | 8519 |  |  |  |  | 19985 | $qval =~ s/\s+$//;      # remove trailing whitespace | 
| 3489 | 8519 |  |  |  |  | 14405 | $qval = quotemeta $qval; | 
| 3490 | 8519 |  |  |  |  | 34426 | my @patterns = ( | 
| 3491 |  |  |  |  |  |  | "^$qval\$",         # exact match | 
| 3492 |  |  |  |  |  |  | "^(?i)$qval\$",     # case-insensitive | 
| 3493 |  |  |  |  |  |  | "^(?i)$qval",       # beginning of string | 
| 3494 |  |  |  |  |  |  | "(?i)$qval",        # substring | 
| 3495 |  |  |  |  |  |  | ); | 
| 3496 |  |  |  |  |  |  | # hash entries to ignore in reverse lookup | 
| 3497 | 8519 |  |  |  |  | 17238 | my ($pattern, $found, $matches); | 
| 3498 | 8519 |  |  |  |  | 18729 | PAT:    foreach $pattern (@patterns) { | 
| 3499 | 21760 |  |  |  |  | 380012 | $matches = scalar grep /$pattern/, values(%$conv); | 
| 3500 | 21760 | 100 |  |  |  | 59970 | next unless $matches; | 
| 3501 |  |  |  |  |  |  | # multiple matches are bad unless they were exact | 
| 3502 | 6450 | 100 | 100 |  |  | 23039 | if ($matches > 1 and $pattern !~ /\$$/) { | 
| 3503 |  |  |  |  |  |  | # don't match entries that we should ignore | 
| 3504 | 3128 |  |  |  |  | 9171 | foreach (keys %ignorePrintConv) { | 
| 3505 | 9384 | 100 | 100 |  |  | 23559 | --$matches if defined $$conv{$_} and $$conv{$_} =~ /$pattern/; | 
| 3506 |  |  |  |  |  |  | } | 
| 3507 | 3128 | 100 |  |  |  | 9455 | last if $matches > 1; | 
| 3508 |  |  |  |  |  |  | } | 
| 3509 | 3452 |  |  |  |  | 52237 | foreach (sort keys %$conv) { | 
| 3510 | 10767 | 100 | 100 |  |  | 43669 | next if $$conv{$_} !~ /$pattern/ or $ignorePrintConv{$_}; | 
| 3511 | 3411 |  |  |  |  | 7026 | $val = $_; | 
| 3512 | 3411 |  |  |  |  | 5579 | $found = 1; | 
| 3513 | 3411 |  |  |  |  | 7283 | last PAT; | 
| 3514 |  |  |  |  |  |  | } | 
| 3515 |  |  |  |  |  |  | } | 
| 3516 | 8519 | 100 |  |  |  | 24652 | unless ($found) { | 
| 3517 |  |  |  |  |  |  | # call OTHER conversion routine if available | 
| 3518 | 5108 | 100 |  |  |  | 11835 | if ($$conv{OTHER}) { | 
| 3519 | 792 |  |  |  |  | 3813 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 3520 | 792 |  |  |  |  | 1567 | undef $evalWarning; | 
| 3521 | 792 |  |  |  |  | 1237 | $val = &{$$conv{OTHER}}($val,1,$conv); | 
|  | 792 |  |  |  |  | 3122 |  | 
| 3522 |  |  |  |  |  |  | } else { | 
| 3523 | 4316 |  |  |  |  | 7622 | $val = undef; | 
| 3524 |  |  |  |  |  |  | } | 
| 3525 | 5108 | 100 |  |  |  | 20034 | $multi = 1 if $matches > 1; | 
| 3526 |  |  |  |  |  |  | } | 
| 3527 |  |  |  |  |  |  | } | 
| 3528 | 8559 | 100 |  |  |  | 32817 | return ($val, $multi) if wantarray; | 
| 3529 | 47 |  |  |  |  | 144 | return $val; | 
| 3530 |  |  |  |  |  |  | } | 
| 3531 |  |  |  |  |  |  |  | 
| 3532 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3533 |  |  |  |  |  |  | # Return true if we are deleting or overwriting the specified tag | 
| 3534 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) new value hash reference | 
| 3535 |  |  |  |  |  |  | #         2) optional tag value (before RawConv) if deleting specific values | 
| 3536 |  |  |  |  |  |  | # Returns: >0 - tag should be overwritten | 
| 3537 |  |  |  |  |  |  | #          =0 - the tag should be preserved | 
| 3538 |  |  |  |  |  |  | #          <0 - not sure, we need the old value to tell (if there is no old value | 
| 3539 |  |  |  |  |  |  | #               then the tag should be written if $$nvHash{IsCreating} is true) | 
| 3540 |  |  |  |  |  |  | # Notes: $$nvHash{Value} is updated with the new value when shifting a value | 
| 3541 |  |  |  |  |  |  | sub IsOverwriting($$;$) | 
| 3542 |  |  |  |  |  |  | { | 
| 3543 | 6205 |  |  | 6205 | 0 | 13499 | my ($self, $nvHash, $val) = @_; | 
| 3544 | 6205 | 100 |  |  |  | 16382 | return 0 unless $nvHash; | 
| 3545 |  |  |  |  |  |  | # overwrite regardless if no DelValues specified | 
| 3546 | 6164 | 100 |  |  |  | 25270 | return 1 unless $$nvHash{DelValue}; | 
| 3547 |  |  |  |  |  |  | # never overwrite if DelValue list exists but is empty | 
| 3548 | 117 |  |  |  |  | 323 | my $shift = $$nvHash{Shift}; | 
| 3549 | 117 | 100 | 100 |  |  | 192 | return 0 unless @{$$nvHash{DelValue}} or defined $shift; | 
|  | 117 |  |  |  |  | 472 |  | 
| 3550 |  |  |  |  |  |  | # return "don't know" if we don't have a value to test | 
| 3551 | 104 | 100 |  |  |  | 395 | return -1 unless defined $val; | 
| 3552 |  |  |  |  |  |  | # apply raw conversion if necessary | 
| 3553 | 46 |  |  |  |  | 127 | my $tagInfo = $$nvHash{TagInfo}; | 
| 3554 | 46 |  |  |  |  | 108 | my $conv = $$tagInfo{RawConv}; | 
| 3555 | 46 | 100 |  |  |  | 127 | if ($conv) { | 
| 3556 | 3 |  |  |  |  | 24 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 3557 | 3 |  |  |  |  | 10 | undef $evalWarning; | 
| 3558 | 3 | 50 |  |  |  | 17 | if (ref $conv eq 'CODE') { | 
| 3559 | 0 |  |  |  |  | 0 | $val = &$conv($val, $self); | 
| 3560 |  |  |  |  |  |  | } else { | 
| 3561 | 3 |  |  |  |  | 8 | my ($priority, @grps); | 
| 3562 | 3 |  |  |  |  | 11 | my $tag = $$tagInfo{Name}; | 
| 3563 |  |  |  |  |  |  | #### eval RawConv ($self, $val, $tag, $tagInfo, $priority, @grps) | 
| 3564 | 3 |  |  |  |  | 307 | $val = eval $conv; | 
| 3565 | 3 | 50 |  |  |  | 29 | $@ and $evalWarning = $@; | 
| 3566 |  |  |  |  |  |  | } | 
| 3567 | 3 | 50 |  |  |  | 23 | return -1 unless defined $val; | 
| 3568 |  |  |  |  |  |  | } | 
| 3569 |  |  |  |  |  |  | # do not overwrite if only creating | 
| 3570 | 46 | 100 |  |  |  | 162 | return 0 if $$nvHash{CreateOnly}; | 
| 3571 |  |  |  |  |  |  | # apply time/number shift if necessary | 
| 3572 | 40 | 100 |  |  |  | 134 | if (defined $shift) { | 
| 3573 | 13 |  |  |  |  | 41 | my $shiftType = $$tagInfo{Shift}; | 
| 3574 | 13 | 100 | 66 |  |  | 67 | unless ($shiftType and $shiftType eq 'Time') { | 
| 3575 | 6 | 50 |  |  |  | 24 | unless (IsFloat($val)) { | 
| 3576 |  |  |  |  |  |  | # do the ValueConv to try to get a number | 
| 3577 | 0 |  |  |  |  | 0 | my $conv = $$tagInfo{ValueConv}; | 
| 3578 | 0 | 0 |  |  |  | 0 | if (defined $conv) { | 
| 3579 | 0 |  |  |  |  | 0 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 3580 | 0 |  |  |  |  | 0 | undef $evalWarning; | 
| 3581 | 0 | 0 |  |  |  | 0 | if (ref $conv eq 'CODE') { | 
|  |  | 0 |  |  |  |  |  | 
| 3582 | 0 |  |  |  |  | 0 | $val = &$conv($val, $self); | 
| 3583 |  |  |  |  |  |  | } elsif (not ref $conv) { | 
| 3584 |  |  |  |  |  |  | #### eval ValueConv ($val, $self) | 
| 3585 | 0 |  |  |  |  | 0 | $val = eval $conv; | 
| 3586 | 0 | 0 |  |  |  | 0 | $@ and $evalWarning = $@; | 
| 3587 |  |  |  |  |  |  | } | 
| 3588 | 0 | 0 |  |  |  | 0 | if ($evalWarning) { | 
| 3589 | 0 |  |  |  |  | 0 | $self->Warn("ValueConv $$tagInfo{Name}: " . CleanWarning()); | 
| 3590 | 0 |  |  |  |  | 0 | return 0; | 
| 3591 |  |  |  |  |  |  | } | 
| 3592 |  |  |  |  |  |  | } | 
| 3593 | 0 | 0 | 0 |  |  | 0 | unless (defined $val and IsFloat($val)) { | 
| 3594 | 0 |  |  |  |  | 0 | $self->Warn("Can't shift $$tagInfo{Name} (not a number)"); | 
| 3595 | 0 |  |  |  |  | 0 | return 0; | 
| 3596 |  |  |  |  |  |  | } | 
| 3597 |  |  |  |  |  |  | } | 
| 3598 | 6 |  |  |  |  | 20 | $shiftType = 'Number';  # allow any number to be shifted | 
| 3599 |  |  |  |  |  |  | } | 
| 3600 | 13 |  |  |  |  | 96 | require 'Image/ExifTool/Shift.pl'; | 
| 3601 | 13 |  |  |  |  | 78 | my $err = $self->ApplyShift($shiftType, $shift, $val, $nvHash); | 
| 3602 | 13 | 50 |  |  |  | 44 | if ($err) { | 
| 3603 | 0 |  |  |  |  | 0 | $self->Warn("$err when shifting $$tagInfo{Name}"); | 
| 3604 | 0 |  |  |  |  | 0 | return 0; | 
| 3605 |  |  |  |  |  |  | } | 
| 3606 |  |  |  |  |  |  | # ensure that the shifted value is valid and reformat if necessary | 
| 3607 | 13 |  |  |  |  | 61 | my $checkVal = $self->GetNewValue($nvHash); | 
| 3608 | 13 | 50 |  |  |  | 39 | return 0 unless defined $checkVal; | 
| 3609 |  |  |  |  |  |  | # don't bother overwriting if value is the same | 
| 3610 | 13 | 50 |  |  |  | 59 | return 0 if $val eq $$nvHash{Value}[0]; | 
| 3611 | 13 |  |  |  |  | 74 | return 1; | 
| 3612 |  |  |  |  |  |  | } | 
| 3613 |  |  |  |  |  |  | # return 1 if value matches a DelValue | 
| 3614 | 27 |  |  |  |  | 53 | my $delVal; | 
| 3615 | 27 |  |  |  |  | 49 | foreach $delVal (@{$$nvHash{DelValue}}) { | 
|  | 27 |  |  |  |  | 76 |  | 
| 3616 | 32 | 100 |  |  |  | 127 | return 1 if $val eq $delVal; | 
| 3617 |  |  |  |  |  |  | } | 
| 3618 | 17 |  |  |  |  | 60 | return 0; | 
| 3619 |  |  |  |  |  |  | } | 
| 3620 |  |  |  |  |  |  |  | 
| 3621 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3622 |  |  |  |  |  |  | # Get write group for specified tag | 
| 3623 |  |  |  |  |  |  | # Inputs: 0) new value hash reference | 
| 3624 |  |  |  |  |  |  | # Returns: Write group name | 
| 3625 |  |  |  |  |  |  | sub GetWriteGroup($) | 
| 3626 |  |  |  |  |  |  | { | 
| 3627 | 0 |  |  | 0 | 0 | 0 | return $_[0]{WriteGroup}; | 
| 3628 |  |  |  |  |  |  | } | 
| 3629 |  |  |  |  |  |  |  | 
| 3630 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3631 |  |  |  |  |  |  | # Get name of write group or family 1 group | 
| 3632 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) tagInfo ref, 2) write group name | 
| 3633 |  |  |  |  |  |  | # Returns: Name of group for verbose message | 
| 3634 |  |  |  |  |  |  | sub GetWriteGroup1($$) | 
| 3635 |  |  |  |  |  |  | { | 
| 3636 | 32168 |  |  | 32168 | 0 | 69295 | my ($self, $tagInfo, $writeGroup) = @_; | 
| 3637 | 32168 | 100 |  |  |  | 144652 | return $writeGroup unless $writeGroup =~ /^(MakerNotes|XMP|Composite|QuickTime)$/; | 
| 3638 | 26832 |  |  |  |  | 94335 | return $self->GetGroup($tagInfo, 1); | 
| 3639 |  |  |  |  |  |  | } | 
| 3640 |  |  |  |  |  |  |  | 
| 3641 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3642 |  |  |  |  |  |  | # Get new value hash for specified tagInfo/writeGroup | 
| 3643 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) reference to tag info hash | 
| 3644 |  |  |  |  |  |  | #         2) Write group name, 3) Options: 'delete' or 'create' new value hash | 
| 3645 |  |  |  |  |  |  | #         4) optional ProtectSaved value, 5) true if we are deleting a value | 
| 3646 |  |  |  |  |  |  | # Returns: new value hash reference for specified write group | 
| 3647 |  |  |  |  |  |  | #          (or first new value hash in linked list if write group not specified) | 
| 3648 |  |  |  |  |  |  | # Notes: May return undef when 'create' is used with ProtectSaved | 
| 3649 |  |  |  |  |  |  | sub GetNewValueHash($$;$$$$) | 
| 3650 |  |  |  |  |  |  | { | 
| 3651 | 67445 |  |  | 67445 | 0 | 162930 | my ($self, $tagInfo, $writeGroup, $opts) = @_; | 
| 3652 | 67445 | 100 |  |  |  | 131732 | return undef unless $tagInfo; | 
| 3653 | 67444 |  |  |  |  | 173180 | my $nvHash = $$self{NEW_VALUE}{$tagInfo}; | 
| 3654 |  |  |  |  |  |  |  | 
| 3655 | 67444 |  |  |  |  | 95704 | my %opts;   # quick lookup for options | 
| 3656 | 67444 | 100 |  |  |  | 148565 | $opts and $opts{$opts} = 1; | 
| 3657 | 67444 | 100 |  |  |  | 136412 | $writeGroup = '' unless defined $writeGroup; | 
| 3658 |  |  |  |  |  |  |  | 
| 3659 | 67444 | 100 |  |  |  | 121987 | if ($writeGroup) { | 
| 3660 |  |  |  |  |  |  | # find the new value in the list with the specified write group | 
| 3661 | 46315 |  | 100 |  |  | 114357 | while ($nvHash and $$nvHash{WriteGroup} ne $writeGroup) { | 
| 3662 |  |  |  |  |  |  | # QuickTime and All are special cases because all group1 tags may be updated at once | 
| 3663 | 2003 | 100 |  |  |  | 6719 | last if $$nvHash{WriteGroup} =~ /^(QuickTime|All)$/; | 
| 3664 |  |  |  |  |  |  | # replace existing entry if WriteGroup is 'All' (avoids confusion of forum10349) | 
| 3665 | 1967 | 100 | 100 |  |  | 5225 | last if $$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All'; | 
| 3666 | 1955 |  |  |  |  | 4514 | $nvHash = $$nvHash{Next}; | 
| 3667 |  |  |  |  |  |  | } | 
| 3668 |  |  |  |  |  |  | } | 
| 3669 |  |  |  |  |  |  | # remove this entry if deleting, or if creating a new entry and | 
| 3670 |  |  |  |  |  |  | # this entry is marked with "Save" flag | 
| 3671 | 67444 | 100 | 100 |  |  | 177272 | if (defined $nvHash and ($opts{'delete'} or ($opts{'create'} and $$nvHash{Save}))) { | 
|  |  |  | 100 |  |  |  |  | 
| 3672 | 2403 |  | 33 |  |  | 7463 | my $protect = (defined $_[4] and defined $$nvHash{Save} and $$nvHash{Save} > $_[4]); | 
| 3673 |  |  |  |  |  |  | # this is a bit tricky:  we want to add to a protected nvHash only if we | 
| 3674 |  |  |  |  |  |  | # are adding a conditional delete ($_[5] true or DelValue with no Shift) | 
| 3675 |  |  |  |  |  |  | # or accumulating List items (NoReplace true) | 
| 3676 | 2403 | 50 | 0 |  |  | 8038 | if ($protect and not ($opts{create} and ($$nvHash{NoReplace} or $_[5] or | 
|  |  | 100 | 33 |  |  |  |  | 
| 3677 |  |  |  |  |  |  | ($$nvHash{DelValue} and not defined $$nvHash{Shift})))) | 
| 3678 |  |  |  |  |  |  | { | 
| 3679 | 0 |  |  |  |  | 0 | return undef;   # honour ProtectSaved value by not writing this tag | 
| 3680 |  |  |  |  |  |  | } elsif ($opts{'delete'}) { | 
| 3681 | 2394 |  |  |  |  | 7541 | $self->RemoveNewValueHash($nvHash, $tagInfo); | 
| 3682 | 2394 |  |  |  |  | 7877 | undef $nvHash; | 
| 3683 |  |  |  |  |  |  | } else { | 
| 3684 |  |  |  |  |  |  | # save a copy of this new value hash | 
| 3685 | 9 |  |  |  |  | 86 | my %copy = %$nvHash; | 
| 3686 |  |  |  |  |  |  | # make copy of Value and DelValue lists | 
| 3687 | 9 |  |  |  |  | 25 | my $key; | 
| 3688 | 9 |  |  |  |  | 31 | foreach $key (keys %copy) { | 
| 3689 | 67 | 100 |  |  |  | 143 | next unless ref $copy{$key} eq 'ARRAY'; | 
| 3690 | 9 |  |  |  |  | 12 | $copy{$key} = [ @{$copy{$key}} ]; | 
|  | 9 |  |  |  |  | 45 |  | 
| 3691 |  |  |  |  |  |  | } | 
| 3692 | 9 |  |  |  |  | 20 | my $saveHash = $$self{SAVE_NEW_VALUE}; | 
| 3693 |  |  |  |  |  |  | # add to linked list of saved new value hashes | 
| 3694 | 9 |  |  |  |  | 40 | $copy{Next} = $$saveHash{$tagInfo}; | 
| 3695 | 9 |  |  |  |  | 27 | $$saveHash{$tagInfo} = \%copy; | 
| 3696 | 9 |  |  |  |  | 20 | delete $$nvHash{Save}; # don't save it again | 
| 3697 | 9 | 0 | 33 |  |  | 27 | $$nvHash{AddBefore} = scalar @{$$nvHash{Value}} if $protect and $$nvHash{Value}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3698 |  |  |  |  |  |  | } | 
| 3699 |  |  |  |  |  |  | } | 
| 3700 | 67444 | 100 | 100 |  |  | 192211 | if (not defined $nvHash and $opts{'create'}) { | 
| 3701 |  |  |  |  |  |  | # create a new entry | 
| 3702 | 23023 |  |  |  |  | 97129 | $nvHash = { | 
| 3703 |  |  |  |  |  |  | TagInfo => $tagInfo, | 
| 3704 |  |  |  |  |  |  | WriteGroup => $writeGroup, | 
| 3705 |  |  |  |  |  |  | IsNVH => 1, # set flag so we can recognize a new value hash | 
| 3706 |  |  |  |  |  |  | }; | 
| 3707 |  |  |  |  |  |  | # add entry to our NEW_VALUE hash | 
| 3708 | 23023 | 100 |  |  |  | 58671 | if ($$self{NEW_VALUE}{$tagInfo}) { | 
| 3709 |  |  |  |  |  |  | # add to end of linked list | 
| 3710 | 32 |  |  |  |  | 173 | my $lastHash = LastInList($$self{NEW_VALUE}{$tagInfo}); | 
| 3711 | 32 |  |  |  |  | 144 | $$lastHash{Next} = $nvHash; | 
| 3712 |  |  |  |  |  |  | } else { | 
| 3713 | 22991 |  |  |  |  | 64907 | $$self{NEW_VALUE}{$tagInfo} = $nvHash; | 
| 3714 |  |  |  |  |  |  | } | 
| 3715 |  |  |  |  |  |  | } | 
| 3716 | 67444 |  |  |  |  | 169658 | return $nvHash; | 
| 3717 |  |  |  |  |  |  | } | 
| 3718 |  |  |  |  |  |  |  | 
| 3719 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3720 |  |  |  |  |  |  | # Load all tag tables | 
| 3721 |  |  |  |  |  |  | sub LoadAllTables() | 
| 3722 |  |  |  |  |  |  | { | 
| 3723 | 0 | 0 |  | 0 | 0 | 0 | return if $loadedAllTables; | 
| 3724 |  |  |  |  |  |  |  | 
| 3725 |  |  |  |  |  |  | # load all of our non-referenced tables (first our modules) | 
| 3726 | 0 |  |  |  |  | 0 | my $table; | 
| 3727 | 0 |  |  |  |  | 0 | foreach $table (@loadAllTables) { | 
| 3728 | 0 |  |  |  |  | 0 | my $tableName = "Image::ExifTool::$table"; | 
| 3729 | 0 | 0 |  |  |  | 0 | $tableName .= '::Main' unless $table =~ /:/; | 
| 3730 | 0 |  |  |  |  | 0 | GetTagTable($tableName); | 
| 3731 |  |  |  |  |  |  | } | 
| 3732 |  |  |  |  |  |  | # (then our special tables) | 
| 3733 | 0 |  |  |  |  | 0 | GetTagTable('Image::ExifTool::Extra'); | 
| 3734 | 0 |  |  |  |  | 0 | GetTagTable('Image::ExifTool::Composite'); | 
| 3735 |  |  |  |  |  |  | # recursively load all tables referenced by the current tables | 
| 3736 | 0 |  |  |  |  | 0 | my @tableNames = keys %allTables; | 
| 3737 | 0 |  |  |  |  | 0 | my %pushedTables; | 
| 3738 | 0 |  |  |  |  | 0 | while (@tableNames) { | 
| 3739 | 0 |  |  |  |  | 0 | $table = GetTagTable(shift @tableNames); | 
| 3740 |  |  |  |  |  |  | # call write proc if it exists in case it adds tags to the table | 
| 3741 | 0 |  |  |  |  | 0 | my $writeProc = $$table{WRITE_PROC}; | 
| 3742 | 0 | 0 |  |  |  | 0 | if ($writeProc) { | 
| 3743 | 58 |  |  | 58 |  | 596 | no strict 'refs'; | 
|  | 58 |  |  |  |  | 183 |  | 
|  | 58 |  |  |  |  | 179295 |  | 
| 3744 | 0 |  |  |  |  | 0 | &$writeProc(); | 
| 3745 |  |  |  |  |  |  | } | 
| 3746 |  |  |  |  |  |  | # recursively scan through tables in subdirectories | 
| 3747 | 0 |  |  |  |  | 0 | foreach (TagTableKeys($table)) { | 
| 3748 | 0 |  |  |  |  | 0 | my @infoArray = GetTagInfoList($table,$_); | 
| 3749 | 0 |  |  |  |  | 0 | my $tagInfo; | 
| 3750 | 0 |  |  |  |  | 0 | foreach $tagInfo (@infoArray) { | 
| 3751 | 0 | 0 |  |  |  | 0 | my $subdir = $$tagInfo{SubDirectory} or next; | 
| 3752 | 0 | 0 |  |  |  | 0 | my $tableName = $$subdir{TagTable} or next; | 
| 3753 |  |  |  |  |  |  | # next if table already loaded or queued for loading | 
| 3754 | 0 | 0 | 0 |  |  | 0 | next if $allTables{$tableName} or $pushedTables{$tableName}; | 
| 3755 | 0 |  |  |  |  | 0 | push @tableNames, $tableName;   # must scan this one too | 
| 3756 | 0 |  |  |  |  | 0 | $pushedTables{$tableName} = 1; | 
| 3757 |  |  |  |  |  |  | } | 
| 3758 |  |  |  |  |  |  | } | 
| 3759 |  |  |  |  |  |  | } | 
| 3760 | 0 |  |  |  |  | 0 | $loadedAllTables = 1; | 
| 3761 |  |  |  |  |  |  | } | 
| 3762 |  |  |  |  |  |  |  | 
| 3763 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3764 |  |  |  |  |  |  | # Remove new value hash from linked list (and save if necessary) | 
| 3765 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) new value hash ref, 2) tagInfo ref | 
| 3766 |  |  |  |  |  |  | sub RemoveNewValueHash($$$) | 
| 3767 |  |  |  |  |  |  | { | 
| 3768 | 2664 |  |  | 2664 | 0 | 5254 | my ($self, $nvHash, $tagInfo) = @_; | 
| 3769 | 2664 |  |  |  |  | 5656 | my $firstHash = $$self{NEW_VALUE}{$tagInfo}; | 
| 3770 | 2664 | 50 |  |  |  | 6562 | if ($nvHash eq $firstHash) { | 
| 3771 |  |  |  |  |  |  | # remove first entry from linked list | 
| 3772 | 2664 | 50 |  |  |  | 5582 | if ($$nvHash{Next}) { | 
| 3773 | 0 |  |  |  |  | 0 | $$self{NEW_VALUE}{$tagInfo} = $$nvHash{Next}; | 
| 3774 |  |  |  |  |  |  | } else { | 
| 3775 | 2664 |  |  |  |  | 7311 | delete $$self{NEW_VALUE}{$tagInfo}; | 
| 3776 |  |  |  |  |  |  | } | 
| 3777 |  |  |  |  |  |  | } else { | 
| 3778 |  |  |  |  |  |  | # find the list element pointing to this hash | 
| 3779 | 0 |  |  |  |  | 0 | $firstHash = $$firstHash{Next} while $$firstHash{Next} ne $nvHash; | 
| 3780 |  |  |  |  |  |  | # remove from linked list | 
| 3781 | 0 |  |  |  |  | 0 | $$firstHash{Next} = $$nvHash{Next}; | 
| 3782 |  |  |  |  |  |  | } | 
| 3783 |  |  |  |  |  |  | # save the existing entry if necessary | 
| 3784 | 2664 | 100 |  |  |  | 7731 | if ($$nvHash{Save}) { | 
| 3785 | 80 |  |  |  |  | 161 | my $saveHash = $$self{SAVE_NEW_VALUE}; | 
| 3786 |  |  |  |  |  |  | # add to linked list of saved new value hashes | 
| 3787 | 80 |  |  |  |  | 221 | $$nvHash{Next} = $$saveHash{$tagInfo}; | 
| 3788 | 80 |  |  |  |  | 306 | $$saveHash{$tagInfo} = $nvHash; | 
| 3789 |  |  |  |  |  |  | } | 
| 3790 |  |  |  |  |  |  | } | 
| 3791 |  |  |  |  |  |  |  | 
| 3792 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3793 |  |  |  |  |  |  | # Remove all new value entries for specified group | 
| 3794 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) group name | 
| 3795 |  |  |  |  |  |  | sub RemoveNewValuesForGroup($$) | 
| 3796 |  |  |  |  |  |  | { | 
| 3797 | 784 |  |  | 784 | 0 | 1345 | my ($self, $group) = @_; | 
| 3798 |  |  |  |  |  |  |  | 
| 3799 | 784 | 100 |  |  |  | 1605 | return unless $$self{NEW_VALUE}; | 
| 3800 |  |  |  |  |  |  |  | 
| 3801 |  |  |  |  |  |  | # make list of all groups we must remove | 
| 3802 | 7 |  |  |  |  | 27 | my @groups = ( $group ); | 
| 3803 | 7 | 100 |  |  |  | 42 | push @groups, @{$removeGroups{$group}} if $removeGroups{$group}; | 
|  | 2 |  |  |  |  | 8 |  | 
| 3804 |  |  |  |  |  |  |  | 
| 3805 | 7 |  |  |  |  | 20 | my ($out, @keys, $hashKey); | 
| 3806 | 7 | 50 |  |  |  | 36 | $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose} > 1; | 
| 3807 |  |  |  |  |  |  |  | 
| 3808 |  |  |  |  |  |  | # loop though all new values, and remove any in this group | 
| 3809 | 7 |  |  |  |  | 19 | @keys = keys %{$$self{NEW_VALUE}}; | 
|  | 7 |  |  |  |  | 737 |  | 
| 3810 | 7 |  |  |  |  | 36 | foreach $hashKey (@keys) { | 
| 3811 | 1979 |  |  |  |  | 5154 | my $nvHash = $$self{NEW_VALUE}{$hashKey}; | 
| 3812 |  |  |  |  |  |  | # loop through each entry in linked list | 
| 3813 | 1979 |  |  |  |  | 2805 | for (;;) { | 
| 3814 | 1985 |  |  |  |  | 4469 | my $nextHash = $$nvHash{Next}; | 
| 3815 | 1985 |  |  |  |  | 4342 | my $tagInfo = $$nvHash{TagInfo}; | 
| 3816 | 1985 |  |  |  |  | 5385 | my ($grp0,$grp1) = $self->GetGroup($tagInfo); | 
| 3817 | 1985 |  |  |  |  | 5330 | my $wgrp = $$nvHash{WriteGroup}; | 
| 3818 |  |  |  |  |  |  | # use group1 if write group is not specific | 
| 3819 | 1985 | 100 |  |  |  | 4394 | $wgrp = $grp1 if $wgrp eq $grp0; | 
| 3820 | 1985 | 100 |  |  |  | 44906 | if (grep /^($grp0|$wgrp)$/i, @groups) { | 
| 3821 | 270 | 50 |  |  |  | 762 | $out and print $out "Removed new value for $wgrp:$$tagInfo{Name}\n"; | 
| 3822 |  |  |  |  |  |  | # remove from linked list | 
| 3823 | 270 |  |  |  |  | 709 | $self->RemoveNewValueHash($nvHash, $tagInfo); | 
| 3824 |  |  |  |  |  |  | } | 
| 3825 | 1985 | 100 |  |  |  | 7983 | $nvHash = $nextHash or last; | 
| 3826 |  |  |  |  |  |  | } | 
| 3827 |  |  |  |  |  |  | } | 
| 3828 |  |  |  |  |  |  | } | 
| 3829 |  |  |  |  |  |  |  | 
| 3830 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3831 |  |  |  |  |  |  | # Get list of tagInfo hashes for all new data | 
| 3832 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) optional tag table pointer | 
| 3833 |  |  |  |  |  |  | # Returns: list of tagInfo hashes | 
| 3834 |  |  |  |  |  |  | sub GetNewTagInfoList($;$) | 
| 3835 |  |  |  |  |  |  | { | 
| 3836 | 1194 |  |  | 1194 | 0 | 3019 | my ($self, $tagTablePtr) = @_; | 
| 3837 | 1194 |  |  |  |  | 2067 | my @tagInfoList; | 
| 3838 | 1194 |  |  |  |  | 2683 | my $nv = $$self{NEW_VALUE}; | 
| 3839 | 1194 | 100 |  |  |  | 3209 | if ($nv) { | 
| 3840 | 1170 |  |  |  |  | 1858 | my $hashKey; | 
| 3841 | 1170 |  |  |  |  | 22908 | foreach $hashKey (keys %$nv) { | 
| 3842 | 88840 |  |  |  |  | 171115 | my $tagInfo = $$nv{$hashKey}{TagInfo}; | 
| 3843 | 88840 | 100 | 100 |  |  | 265786 | next if $tagTablePtr and $tagTablePtr ne $$tagInfo{Table}; | 
| 3844 | 32433 |  |  |  |  | 56571 | push @tagInfoList, $tagInfo; | 
| 3845 |  |  |  |  |  |  | } | 
| 3846 |  |  |  |  |  |  | } | 
| 3847 | 1194 |  |  |  |  | 14747 | return @tagInfoList; | 
| 3848 |  |  |  |  |  |  | } | 
| 3849 |  |  |  |  |  |  |  | 
| 3850 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3851 |  |  |  |  |  |  | # Get hash of tagInfo references keyed on tagID for a specific table | 
| 3852 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1-N) tag table pointers | 
| 3853 |  |  |  |  |  |  | # Returns: hash reference | 
| 3854 |  |  |  |  |  |  | # Notes: returns only one tagInfo ref for each conditional list | 
| 3855 |  |  |  |  |  |  | sub GetNewTagInfoHash($@) | 
| 3856 |  |  |  |  |  |  | { | 
| 3857 | 473 |  |  | 473 | 0 | 891 | my $self = shift; | 
| 3858 | 473 |  |  |  |  | 879 | my (%tagInfoHash, $hashKey); | 
| 3859 | 473 |  |  |  |  | 993 | my $nv = $$self{NEW_VALUE}; | 
| 3860 | 473 |  |  |  |  | 1208 | while ($nv) { | 
| 3861 | 921 |  | 100 |  |  | 2213 | my $tagTablePtr = shift || last; | 
| 3862 | 463 |  |  |  |  | 5015 | foreach $hashKey (keys %$nv) { | 
| 3863 | 21399 |  |  |  |  | 37788 | my $tagInfo = $$nv{$hashKey}{TagInfo}; | 
| 3864 | 21399 | 100 | 66 |  |  | 75047 | next if $tagTablePtr and $tagTablePtr ne $$tagInfo{Table}; | 
| 3865 | 287 |  |  |  |  | 1062 | $tagInfoHash{$$tagInfo{TagID}} = $tagInfo; | 
| 3866 |  |  |  |  |  |  | } | 
| 3867 |  |  |  |  |  |  | } | 
| 3868 | 473 |  |  |  |  | 1578 | return \%tagInfoHash; | 
| 3869 |  |  |  |  |  |  | } | 
| 3870 |  |  |  |  |  |  |  | 
| 3871 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3872 |  |  |  |  |  |  | # Get a tagInfo/tagID hash for subdirectories we need to add | 
| 3873 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) parent tag table reference | 
| 3874 |  |  |  |  |  |  | #         2) parent directory name (taken from GROUP0 of tag table if not defined) | 
| 3875 |  |  |  |  |  |  | # Returns: Reference to Hash of subdirectory tagInfo references keyed by tagID | 
| 3876 |  |  |  |  |  |  | #          (plus Reference to edit directory hash in list context) | 
| 3877 |  |  |  |  |  |  | sub GetAddDirHash($$;$) | 
| 3878 |  |  |  |  |  |  | { | 
| 3879 | 459 |  |  | 459 | 0 | 1392 | my ($self, $tagTablePtr, $parent) = @_; | 
| 3880 | 459 | 100 |  |  |  | 1423 | $parent or $parent = $$tagTablePtr{GROUPS}{0}; | 
| 3881 | 459 |  |  |  |  | 1801 | my $tagID; | 
| 3882 |  |  |  |  |  |  | my %addDirHash; | 
| 3883 | 459 |  |  |  |  | 0 | my %editDirHash; | 
| 3884 | 459 |  |  |  |  | 1191 | my $addDirs = $$self{ADD_DIRS}; | 
| 3885 | 459 |  |  |  |  | 1087 | my $editDirs = $$self{EDIT_DIRS}; | 
| 3886 | 459 |  |  |  |  | 1743 | foreach $tagID (TagTableKeys($tagTablePtr)) { | 
| 3887 | 150141 |  |  |  |  | 267010 | my @infoArray = GetTagInfoList($tagTablePtr,$tagID); | 
| 3888 | 150141 |  |  |  |  | 195924 | my $tagInfo; | 
| 3889 | 150141 |  |  |  |  | 219244 | foreach $tagInfo (@infoArray) { | 
| 3890 | 185743 | 100 |  |  |  | 453884 | next unless $$tagInfo{SubDirectory}; | 
| 3891 |  |  |  |  |  |  | # get name for this sub directory | 
| 3892 |  |  |  |  |  |  | # (take directory name from SubDirectory DirName if it exists, | 
| 3893 |  |  |  |  |  |  | #  otherwise Group0 name of SubDirectory TagTable or tag Group1 name) | 
| 3894 | 34424 |  |  |  |  | 61318 | my $dirName = $$tagInfo{SubDirectory}{DirName}; | 
| 3895 | 34424 | 100 |  |  |  | 55127 | unless ($dirName) { | 
| 3896 |  |  |  |  |  |  | # use tag name for directory name and save for next time | 
| 3897 | 3794 |  |  |  |  | 7286 | $dirName = $$tagInfo{Name}; | 
| 3898 | 3794 |  |  |  |  | 6389 | $$tagInfo{SubDirectory}{DirName} = $dirName; | 
| 3899 |  |  |  |  |  |  | } | 
| 3900 |  |  |  |  |  |  | # save this directory information if we are writing it | 
| 3901 | 34424 | 100 | 100 |  |  | 77580 | if ($$editDirs{$dirName} and $$editDirs{$dirName} eq $parent) { | 
| 3902 | 252 |  |  |  |  | 877 | $editDirHash{$tagID} = $tagInfo; | 
| 3903 | 252 | 100 |  |  |  | 1208 | $addDirHash{$tagID} = $tagInfo if $$addDirs{$dirName}; | 
| 3904 |  |  |  |  |  |  | } | 
| 3905 |  |  |  |  |  |  | } | 
| 3906 |  |  |  |  |  |  | } | 
| 3907 | 459 | 100 |  |  |  | 7188 | return (\%addDirHash, \%editDirHash) if wantarray; | 
| 3908 | 384 |  |  |  |  | 2051 | return \%addDirHash; | 
| 3909 |  |  |  |  |  |  | } | 
| 3910 |  |  |  |  |  |  |  | 
| 3911 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3912 |  |  |  |  |  |  | # Get localized version of tagInfo hash (used by MIE, XMP, PNG and QuickTime) | 
| 3913 |  |  |  |  |  |  | # Inputs: 0) tagInfo hash ref, 1) locale code (eg. "en_CA" for MIE) | 
| 3914 |  |  |  |  |  |  | # Returns: new tagInfo hash ref, or undef if invalid | 
| 3915 |  |  |  |  |  |  | # - sets LangCode member in new tagInfo | 
| 3916 |  |  |  |  |  |  | sub GetLangInfo($$) | 
| 3917 |  |  |  |  |  |  | { | 
| 3918 | 298 |  |  | 298 | 0 | 650 | my ($tagInfo, $langCode) = @_; | 
| 3919 |  |  |  |  |  |  | # make a new tagInfo hash for this locale | 
| 3920 | 298 |  |  |  |  | 617 | my $table = $$tagInfo{Table}; | 
| 3921 | 298 |  |  |  |  | 829 | my $tagID = $$tagInfo{TagID} . '-' . $langCode; | 
| 3922 | 298 |  |  |  |  | 654 | my $langInfo = $$table{$tagID}; | 
| 3923 | 298 | 100 |  |  |  | 734 | unless ($langInfo) { | 
| 3924 |  |  |  |  |  |  | # make a new tagInfo entry for this locale | 
| 3925 |  |  |  |  |  |  | $langInfo = { | 
| 3926 |  |  |  |  |  |  | %$tagInfo, | 
| 3927 |  |  |  |  |  |  | Name => $$tagInfo{Name} . '-' . $langCode, | 
| 3928 | 182 |  |  |  |  | 1343 | Description => Image::ExifTool::MakeDescription($$tagInfo{Name}) . | 
| 3929 |  |  |  |  |  |  | " ($langCode)", | 
| 3930 |  |  |  |  |  |  | LangCode => $langCode, | 
| 3931 |  |  |  |  |  |  | SrcTagInfo => $tagInfo, # save reference to original tagInfo | 
| 3932 |  |  |  |  |  |  | }; | 
| 3933 | 182 |  |  |  |  | 633 | AddTagToTable($table, $tagID, $langInfo); | 
| 3934 |  |  |  |  |  |  | } | 
| 3935 | 298 |  |  |  |  | 777 | return $langInfo; | 
| 3936 |  |  |  |  |  |  | } | 
| 3937 |  |  |  |  |  |  |  | 
| 3938 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3939 |  |  |  |  |  |  | # initialize ADD_DIRS and EDIT_DIRS hashes for all directories that need | 
| 3940 |  |  |  |  |  |  | # to be created or will have tags changed in them | 
| 3941 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) file type string (or map hash ref) | 
| 3942 |  |  |  |  |  |  | #         2) preferred family 0 group for creating tags, 3) alternate preferred group | 
| 3943 |  |  |  |  |  |  | # Notes: | 
| 3944 |  |  |  |  |  |  | # - the ADD_DIRS and EDIT_DIRS keys are the directory names, and the values | 
| 3945 |  |  |  |  |  |  | #   are the names of the parent directories (undefined for a top-level directory) | 
| 3946 |  |  |  |  |  |  | # - also initializes FORCE_WRITE lookup | 
| 3947 |  |  |  |  |  |  | sub InitWriteDirs($$;$$) | 
| 3948 |  |  |  |  |  |  | { | 
| 3949 | 317 |  |  | 317 | 0 | 1235 | my ($self, $fileType, $preferredGroup, $altGroup) = @_; | 
| 3950 | 317 |  |  |  |  | 1460 | my $editDirs = $$self{EDIT_DIRS} = { }; | 
| 3951 | 317 |  |  |  |  | 1194 | my $addDirs = $$self{ADD_DIRS} = { }; | 
| 3952 | 317 |  |  |  |  | 1164 | my $fileDirs = $dirMap{$fileType}; | 
| 3953 | 317 | 100 |  |  |  | 1118 | unless ($fileDirs) { | 
| 3954 | 192 | 100 |  |  |  | 782 | return unless ref $fileType eq 'HASH'; | 
| 3955 | 80 |  |  |  |  | 270 | $fileDirs = $fileType; | 
| 3956 |  |  |  |  |  |  | } | 
| 3957 | 205 |  |  |  |  | 1204 | my @tagInfoList = $self->GetNewTagInfoList(); | 
| 3958 | 205 |  |  |  |  | 688 | my ($tagInfo, $nvHash); | 
| 3959 |  |  |  |  |  |  |  | 
| 3960 |  |  |  |  |  |  | # save the preferred group | 
| 3961 | 205 |  |  |  |  | 803 | $$self{PreferredGroup} = $preferredGroup; | 
| 3962 |  |  |  |  |  |  |  | 
| 3963 | 205 |  |  |  |  | 626 | foreach $tagInfo (@tagInfoList) { | 
| 3964 |  |  |  |  |  |  | # cycle through all hashes in linked list | 
| 3965 | 12929 |  |  |  |  | 25154 | for ($nvHash=$self->GetNewValueHash($tagInfo); $nvHash; $nvHash=$$nvHash{Next}) { | 
| 3966 |  |  |  |  |  |  | # are we creating this tag? (otherwise just deleting or editing it) | 
| 3967 | 12955 |  |  |  |  | 25936 | my $isCreating = $$nvHash{IsCreating}; | 
| 3968 | 12955 | 100 |  |  |  | 22481 | if ($preferredGroup) { | 
| 3969 | 3524 |  |  |  |  | 8237 | my $g0 = $self->GetGroup($tagInfo, 0); | 
| 3970 | 3524 | 100 |  |  |  | 6824 | if ($isCreating) { | 
| 3971 |  |  |  |  |  |  | # if another group is taking priority, only create | 
| 3972 |  |  |  |  |  |  | # directory if specifically adding tags to this group | 
| 3973 |  |  |  |  |  |  | # or if this tag isn't being added to the priority group | 
| 3974 |  |  |  |  |  |  | $isCreating = 0 if $preferredGroup ne $g0 and | 
| 3975 | 825 | 100 | 100 |  |  | 3871 | $$nvHash{CreateGroups}{$preferredGroup} and | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 3976 |  |  |  |  |  |  | (not $altGroup or $altGroup ne $g0); | 
| 3977 |  |  |  |  |  |  | } else { | 
| 3978 |  |  |  |  |  |  | # create this directory if any tag is preferred and has a value | 
| 3979 |  |  |  |  |  |  | # (unless group creation is disabled via the WriteMode option) | 
| 3980 |  |  |  |  |  |  | $isCreating = 1 if $$nvHash{Value} and $preferredGroup eq $g0 and | 
| 3981 | 2699 | 50 | 100 |  |  | 10902 | not $$nvHash{EditOnly} and $$self{OPTIONS}{WriteMode} =~ /g/; | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 3982 |  |  |  |  |  |  | } | 
| 3983 |  |  |  |  |  |  | } | 
| 3984 |  |  |  |  |  |  | # tag belongs to directory specified by WriteGroup, or by | 
| 3985 |  |  |  |  |  |  | # the Group0 name if WriteGroup not defined | 
| 3986 | 12955 |  |  |  |  | 28389 | my $dirName = $$nvHash{WriteGroup}; | 
| 3987 |  |  |  |  |  |  | # remove MIE copy number(s) if they exist | 
| 3988 | 12955 | 100 |  |  |  | 29765 | if ($dirName =~ /^MIE\d*(-[a-z]+)?\d*$/i) { | 
| 3989 | 387 |  | 50 |  |  | 1762 | $dirName = 'MIE' . ($1 || ''); | 
| 3990 |  |  |  |  |  |  | } | 
| 3991 | 12955 |  |  |  |  | 17568 | my @dirNames; | 
| 3992 |  |  |  |  |  |  | # allow a group name of '*' to force writing EXIF/IPTC/XMP/PNG (ForceWrite tag) | 
| 3993 | 12955 | 50 | 33 |  |  | 32870 | if ($dirName eq '*' and $$nvHash{Value}) { | 
|  |  | 100 |  |  |  |  |  | 
| 3994 | 0 |  |  |  |  | 0 | my $val = $$nvHash{Value}[0]; | 
| 3995 | 0 | 0 |  |  |  | 0 | if ($val) { | 
| 3996 | 0 |  |  |  |  | 0 | foreach (qw(EXIF IPTC XMP PNG FixBase)) { | 
| 3997 | 0 | 0 |  |  |  | 0 | next unless $val =~ /\b($_|All)\b/i; | 
| 3998 | 0 |  |  |  |  | 0 | push @dirNames, $_; | 
| 3999 | 0 | 0 |  |  |  | 0 | push @dirNames, 'EXIF' if $_ eq 'FixBase'; | 
| 4000 | 0 |  |  |  |  | 0 | $$self{FORCE_WRITE}{$_} = 1; | 
| 4001 |  |  |  |  |  |  | } | 
| 4002 |  |  |  |  |  |  | } | 
| 4003 | 0 |  |  |  |  | 0 | $dirName = shift @dirNames; | 
| 4004 |  |  |  |  |  |  | } elsif ($dirName eq 'QuickTime') { | 
| 4005 |  |  |  |  |  |  | # write to specific QuickTime group | 
| 4006 | 46 |  |  |  |  | 239 | $dirName = $self->GetGroup($tagInfo, 1); | 
| 4007 |  |  |  |  |  |  | } | 
| 4008 | 12955 |  |  |  |  | 22373 | while ($dirName) { | 
| 4009 | 52584 |  |  |  |  | 78871 | my $parent = $$fileDirs{$dirName}; | 
| 4010 | 52584 | 100 |  |  |  | 89505 | if (ref $parent) { | 
| 4011 | 6351 |  |  |  |  | 12475 | push @dirNames, reverse @$parent; | 
| 4012 | 6351 |  |  |  |  | 10200 | $parent = pop @dirNames; | 
| 4013 |  |  |  |  |  |  | } | 
| 4014 | 52584 |  |  |  |  | 77878 | $$editDirs{$dirName} = $parent; | 
| 4015 | 52584 | 100 | 100 |  |  | 98928 | $$addDirs{$dirName} = $parent if $isCreating and $isCreating != 2; | 
| 4016 | 52584 |  | 100 |  |  | 141919 | $dirName = $parent || shift @dirNames | 
| 4017 |  |  |  |  |  |  | } | 
| 4018 |  |  |  |  |  |  | } | 
| 4019 |  |  |  |  |  |  | } | 
| 4020 | 205 | 100 |  |  |  | 618 | if (%{$$self{DEL_GROUP}}) { | 
|  | 205 |  |  |  |  | 1101 |  | 
| 4021 |  |  |  |  |  |  | # add delete groups to list of edited groups | 
| 4022 | 37 |  |  |  |  | 86 | foreach (keys %{$$self{DEL_GROUP}}) { | 
|  | 37 |  |  |  |  | 313 |  | 
| 4023 | 843 | 100 |  |  |  | 1581 | next if /^-/;   # ignore excluded groups | 
| 4024 | 841 |  |  |  |  | 1188 | my $dirName = $_; | 
| 4025 |  |  |  |  |  |  | # translate necessary group 0 names | 
| 4026 | 841 | 100 |  |  |  | 1630 | $dirName = $translateWriteGroup{$dirName} if $translateWriteGroup{$dirName}; | 
| 4027 |  |  |  |  |  |  | # convert XMP group 1 names | 
| 4028 | 841 | 100 |  |  |  | 1549 | $dirName = 'XMP' if $dirName =~ /^XMP-/; | 
| 4029 | 841 |  |  |  |  | 1083 | my @dirNames; | 
| 4030 | 841 |  |  |  |  | 1416 | while ($dirName) { | 
| 4031 | 1199 |  |  |  |  | 1825 | my $parent = $$fileDirs{$dirName}; | 
| 4032 | 1199 | 100 |  |  |  | 2126 | if (ref $parent) { | 
| 4033 | 13 |  |  |  |  | 39 | push @dirNames, reverse @$parent; | 
| 4034 | 13 |  |  |  |  | 26 | $parent = pop @dirNames; | 
| 4035 |  |  |  |  |  |  | } | 
| 4036 | 1199 |  |  |  |  | 1982 | $$editDirs{$dirName} = $parent; | 
| 4037 | 1199 |  | 100 |  |  | 3358 | $dirName = $parent || shift @dirNames | 
| 4038 |  |  |  |  |  |  | } | 
| 4039 |  |  |  |  |  |  | } | 
| 4040 |  |  |  |  |  |  | } | 
| 4041 |  |  |  |  |  |  | # special case to edit JFIF to get resolutions if editing EXIF information | 
| 4042 | 205 | 100 | 100 |  |  | 1651 | if ($$editDirs{IFD0} and $$fileDirs{JFIF}) { | 
| 4043 | 86 |  |  |  |  | 401 | $$editDirs{JFIF} = 'IFD1'; | 
| 4044 | 86 |  |  |  |  | 680 | $$editDirs{APP0} = undef; | 
| 4045 |  |  |  |  |  |  | } | 
| 4046 |  |  |  |  |  |  |  | 
| 4047 | 205 | 100 |  |  |  | 2657 | if ($$self{OPTIONS}{Verbose}) { | 
| 4048 | 2 |  |  |  |  | 9 | my $out = $$self{OPTIONS}{TextOut}; | 
| 4049 | 2 |  |  |  |  | 9 | print $out "  Editing tags in: "; | 
| 4050 | 2 |  |  |  |  | 18 | foreach (sort keys %$editDirs) { print $out "$_ "; } | 
|  | 11 |  |  |  |  | 22 |  | 
| 4051 | 2 |  |  |  |  | 17 | print $out "\n"; | 
| 4052 | 2 | 50 |  |  |  | 13 | return unless $$self{OPTIONS}{Verbose} > 1; | 
| 4053 | 2 |  |  |  |  | 5 | print $out "  Creating tags in: "; | 
| 4054 | 2 |  |  |  |  | 10 | foreach (sort keys %$addDirs) { print $out "$_ "; } | 
|  | 7 |  |  |  |  | 18 |  | 
| 4055 | 2 |  |  |  |  | 12 | print $out "\n"; | 
| 4056 |  |  |  |  |  |  | } | 
| 4057 |  |  |  |  |  |  | } | 
| 4058 |  |  |  |  |  |  |  | 
| 4059 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4060 |  |  |  |  |  |  | # Write an image directory | 
| 4061 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) source directory information reference | 
| 4062 |  |  |  |  |  |  | #         2) tag table reference, 3) optional reference to writing procedure | 
| 4063 |  |  |  |  |  |  | # Returns: New directory data or undefined on error (or empty string to delete directory) | 
| 4064 |  |  |  |  |  |  | sub WriteDirectory($$$;$) | 
| 4065 |  |  |  |  |  |  | { | 
| 4066 | 1715 |  |  | 1715 | 0 | 5800 | my ($self, $dirInfo, $tagTablePtr, $writeProc) = @_; | 
| 4067 | 1715 |  |  |  |  | 3894 | my ($out, $nvHash, $delFlag); | 
| 4068 |  |  |  |  |  |  |  | 
| 4069 | 1715 | 50 |  |  |  | 4150 | $tagTablePtr or return undef; | 
| 4070 | 1715 | 100 |  |  |  | 5578 | $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose}; | 
| 4071 |  |  |  |  |  |  | # set directory name from default group0 name if not done already | 
| 4072 | 1715 |  |  |  |  | 3554 | my $dirName = $$dirInfo{DirName}; | 
| 4073 | 1715 |  |  |  |  | 2977 | my $dataPt = $$dirInfo{DataPt}; | 
| 4074 | 1715 |  |  |  |  | 6160 | my $grp0 = $$tagTablePtr{GROUPS}{0}; | 
| 4075 | 1715 | 100 |  |  |  | 4522 | $dirName or $dirName = $$dirInfo{DirName} = $grp0; | 
| 4076 | 1715 | 100 |  |  |  | 2849 | if (%{$$self{DEL_GROUP}}) { | 
|  | 1715 |  |  |  |  | 4938 |  | 
| 4077 | 207 |  |  |  |  | 412 | my $delGroup = $$self{DEL_GROUP}; | 
| 4078 |  |  |  |  |  |  | # delete entire directory if specified | 
| 4079 | 207 |  |  |  |  | 447 | my $grp1 = $dirName; | 
| 4080 | 207 | 100 | 100 |  |  | 874 | $delFlag = ($$delGroup{$grp0} or $$delGroup{$grp1}) unless $permanentDir{$grp0}; | 
| 4081 |  |  |  |  |  |  | # (never delete an entire QuickTime group) | 
| 4082 | 207 | 100 |  |  |  | 570 | if ($delFlag) { | 
| 4083 | 40 | 50 | 100 |  |  | 496 | if (($grp0 =~ /^(MakerNotes)$/ or $grp1 =~ /^(IFD0|ExifIFD|MakerNotes)$/) and | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 4084 |  |  |  |  |  |  | $self->IsRawType() and | 
| 4085 |  |  |  |  |  |  | # allow non-permanent MakerNote directories to be deleted (ie. NikonCapture) | 
| 4086 |  |  |  |  |  |  | (not $$dirInfo{TagInfo} or not defined $$dirInfo{TagInfo}{Permanent} or | 
| 4087 |  |  |  |  |  |  | $$dirInfo{TagInfo}{Permanent})) | 
| 4088 |  |  |  |  |  |  | { | 
| 4089 | 0 |  |  |  |  | 0 | $self->WarnOnce("Can't delete $1 from $$self{FileType}",1); | 
| 4090 | 0 |  |  |  |  | 0 | undef $grp1; | 
| 4091 |  |  |  |  |  |  | } elsif (not $blockExifTypes{$$self{FILE_TYPE}}) { | 
| 4092 |  |  |  |  |  |  | # restrict delete logic to prevent entire tiff image from being killed | 
| 4093 |  |  |  |  |  |  | # (don't allow IFD0 to be deleted, and delete only ExifIFD if EXIF specified) | 
| 4094 | 10 | 50 | 33 |  |  | 146 | if ($$self{FILE_TYPE} eq 'PSD') { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 4095 |  |  |  |  |  |  | # don't delete Photoshop directories from PSD image | 
| 4096 | 0 | 0 |  |  |  | 0 | undef $grp1 if $grp0 eq 'Photoshop'; | 
| 4097 |  |  |  |  |  |  | } elsif ($$self{FILE_TYPE} =~ /^(EPS|PS)$/) { | 
| 4098 |  |  |  |  |  |  | # allow anything to be deleted from PostScript files | 
| 4099 |  |  |  |  |  |  | } elsif ($grp1 eq 'IFD0') { | 
| 4100 | 0 |  | 0 |  |  | 0 | my $type = $$self{TIFF_TYPE} || $$self{FILE_TYPE}; | 
| 4101 | 0 | 0 |  |  |  | 0 | $$delGroup{IFD0} and $self->Warn("Can't delete IFD0 from $type",1); | 
| 4102 | 0 |  |  |  |  | 0 | undef $grp1; | 
| 4103 |  |  |  |  |  |  | } elsif ($grp0 eq 'EXIF' and $$delGroup{$grp0}) { | 
| 4104 | 0 | 0 | 0 |  |  | 0 | undef $grp1 unless $$delGroup{$grp1} or $grp1 eq 'ExifIFD'; | 
| 4105 |  |  |  |  |  |  | } | 
| 4106 |  |  |  |  |  |  | } | 
| 4107 | 40 | 50 |  |  |  | 135 | if ($grp1) { | 
| 4108 | 40 | 100 | 66 |  |  | 183 | if ($dataPt or $$dirInfo{RAF}) { | 
| 4109 | 30 |  |  |  |  | 73 | ++$$self{CHANGED}; | 
| 4110 | 30 | 100 |  |  |  | 92 | $out and print $out "  Deleting $grp1\n"; | 
| 4111 | 30 | 100 |  |  |  | 135 | $self->Warn('ICC_Profile deleted. Image colors may be affected') if $grp1 eq 'ICC_Profile'; | 
| 4112 |  |  |  |  |  |  | # can no longer validate TIFF_END if deleting an entire IFD | 
| 4113 | 30 | 100 |  |  |  | 143 | delete $$self{TIFF_END} if $dirName =~ /IFD/; | 
| 4114 |  |  |  |  |  |  | } | 
| 4115 |  |  |  |  |  |  | # don't add back into the wrong location | 
| 4116 | 40 |  |  |  |  | 122 | my $right = $$self{ADD_DIRS}{$grp1}; | 
| 4117 |  |  |  |  |  |  | # (take care because EXIF directory name may be either EXIF or IFD0, | 
| 4118 |  |  |  |  |  |  | #  but IFD0 will be the one that appears in the directory map) | 
| 4119 | 40 | 100 | 100 |  |  | 191 | $right = $$self{ADD_DIRS}{IFD0} if not $right and $grp1 eq 'EXIF'; | 
| 4120 | 40 | 100 | 100 |  |  | 190 | if ($delFlag == 2 and $right) { | 
| 4121 |  |  |  |  |  |  | # also check grandparent because some routines create 2 levels in 1 | 
| 4122 | 21 |  | 100 |  |  | 99 | my $right2 = $$self{ADD_DIRS}{$right} || ''; | 
| 4123 | 21 |  |  |  |  | 49 | my $parent = $$dirInfo{Parent}; | 
| 4124 | 21 | 50 | 66 |  |  | 132 | if (not $parent or $parent eq $right or $parent eq $right2) { | 
|  |  |  | 33 |  |  |  |  | 
| 4125 |  |  |  |  |  |  | # prevent duplicate directories from being recreated at the same path | 
| 4126 | 21 |  |  |  |  | 43 | my $path = join '-', @{$$self{PATH}}, $dirName; | 
|  | 21 |  |  |  |  | 90 |  | 
| 4127 | 21 | 100 |  |  |  | 98 | $$self{Recreated} or $$self{Recreated} = { }; | 
| 4128 | 21 | 50 |  |  |  | 80 | if ($$self{Recreated}{$path}) { | 
| 4129 | 0 | 0 |  |  |  | 0 | my $p = $parent ? " in $parent" : ''; | 
| 4130 | 0 |  |  |  |  | 0 | $self->Warn("Not recreating duplicate $grp1$p",1); | 
| 4131 | 0 |  |  |  |  | 0 | return ''; | 
| 4132 |  |  |  |  |  |  | } | 
| 4133 | 21 |  |  |  |  | 81 | $$self{Recreated}{$path} = 1; | 
| 4134 |  |  |  |  |  |  | # empty the directory | 
| 4135 | 21 |  |  |  |  | 51 | my $data = ''; | 
| 4136 | 21 |  |  |  |  | 59 | $$dirInfo{DataPt}   = \$data; | 
| 4137 | 21 |  |  |  |  | 53 | $$dirInfo{DataLen}  = 0; | 
| 4138 | 21 |  |  |  |  | 53 | $$dirInfo{DirStart} = 0; | 
| 4139 | 21 |  |  |  |  | 48 | $$dirInfo{DirLen}   = 0; | 
| 4140 | 21 |  |  |  |  | 56 | delete $$dirInfo{RAF}; | 
| 4141 | 21 |  |  |  |  | 44 | delete $$dirInfo{Base}; | 
| 4142 | 21 |  |  |  |  | 71 | delete $$dirInfo{DataPos}; | 
| 4143 |  |  |  |  |  |  | } else { | 
| 4144 | 0 |  |  |  |  | 0 | $self->Warn("Not recreating $grp1 in $parent (should be in $right)",1); | 
| 4145 | 0 |  |  |  |  | 0 | return ''; | 
| 4146 |  |  |  |  |  |  | } | 
| 4147 |  |  |  |  |  |  | } else { | 
| 4148 | 19 | 100 |  |  |  | 128 | return '' unless $$dirInfo{NoDelete}; | 
| 4149 |  |  |  |  |  |  | } | 
| 4150 |  |  |  |  |  |  | } | 
| 4151 |  |  |  |  |  |  | } | 
| 4152 |  |  |  |  |  |  | } | 
| 4153 |  |  |  |  |  |  | # use default proc from tag table if no proc specified | 
| 4154 | 1697 | 100 | 100 |  |  | 8469 | $writeProc or $writeProc = $$tagTablePtr{WRITE_PROC} or return undef; | 
| 4155 |  |  |  |  |  |  |  | 
| 4156 |  |  |  |  |  |  | # are we rewriting a pre-existing directory? | 
| 4157 | 1466 |  | 100 |  |  | 6209 | my $isRewriting = ($$dirInfo{DirLen} or (defined $dataPt and length $$dataPt) or $$dirInfo{RAF}); | 
| 4158 |  |  |  |  |  |  |  | 
| 4159 |  |  |  |  |  |  | # copy or delete new directory as a block if specified | 
| 4160 | 1466 |  |  |  |  | 2636 | my $blockName = $dirName; | 
| 4161 | 1466 | 100 |  |  |  | 3744 | $blockName = 'EXIF' if $blockName eq 'IFD0'; | 
| 4162 | 1466 |  | 100 |  |  | 6026 | my $tagInfo = $Image::ExifTool::Extra{$blockName} || $$dirInfo{TagInfo}; | 
| 4163 | 1466 |  | 100 |  |  | 7534 | while ($tagInfo and ($nvHash = $$self{NEW_VALUE}{$tagInfo}) and | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 4164 |  |  |  |  |  |  | $self->IsOverwriting($nvHash) and not ($$nvHash{CreateOnly} and $isRewriting)) | 
| 4165 |  |  |  |  |  |  | { | 
| 4166 |  |  |  |  |  |  | # protect against writing EXIF to wrong file types, etc | 
| 4167 | 13 | 100 |  |  |  | 72 | if ($blockName eq 'EXIF') { | 
| 4168 | 1 | 50 |  |  |  | 6 | unless ($blockExifTypes{$$self{FILE_TYPE}}) { | 
| 4169 | 0 |  |  |  |  | 0 | $self->Warn("Can't write EXIF as a block to $$self{FILE_TYPE} file"); | 
| 4170 | 0 |  |  |  |  | 0 | last; | 
| 4171 |  |  |  |  |  |  | } | 
| 4172 |  |  |  |  |  |  | # this can happen if we call WriteDirectory for an EXIF directory without going | 
| 4173 |  |  |  |  |  |  | # through WriteTIFF as the WriteProc (which happens if conditionally replacing | 
| 4174 |  |  |  |  |  |  | # the EXIF block and the condition fails), but we never want to do a block write | 
| 4175 |  |  |  |  |  |  | # in this case because the EXIF block would end up with two TIFF headers | 
| 4176 | 1 | 50 |  |  |  | 6 | last unless $writeProc eq \&Image::ExifTool::WriteTIFF; | 
| 4177 |  |  |  |  |  |  | } | 
| 4178 | 13 | 100 |  |  |  | 69 | last unless $self->IsOverwriting($nvHash, $dataPt ? $$dataPt : ''); | 
|  |  | 50 |  |  |  |  |  | 
| 4179 | 13 |  |  |  |  | 41 | my $verb = 'Writing'; | 
| 4180 | 13 |  |  |  |  | 57 | my $newVal = $self->GetNewValue($nvHash); | 
| 4181 | 13 | 50 | 33 |  |  | 118 | unless (defined $newVal and length $newVal) { | 
| 4182 | 0 | 0 | 0 |  |  | 0 | return '' unless $dataPt or $$dirInfo{RAF}; # nothing to do if block never existed | 
| 4183 |  |  |  |  |  |  | # don't allow MakerNotes to be removed from RAW files | 
| 4184 | 0 | 0 | 0 |  |  | 0 | if ($blockName eq 'MakerNotes' and $rawType{$$self{FileType}}) { | 
| 4185 | 0 |  |  |  |  | 0 | $self->Warn("Can't delete MakerNotes from $$self{VALUE}{FileType}",1); | 
| 4186 | 0 |  |  |  |  | 0 | return undef; | 
| 4187 |  |  |  |  |  |  | } | 
| 4188 | 0 |  |  |  |  | 0 | $verb = 'Deleting'; | 
| 4189 | 0 |  |  |  |  | 0 | $newVal = ''; | 
| 4190 |  |  |  |  |  |  | } | 
| 4191 | 13 |  |  |  |  | 55 | $$dirInfo{BlockWrite} = 1;  # set flag indicating we did a block write | 
| 4192 | 13 | 50 |  |  |  | 67 | $out and print $out "  $verb $blockName as a block\n"; | 
| 4193 | 13 |  |  |  |  | 38 | ++$$self{CHANGED}; | 
| 4194 | 13 |  |  |  |  | 59 | return $newVal; | 
| 4195 |  |  |  |  |  |  | } | 
| 4196 |  |  |  |  |  |  | # guard against writing the same directory twice | 
| 4197 | 1453 | 100 | 100 |  |  | 10527 | if (defined $dataPt and defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos} and | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 4198 |  |  |  |  |  |  | not $$dirInfo{NoRefTest}) | 
| 4199 |  |  |  |  |  |  | { | 
| 4200 | 680 |  | 100 |  |  | 3205 | my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0) + $$self{BASE}; | 
| 4201 |  |  |  |  |  |  | # (Phase One P25 IIQ files have ICC_Profile duplicated in IFD0 and IFD1) | 
| 4202 | 680 | 50 | 0 |  |  | 2895 | if ($$self{PROCESSED}{$addr} and ($dirName ne 'ICC_Profile' or $$self{TIFF_TYPE} ne 'IIQ')) { | 
|  |  |  | 33 |  |  |  |  | 
| 4203 | 0 | 0 | 0 |  |  | 0 | if (defined $$dirInfo{DirLen} and not $$dirInfo{DirLen} and $dirName ne $$self{PROCESSED}{$addr}) { | 
|  |  | 0 | 0 |  |  |  |  | 
| 4204 |  |  |  |  |  |  | # it is hypothetically possible to have 2 different directories | 
| 4205 |  |  |  |  |  |  | # with the same address if one has a length of zero | 
| 4206 |  |  |  |  |  |  | } elsif ($self->Error("$dirName pointer references previous $$self{PROCESSED}{$addr} directory", 2)) { | 
| 4207 | 0 |  |  |  |  | 0 | return undef; | 
| 4208 |  |  |  |  |  |  | } else { | 
| 4209 | 0 |  |  |  |  | 0 | $self->Warn("Deleting duplicate $dirName directory"); | 
| 4210 | 0 | 0 |  |  |  | 0 | $out and print $out "  Deleting $dirName\n"; | 
| 4211 |  |  |  |  |  |  | # delete the duplicate directory (don't recreate it when writing new | 
| 4212 |  |  |  |  |  |  | # tags to prevent propagating a duplicate IFD in cases like when the | 
| 4213 |  |  |  |  |  |  | # same ExifIFD exists in both IFD0 and IFD1) | 
| 4214 | 0 |  |  |  |  | 0 | return ''; | 
| 4215 |  |  |  |  |  |  | } | 
| 4216 |  |  |  |  |  |  | } else { | 
| 4217 | 680 |  |  |  |  | 2259 | $$self{PROCESSED}{$addr} = $dirName; | 
| 4218 |  |  |  |  |  |  | } | 
| 4219 |  |  |  |  |  |  | } | 
| 4220 | 1453 |  |  |  |  | 3303 | my $oldDir = $$self{DIR_NAME}; | 
| 4221 | 1453 |  |  |  |  | 4419 | my @save = @$self{'Compression','SubfileType'}; | 
| 4222 | 1453 |  |  |  |  | 2701 | my $name; | 
| 4223 | 1453 | 100 |  |  |  | 3470 | if ($out) { | 
| 4224 |  |  |  |  |  |  | $name = ($dirName eq 'MakerNotes' and $$dirInfo{TagInfo}) ? | 
| 4225 | 4 | 50 | 33 |  |  | 19 | $$dirInfo{TagInfo}{Name} : $dirName; | 
| 4226 | 4 | 100 | 100 |  |  | 36 | if (not defined $oldDir or $oldDir ne $name) { | 
| 4227 | 3 | 100 |  |  |  | 13 | my $verb = $isRewriting ? 'Rewriting' : 'Creating'; | 
| 4228 | 3 |  |  |  |  | 17 | print $out "  $verb $name\n"; | 
| 4229 |  |  |  |  |  |  | } | 
| 4230 |  |  |  |  |  |  | } | 
| 4231 | 1453 |  |  |  |  | 4250 | my $saveOrder = GetByteOrder(); | 
| 4232 | 1453 |  |  |  |  | 3333 | my $oldChanged = $$self{CHANGED}; | 
| 4233 | 1453 |  |  |  |  | 2891 | $$self{DIR_NAME} = $dirName; | 
| 4234 | 1453 |  |  |  |  | 2382 | push @{$$self{PATH}}, $dirName; | 
|  | 1453 |  |  |  |  | 3634 |  | 
| 4235 | 1453 |  |  |  |  | 3200 | $$dirInfo{IsWriting} = 1; | 
| 4236 | 1453 |  |  |  |  | 2434 | my $newData; | 
| 4237 |  |  |  |  |  |  | { | 
| 4238 | 58 |  |  | 58 |  | 558 | no strict 'refs'; | 
|  | 58 |  |  |  |  | 196 |  | 
|  | 58 |  |  |  |  | 1227369 |  | 
|  | 1453 |  |  |  |  | 2197 |  | 
| 4239 | 1453 |  |  |  |  | 11321 | $newData = &$writeProc($self, $dirInfo, $tagTablePtr); | 
| 4240 |  |  |  |  |  |  | } | 
| 4241 | 1453 |  |  |  |  | 2876 | pop @{$$self{PATH}}; | 
|  | 1453 |  |  |  |  | 3587 |  | 
| 4242 |  |  |  |  |  |  | # nothing changed if error occurred or nothing was created | 
| 4243 | 1453 | 100 | 100 |  |  | 6688 | $$self{CHANGED} = $oldChanged unless defined $newData and (length($newData) or $isRewriting); | 
|  |  |  | 100 |  |  |  |  | 
| 4244 | 1453 |  |  |  |  | 3429 | $$self{DIR_NAME} = $oldDir; | 
| 4245 | 1453 |  |  |  |  | 4097 | @$self{'Compression','SubfileType'} = @save; | 
| 4246 | 1453 |  |  |  |  | 5198 | SetByteOrder($saveOrder); | 
| 4247 | 1453 | 100 |  |  |  | 3761 | if ($out) { | 
| 4248 | 4 | 50 | 33 |  |  | 25 | print $out "  Deleting $name\n" if defined $newData and not length $newData; | 
| 4249 | 4 | 50 | 33 |  |  | 19 | if ($$self{CHANGED} == $oldChanged and $$self{OPTIONS}{Verbose} > 2) { | 
| 4250 | 0 |  |  |  |  | 0 | print $out "$$self{INDENT}  [nothing changed in $dirName]\n"; | 
| 4251 |  |  |  |  |  |  | } | 
| 4252 |  |  |  |  |  |  | } | 
| 4253 | 1453 |  |  |  |  | 6662 | return $newData; | 
| 4254 |  |  |  |  |  |  | } | 
| 4255 |  |  |  |  |  |  |  | 
| 4256 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4257 |  |  |  |  |  |  | # Uncommon utility routines to for reading binary data values | 
| 4258 |  |  |  |  |  |  | # Inputs: 0) data reference, 1) offset into data | 
| 4259 |  |  |  |  |  |  | sub Get64s($$) | 
| 4260 |  |  |  |  |  |  | { | 
| 4261 | 12 |  |  | 12 | 0 | 27 | my ($dataPt, $pos) = @_; | 
| 4262 | 12 | 50 |  |  |  | 26 | my $pt = GetByteOrder() eq 'MM' ? 0 : 4;    # get position of high word | 
| 4263 | 12 |  |  |  |  | 35 | my $hi = Get32s($dataPt, $pos + $pt);       # preserve sign bit of high word | 
| 4264 | 12 |  |  |  |  | 37 | my $lo = Get32u($dataPt, $pos + 4 - $pt); | 
| 4265 | 12 |  |  |  |  | 35 | return $hi * 4294967296 + $lo; | 
| 4266 |  |  |  |  |  |  | } | 
| 4267 |  |  |  |  |  |  | sub Get64u($$) | 
| 4268 |  |  |  |  |  |  | { | 
| 4269 | 183 |  |  | 183 | 0 | 508 | my ($dataPt, $pos) = @_; | 
| 4270 | 183 | 100 |  |  |  | 470 | my $pt = GetByteOrder() eq 'MM' ? 0 : 4;    # get position of high word | 
| 4271 | 183 |  |  |  |  | 599 | my $hi = Get32u($dataPt, $pos + $pt);       # (unsigned this time) | 
| 4272 | 183 |  |  |  |  | 601 | my $lo = Get32u($dataPt, $pos + 4 - $pt); | 
| 4273 | 183 |  |  |  |  | 745 | return $hi * 4294967296 + $lo; | 
| 4274 |  |  |  |  |  |  | } | 
| 4275 |  |  |  |  |  |  | sub GetFixed64s($$) | 
| 4276 |  |  |  |  |  |  | { | 
| 4277 | 0 |  |  | 0 | 0 | 0 | my ($dataPt, $pos) = @_; | 
| 4278 | 0 |  |  |  |  | 0 | my $val = Get64s($dataPt, $pos) / 4294967296; | 
| 4279 |  |  |  |  |  |  | # remove insignificant digits | 
| 4280 | 0 | 0 |  |  |  | 0 | return int($val * 1e10 + ($val>0 ? 0.5 : -0.5)) / 1e10; | 
| 4281 |  |  |  |  |  |  | } | 
| 4282 |  |  |  |  |  |  | # Decode extended 80-bit float used by Apple SANE and Intel 8087 | 
| 4283 |  |  |  |  |  |  | # (note: different than the IEEE standard 80-bit float) | 
| 4284 |  |  |  |  |  |  | sub GetExtended($$) | 
| 4285 |  |  |  |  |  |  | { | 
| 4286 | 1 |  |  | 1 | 0 | 4 | my ($dataPt, $pos) = @_; | 
| 4287 | 1 | 50 |  |  |  | 4 | my $pt = GetByteOrder() eq 'MM' ? 0 : 2;    # get position of exponent | 
| 4288 | 1 |  |  |  |  | 7 | my $exp = Get16u($dataPt, $pos + $pt); | 
| 4289 | 1 |  |  |  |  | 8 | my $sig = Get64u($dataPt, $pos + 2 - $pt);  # get significand as int64u | 
| 4290 | 1 | 50 |  |  |  | 7 | my $sign = $exp & 0x8000 ? -1 : 1; | 
| 4291 | 1 |  |  |  |  | 3 | $exp = ($exp & 0x7fff) - 16383 - 63; # (-63 to fractionalize significand) | 
| 4292 | 1 |  |  |  |  | 14 | return $sign * $sig * 2 ** $exp; | 
| 4293 |  |  |  |  |  |  | } | 
| 4294 |  |  |  |  |  |  |  | 
| 4295 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4296 |  |  |  |  |  |  | # Dump data in hex and ASCII to console | 
| 4297 |  |  |  |  |  |  | # Inputs: 0) data reference, 1) length or undef, 2-N) Options: | 
| 4298 |  |  |  |  |  |  | # Options: Start => offset to start of data (default=0) | 
| 4299 |  |  |  |  |  |  | #          Addr => address to print for data start (default=DataPos+Base+Start) | 
| 4300 |  |  |  |  |  |  | #          DataPos => position of data within block (relative to Base) | 
| 4301 |  |  |  |  |  |  | #          Base => base offset for pointers from start of file | 
| 4302 |  |  |  |  |  |  | #          Width => width of printout (bytes, default=16) | 
| 4303 |  |  |  |  |  |  | #          Prefix => prefix to print at start of line (default='') | 
| 4304 |  |  |  |  |  |  | #          MaxLen => maximum length to dump | 
| 4305 |  |  |  |  |  |  | #          Out => output file reference | 
| 4306 |  |  |  |  |  |  | #          Len => data length | 
| 4307 |  |  |  |  |  |  | sub HexDump($;$%) | 
| 4308 |  |  |  |  |  |  | { | 
| 4309 | 169 |  |  | 169 | 0 | 304 | my $dataPt = shift; | 
| 4310 | 169 |  |  |  |  | 296 | my $len    = shift; | 
| 4311 | 169 |  |  |  |  | 823 | my %opts   = @_; | 
| 4312 | 169 |  | 100 |  |  | 454 | my $start  = $opts{Start}  || 0; | 
| 4313 | 169 |  |  |  |  | 249 | my $addr   = $opts{Addr}; | 
| 4314 | 169 |  | 50 |  |  | 505 | my $wid    = $opts{Width}  || 16; | 
| 4315 | 169 |  | 100 |  |  | 353 | my $prefix = $opts{Prefix} || ''; | 
| 4316 | 169 |  | 50 |  |  | 366 | my $out    = $opts{Out}    || \*STDOUT; | 
| 4317 | 169 |  |  |  |  | 266 | my $maxLen = $opts{MaxLen}; | 
| 4318 | 169 |  |  |  |  | 294 | my $datLen = length($$dataPt) - $start; | 
| 4319 | 169 |  |  |  |  | 245 | my $more; | 
| 4320 | 169 | 50 |  |  |  | 354 | $len = $opts{Len} if defined $opts{Len}; | 
| 4321 |  |  |  |  |  |  |  | 
| 4322 | 169 | 100 | 50 |  |  | 493 | $addr = $start + ($opts{DataPos} || 0) + ($opts{Base} || 0) unless defined $addr; | 
|  |  |  | 50 |  |  |  |  | 
| 4323 | 169 | 100 |  |  |  | 310 | $len = $datLen unless defined $len; | 
| 4324 | 169 | 100 | 66 |  |  | 549 | if ($maxLen and $len > $maxLen) { | 
| 4325 |  |  |  |  |  |  | # print one line less to allow for $more line below | 
| 4326 | 5 |  |  |  |  | 20 | $maxLen = int(($maxLen - 1) / $wid) * $wid; | 
| 4327 | 5 |  |  |  |  | 8 | $more = $len - $maxLen; | 
| 4328 | 5 |  |  |  |  | 9 | $len = $maxLen; | 
| 4329 |  |  |  |  |  |  | } | 
| 4330 | 169 | 50 |  |  |  | 371 | if ($len > $datLen) { | 
| 4331 | 0 |  |  |  |  | 0 | print $out "$prefix    Warning: Attempted dump outside data\n"; | 
| 4332 | 0 |  |  |  |  | 0 | print $out "$prefix    ($len bytes specified, but only $datLen available)\n"; | 
| 4333 | 0 |  |  |  |  | 0 | $len = $datLen; | 
| 4334 |  |  |  |  |  |  | } | 
| 4335 | 169 |  |  |  |  | 531 | my $format = sprintf("%%-%ds", $wid * 3); | 
| 4336 | 169 |  |  |  |  | 398 | my $tmpl = 'H2' x $wid; # ('(H2)*' would have been nice, but older perl versions don't support it) | 
| 4337 | 169 |  |  |  |  | 241 | my $i; | 
| 4338 | 169 |  |  |  |  | 440 | for ($i=0; $i<$len; $i+=$wid) { | 
| 4339 | 228 | 100 |  |  |  | 572 | $wid > $len-$i and $wid = $len-$i, $tmpl = 'H2' x $wid; | 
| 4340 | 228 |  |  |  |  | 833 | printf $out "$prefix%8.4x: ", $addr+$i; | 
| 4341 | 228 |  |  |  |  | 566 | my $dat = substr($$dataPt, $i+$start, $wid); | 
| 4342 | 228 |  |  |  |  | 1022 | my $s = join(' ', unpack($tmpl, $dat)); | 
| 4343 | 228 |  |  |  |  | 748 | printf $out $format, $s; | 
| 4344 | 228 |  |  |  |  | 433 | $dat =~ tr /\x00-\x1f\x7f-\xff/./; | 
| 4345 | 228 |  |  |  |  | 691 | print $out "[$dat]\n"; | 
| 4346 |  |  |  |  |  |  | } | 
| 4347 | 169 | 100 |  |  |  | 1214 | $more and print $out "$prefix    [snip $more bytes]\n"; | 
| 4348 |  |  |  |  |  |  | } | 
| 4349 |  |  |  |  |  |  |  | 
| 4350 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4351 |  |  |  |  |  |  | # Print verbose tag information | 
| 4352 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) tag ID | 
| 4353 |  |  |  |  |  |  | #         2) tag info reference (or undef) | 
| 4354 |  |  |  |  |  |  | #         3-N) extra parms: | 
| 4355 |  |  |  |  |  |  | # Parms: Index => Index of tag in menu (starting at 0) | 
| 4356 |  |  |  |  |  |  | #        Value => Tag value | 
| 4357 |  |  |  |  |  |  | #        DataPt => reference to value data block | 
| 4358 |  |  |  |  |  |  | #        DataPos => location of data block in file | 
| 4359 |  |  |  |  |  |  | #        Base => base added to all offsets | 
| 4360 |  |  |  |  |  |  | #        Size => length of value data within block | 
| 4361 |  |  |  |  |  |  | #        Format => value format string | 
| 4362 |  |  |  |  |  |  | #        Count => number of values | 
| 4363 |  |  |  |  |  |  | #        Extra => Extra Verbose=2 information to put after tag number | 
| 4364 |  |  |  |  |  |  | #        Table => Reference to tag table | 
| 4365 |  |  |  |  |  |  | #        --> plus any of these HexDump() options: Start, Addr, Width | 
| 4366 |  |  |  |  |  |  | sub VerboseInfo($$$%) | 
| 4367 |  |  |  |  |  |  | { | 
| 4368 | 617 |  |  | 617 | 0 | 3312 | my ($self, $tagID, $tagInfo, %parms) = @_; | 
| 4369 | 617 |  |  |  |  | 1438 | my $verbose = $$self{OPTIONS}{Verbose}; | 
| 4370 | 617 |  |  |  |  | 1115 | my $out = $$self{OPTIONS}{TextOut}; | 
| 4371 | 617 |  |  |  |  | 998 | my ($tag, $line, $hexID); | 
| 4372 |  |  |  |  |  |  |  | 
| 4373 |  |  |  |  |  |  | # generate hex number if tagID is numerical | 
| 4374 | 617 | 100 |  |  |  | 1232 | if (defined $tagID) { | 
| 4375 | 578 | 100 |  |  |  | 3892 | $tagID =~ /^\d+$/ and $hexID = sprintf("0x%.4x", $tagID); | 
| 4376 |  |  |  |  |  |  | } else { | 
| 4377 | 39 |  |  |  |  | 109 | $tagID = 'Unknown'; | 
| 4378 |  |  |  |  |  |  | } | 
| 4379 |  |  |  |  |  |  | # get tag name | 
| 4380 | 617 | 50 | 33 |  |  | 2633 | if ($tagInfo and $$tagInfo{Name}) { | 
| 4381 | 617 |  |  |  |  | 1246 | $tag = $$tagInfo{Name}; | 
| 4382 |  |  |  |  |  |  | } else { | 
| 4383 | 0 |  |  |  |  | 0 | my $prefix; | 
| 4384 | 0 | 0 |  |  |  | 0 | $prefix = $parms{Table}{TAG_PREFIX} if $parms{Table}; | 
| 4385 | 0 | 0 | 0 |  |  | 0 | if ($prefix or $hexID) { | 
| 4386 | 0 | 0 |  |  |  | 0 | $prefix = 'Unknown' unless $prefix; | 
| 4387 | 0 | 0 |  |  |  | 0 | $tag = $prefix . '_' . ($hexID ? $hexID : $tagID); | 
| 4388 |  |  |  |  |  |  | } else { | 
| 4389 | 0 |  |  |  |  | 0 | $tag = $tagID; | 
| 4390 |  |  |  |  |  |  | } | 
| 4391 |  |  |  |  |  |  | } | 
| 4392 | 617 |  |  |  |  | 1009 | my $dataPt = $parms{DataPt}; | 
| 4393 | 617 |  |  |  |  | 1015 | my $size = $parms{Size}; | 
| 4394 | 617 | 50 | 66 |  |  | 1655 | $size = length $$dataPt unless defined $size or not $dataPt; | 
| 4395 | 617 |  |  |  |  | 1223 | my $indent = $$self{INDENT}; | 
| 4396 |  |  |  |  |  |  |  | 
| 4397 |  |  |  |  |  |  | # Level 1: print tag/value information | 
| 4398 | 617 |  |  |  |  | 1000 | $line = $indent; | 
| 4399 | 617 |  |  |  |  | 1006 | my $index = $parms{Index}; | 
| 4400 | 617 | 100 |  |  |  | 1249 | if (defined $index) { | 
| 4401 | 365 |  |  |  |  | 686 | $line .= $index . ') '; | 
| 4402 | 365 | 100 |  |  |  | 821 | $line .= ' ' if length($index) < 2; | 
| 4403 | 365 |  |  |  |  | 566 | $indent .= '    '; # indent everything else to align with tag name | 
| 4404 |  |  |  |  |  |  | } | 
| 4405 | 617 |  |  |  |  | 1010 | $line .= $tag; | 
| 4406 | 617 | 100 | 66 |  |  | 2131 | if ($tagInfo and $$tagInfo{SubDirectory}) { | 
| 4407 | 39 |  |  |  |  | 77 | $line .= ' (SubDirectory) -->'; | 
| 4408 |  |  |  |  |  |  | } else { | 
| 4409 | 578 |  |  |  |  | 1062 | my $maxLen = 90 - length($line); | 
| 4410 | 578 |  |  |  |  | 964 | my $val = $parms{Value}; | 
| 4411 | 578 | 50 |  |  |  | 1100 | if (defined $val) { | 
|  |  | 0 |  |  |  |  |  | 
| 4412 | 578 | 50 |  |  |  | 1244 | $val = '[' . join(',',@$val) . ']' if ref $val eq 'ARRAY'; | 
| 4413 | 578 |  |  |  |  | 1815 | $line .= ' = ' . $self->Printable($val, $maxLen); | 
| 4414 |  |  |  |  |  |  | } elsif ($dataPt) { | 
| 4415 | 0 |  | 0 |  |  | 0 | my $start = $parms{Start} || 0; | 
| 4416 | 0 |  |  |  |  | 0 | $line .= ' = ' . $self->Printable(substr($$dataPt,$start,$size), $maxLen); | 
| 4417 |  |  |  |  |  |  | } | 
| 4418 |  |  |  |  |  |  | } | 
| 4419 | 617 |  |  |  |  | 1991 | print $out "$line\n"; | 
| 4420 |  |  |  |  |  |  |  | 
| 4421 |  |  |  |  |  |  | # Level 2: print detailed information about the tag | 
| 4422 | 617 | 50 | 66 |  |  | 2906 | if ($verbose > 1 and ($parms{Extra} or $parms{Format} or | 
|  |  |  | 66 |  |  |  |  | 
| 4423 |  |  |  |  |  |  | $parms{DataPt} or defined $size or $tagID =~ /\//)) | 
| 4424 |  |  |  |  |  |  | { | 
| 4425 | 390 |  |  |  |  | 730 | $line = $indent . '- Tag '; | 
| 4426 | 390 | 100 |  |  |  | 690 | if ($hexID) { | 
| 4427 | 389 |  |  |  |  | 551 | $line .= $hexID; | 
| 4428 |  |  |  |  |  |  | } else { | 
| 4429 | 1 |  |  |  |  | 4 | $tagID =~ s/([\0-\x1f\x7f-\xff])/sprintf('\\x%.2x',ord $1)/ge; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4430 | 1 |  |  |  |  | 3 | $line .= "'${tagID}'"; | 
| 4431 |  |  |  |  |  |  | } | 
| 4432 | 390 | 50 |  |  |  | 797 | $line .= $parms{Extra} if defined $parms{Extra}; | 
| 4433 | 390 |  |  |  |  | 657 | my $format = $parms{Format}; | 
| 4434 | 390 | 50 | 66 |  |  | 972 | if ($format or defined $size) { | 
| 4435 | 390 |  |  |  |  | 595 | $line .= ' ('; | 
| 4436 | 390 | 50 |  |  |  | 752 | if (defined $size) { | 
| 4437 | 390 |  |  |  |  | 709 | $line .= "$size bytes"; | 
| 4438 | 390 | 100 |  |  |  | 770 | $line .= ', ' if $format; | 
| 4439 |  |  |  |  |  |  | } | 
| 4440 | 390 | 100 |  |  |  | 736 | if ($format) { | 
| 4441 | 352 |  |  |  |  | 531 | $line .= $format; | 
| 4442 | 352 | 50 |  |  |  | 952 | $line .= '['.$parms{Count}.']' if $parms{Count}; | 
| 4443 |  |  |  |  |  |  | } | 
| 4444 | 390 |  |  |  |  | 629 | $line .= ')'; | 
| 4445 |  |  |  |  |  |  | } | 
| 4446 | 390 | 50 | 66 |  |  | 1047 | $line .= ':' if $verbose > 2 and $parms{DataPt}; | 
| 4447 | 390 |  |  |  |  | 1047 | print $out "$line\n"; | 
| 4448 |  |  |  |  |  |  | } | 
| 4449 |  |  |  |  |  |  |  | 
| 4450 |  |  |  |  |  |  | # Level 3: do hex dump of value | 
| 4451 | 617 | 100 | 100 |  |  | 2930 | if ($verbose > 2 and $parms{DataPt} and (not $tagInfo or not $$tagInfo{ReadFromRAF})) { | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 4452 | 165 |  |  |  |  | 361 | $parms{Out} = $out; | 
| 4453 | 165 |  |  |  |  | 306 | $parms{Prefix} = $indent; | 
| 4454 |  |  |  |  |  |  | # limit dump length if Verbose < 5 | 
| 4455 | 165 | 50 |  |  |  | 526 | $parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5; | 
|  |  | 50 |  |  |  |  |  | 
| 4456 | 165 |  |  |  |  | 725 | HexDump($dataPt, $size, %parms); | 
| 4457 |  |  |  |  |  |  | } | 
| 4458 |  |  |  |  |  |  | } | 
| 4459 |  |  |  |  |  |  |  | 
| 4460 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4461 |  |  |  |  |  |  | # Dump trailer information | 
| 4462 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) dirInfo hash (RAF, DirName, DataPos, DirLen) | 
| 4463 |  |  |  |  |  |  | # Notes: Restores current file position before returning | 
| 4464 |  |  |  |  |  |  | sub DumpTrailer($$) | 
| 4465 |  |  |  |  |  |  | { | 
| 4466 | 1 |  |  | 1 | 0 | 5 | my ($self, $dirInfo) = @_; | 
| 4467 | 1 |  |  |  |  | 4 | my $raf = $$dirInfo{RAF}; | 
| 4468 | 1 |  |  |  |  | 4 | my $curPos = $raf->Tell(); | 
| 4469 | 1 |  | 50 |  |  | 6 | my $trailer = $$dirInfo{DirName} || 'Unknown'; | 
| 4470 | 1 |  |  |  |  | 5 | my $pos = $$dirInfo{DataPos}; | 
| 4471 | 1 |  |  |  |  | 3 | my $verbose = $$self{OPTIONS}{Verbose}; | 
| 4472 | 1 |  |  |  |  | 3 | my $htmlDump = $$self{HTML_DUMP}; | 
| 4473 | 1 |  |  |  |  | 2 | my ($buff, $buf2); | 
| 4474 | 1 |  |  |  |  | 2 | my $size = $$dirInfo{DirLen}; | 
| 4475 | 1 | 50 |  |  |  | 4 | $pos = $curPos unless defined $pos; | 
| 4476 |  |  |  |  |  |  |  | 
| 4477 |  |  |  |  |  |  | # get full trailer size if not specified | 
| 4478 | 1 |  |  |  |  | 2 | for (;;) { | 
| 4479 | 1 | 50 |  |  |  | 4 | unless ($size) { | 
| 4480 | 0 | 0 |  |  |  | 0 | $raf->Seek(0, 2) or last; | 
| 4481 | 0 |  |  |  |  | 0 | $size = $raf->Tell() - $pos; | 
| 4482 | 0 | 0 |  |  |  | 0 | last unless $size; | 
| 4483 |  |  |  |  |  |  | } | 
| 4484 | 1 | 50 |  |  |  | 5 | $raf->Seek($pos, 0) or last; | 
| 4485 | 1 | 50 |  |  |  | 5 | if ($htmlDump) { | 
| 4486 | 0 | 0 |  |  |  | 0 | my $num = $raf->Read($buff, $size) or return; | 
| 4487 | 0 |  |  |  |  | 0 | my $desc = "$trailer trailer"; | 
| 4488 | 0 | 0 |  |  |  | 0 | $desc = "[$desc]" if $trailer eq 'Unknown'; | 
| 4489 | 0 |  |  |  |  | 0 | $self->HDump($pos, $num, $desc, undef, 0x08); | 
| 4490 | 0 |  |  |  |  | 0 | last; | 
| 4491 |  |  |  |  |  |  | } | 
| 4492 | 1 |  |  |  |  | 3 | my $out = $$self{OPTIONS}{TextOut}; | 
| 4493 | 1 |  |  |  |  | 8 | printf $out "$trailer trailer (%d bytes at offset 0x%.4x):\n", $size, $pos; | 
| 4494 | 1 | 50 |  |  |  | 6 | last unless $verbose > 2; | 
| 4495 | 0 |  |  |  |  | 0 | my $num = $size;    # number of bytes to read | 
| 4496 |  |  |  |  |  |  | # limit size if not very verbose | 
| 4497 | 0 | 0 |  |  |  | 0 | if ($verbose < 5) { | 
| 4498 | 0 | 0 |  |  |  | 0 | my $limit = $verbose < 4 ? 96 : 512; | 
| 4499 | 0 | 0 |  |  |  | 0 | $num = $limit if $num > $limit; | 
| 4500 |  |  |  |  |  |  | } | 
| 4501 | 0 | 0 |  |  |  | 0 | $raf->Read($buff, $num) == $num or return; | 
| 4502 |  |  |  |  |  |  | # read the end of the trailer too if not done already | 
| 4503 | 0 | 0 |  |  |  | 0 | if ($size > 2 * $num) { | 
|  |  | 0 |  |  |  |  |  | 
| 4504 | 0 |  |  |  |  | 0 | $raf->Seek($pos + $size - $num, 0); | 
| 4505 | 0 |  |  |  |  | 0 | $raf->Read($buf2, $num); | 
| 4506 |  |  |  |  |  |  | } elsif ($size > $num) { | 
| 4507 | 0 |  |  |  |  | 0 | $raf->Seek($pos + $num, 0); | 
| 4508 | 0 |  |  |  |  | 0 | $raf->Read($buf2, $size - $num); | 
| 4509 | 0 |  |  |  |  | 0 | $buff .= $buf2; | 
| 4510 | 0 |  |  |  |  | 0 | undef $buf2; | 
| 4511 |  |  |  |  |  |  | } | 
| 4512 | 0 |  |  |  |  | 0 | HexDump(\$buff, undef, Addr => $pos, Out => $out); | 
| 4513 | 0 | 0 |  |  |  | 0 | if (defined $buf2) { | 
| 4514 | 0 |  |  |  |  | 0 | print $out "    [snip ", $size - $num * 2, " bytes]\n"; | 
| 4515 | 0 |  |  |  |  | 0 | HexDump(\$buf2, undef, Addr => $pos + $size - $num, Out => $out); | 
| 4516 |  |  |  |  |  |  | } | 
| 4517 | 0 |  |  |  |  | 0 | last; | 
| 4518 |  |  |  |  |  |  | } | 
| 4519 | 1 |  |  |  |  | 5 | $raf->Seek($curPos, 0); | 
| 4520 |  |  |  |  |  |  | } | 
| 4521 |  |  |  |  |  |  |  | 
| 4522 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4523 |  |  |  |  |  |  | # Dump unknown trailer information | 
| 4524 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) dirInfo ref (with RAF, DataPos and DirLen defined) | 
| 4525 |  |  |  |  |  |  | # Notes: changes dirInfo elements | 
| 4526 |  |  |  |  |  |  | sub DumpUnknownTrailer($$) | 
| 4527 |  |  |  |  |  |  | { | 
| 4528 | 0 |  |  | 0 | 0 | 0 | my ($self, $dirInfo) = @_; | 
| 4529 | 0 |  |  |  |  | 0 | my $pos = $$dirInfo{DataPos}; | 
| 4530 | 0 |  |  |  |  | 0 | my $endPos = $pos + $$dirInfo{DirLen}; | 
| 4531 |  |  |  |  |  |  | # account for preview/MPF image trailer | 
| 4532 | 0 |  | 0 |  |  | 0 | my $prePos = $$self{VALUE}{PreviewImageStart} || $$self{PreviewImageStart}; | 
| 4533 | 0 |  | 0 |  |  | 0 | my $preLen = $$self{VALUE}{PreviewImageLength} || $$self{PreviewImageLength}; | 
| 4534 | 0 |  |  |  |  | 0 | my $tag = 'PreviewImage'; | 
| 4535 | 0 |  |  |  |  | 0 | my $mpImageNum = 0; | 
| 4536 | 0 |  |  |  |  | 0 | my (%image, $lastOne); | 
| 4537 | 0 |  |  |  |  | 0 | for (;;) { | 
| 4538 |  |  |  |  |  |  | # add to Preview block list if valid and in the trailer | 
| 4539 | 0 | 0 | 0 |  |  | 0 | $image{$prePos} = [$tag, $preLen] if $prePos and $preLen and $prePos+$preLen > $pos; | 
|  |  |  | 0 |  |  |  |  | 
| 4540 | 0 | 0 |  |  |  | 0 | last if $lastOne;   # checked all images | 
| 4541 |  |  |  |  |  |  | # look for MPF images (in the proper order) | 
| 4542 | 0 |  |  |  |  | 0 | ++$mpImageNum; | 
| 4543 | 0 |  |  |  |  | 0 | $prePos = $$self{VALUE}{"MPImageStart ($mpImageNum)"}; | 
| 4544 | 0 | 0 |  |  |  | 0 | if (defined $prePos) { | 
| 4545 | 0 |  |  |  |  | 0 | $preLen = $$self{VALUE}{"MPImageLength ($mpImageNum)"}; | 
| 4546 |  |  |  |  |  |  | } else { | 
| 4547 | 0 |  |  |  |  | 0 | $prePos = $$self{VALUE}{'MPImageStart'}; | 
| 4548 | 0 |  |  |  |  | 0 | $preLen = $$self{VALUE}{'MPImageLength'}; | 
| 4549 | 0 |  |  |  |  | 0 | $lastOne = 1; | 
| 4550 |  |  |  |  |  |  | } | 
| 4551 | 0 |  |  |  |  | 0 | $tag = "MPImage$mpImageNum"; | 
| 4552 |  |  |  |  |  |  | } | 
| 4553 |  |  |  |  |  |  | # dump trailer sections in order | 
| 4554 | 0 |  |  |  |  | 0 | $image{$endPos} = [ '', 0 ];    # add terminator "image" | 
| 4555 | 0 |  |  |  |  | 0 | foreach $prePos (sort { $a <=> $b } keys %image) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 4556 | 0 | 0 |  |  |  | 0 | if ($pos < $prePos) { | 
| 4557 |  |  |  |  |  |  | # dump unknown trailer data | 
| 4558 | 0 |  |  |  |  | 0 | $$dirInfo{DirName} = 'Unknown'; | 
| 4559 | 0 |  |  |  |  | 0 | $$dirInfo{DataPos} = $pos; | 
| 4560 | 0 |  |  |  |  | 0 | $$dirInfo{DirLen} = $prePos - $pos; | 
| 4561 | 0 |  |  |  |  | 0 | $self->DumpTrailer($dirInfo); | 
| 4562 |  |  |  |  |  |  | } | 
| 4563 | 0 |  |  |  |  | 0 | ($tag, $preLen) = @{$image{$prePos}}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4564 | 0 | 0 |  |  |  | 0 | last unless $preLen; | 
| 4565 |  |  |  |  |  |  | # dump image if verbose (it is htmlDump'd by ExtractImage) | 
| 4566 | 0 | 0 |  |  |  | 0 | if ($$self{OPTIONS}{Verbose}) { | 
| 4567 | 0 |  |  |  |  | 0 | $$dirInfo{DirName} = $tag; | 
| 4568 | 0 |  |  |  |  | 0 | $$dirInfo{DataPos} = $prePos; | 
| 4569 | 0 |  |  |  |  | 0 | $$dirInfo{DirLen}  = $preLen; | 
| 4570 | 0 |  |  |  |  | 0 | $self->DumpTrailer($dirInfo); | 
| 4571 |  |  |  |  |  |  | } | 
| 4572 | 0 |  |  |  |  | 0 | $pos = $prePos + $preLen; | 
| 4573 |  |  |  |  |  |  | } | 
| 4574 |  |  |  |  |  |  | } | 
| 4575 |  |  |  |  |  |  |  | 
| 4576 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4577 |  |  |  |  |  |  | # Find last element in linked list | 
| 4578 |  |  |  |  |  |  | # Inputs: 0) element in list | 
| 4579 |  |  |  |  |  |  | # Returns: Last element in list | 
| 4580 |  |  |  |  |  |  | sub LastInList($) | 
| 4581 |  |  |  |  |  |  | { | 
| 4582 | 33 |  |  | 33 | 0 | 72 | my $element = shift; | 
| 4583 | 33 |  |  |  |  | 156 | while ($$element{Next}) { | 
| 4584 | 0 |  |  |  |  | 0 | $element = $$element{Next}; | 
| 4585 |  |  |  |  |  |  | } | 
| 4586 | 33 |  |  |  |  | 75 | return $element; | 
| 4587 |  |  |  |  |  |  | } | 
| 4588 |  |  |  |  |  |  |  | 
| 4589 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4590 |  |  |  |  |  |  | # Print verbose value while writing | 
| 4591 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) heading "eg. '+ IPTC:Keywords', | 
| 4592 |  |  |  |  |  |  | #         2) value, 3) [optional] extra text after value | 
| 4593 |  |  |  |  |  |  | sub VerboseValue($$$;$) | 
| 4594 |  |  |  |  |  |  | { | 
| 4595 | 1058 | 100 |  | 1058 | 0 | 3236 | return unless $_[0]{OPTIONS}{Verbose} > 1; | 
| 4596 | 14 |  |  |  |  | 35 | my ($self, $str, $val, $xtra) = @_; | 
| 4597 | 14 |  |  |  |  | 28 | my $out = $$self{OPTIONS}{TextOut}; | 
| 4598 | 14 | 100 |  |  |  | 35 | $xtra or $xtra = ''; | 
| 4599 | 14 |  |  |  |  | 30 | my $maxLen = 81 - length($str) - length($xtra); | 
| 4600 | 14 |  |  |  |  | 42 | $val = $self->Printable($val, $maxLen); | 
| 4601 | 14 |  |  |  |  | 67 | print $out "    $str = '${val}'$xtra\n"; | 
| 4602 |  |  |  |  |  |  | } | 
| 4603 |  |  |  |  |  |  |  | 
| 4604 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4605 |  |  |  |  |  |  | # Pack Unicode numbers into UTF8 string | 
| 4606 |  |  |  |  |  |  | # Inputs: 0-N) list of Unicode numbers | 
| 4607 |  |  |  |  |  |  | # Returns: Packed UTF-8 string | 
| 4608 |  |  |  |  |  |  | sub PackUTF8(@) | 
| 4609 |  |  |  |  |  |  | { | 
| 4610 | 0 |  |  | 0 | 0 | 0 | my @out; | 
| 4611 | 0 |  |  |  |  | 0 | while (@_) { | 
| 4612 | 0 |  |  |  |  | 0 | my $ch = pop; | 
| 4613 | 0 | 0 |  |  |  | 0 | unshift(@out, $ch), next if $ch < 0x80; | 
| 4614 | 0 |  |  |  |  | 0 | unshift(@out, 0x80 | ($ch & 0x3f)); | 
| 4615 | 0 |  |  |  |  | 0 | $ch >>= 6; | 
| 4616 | 0 | 0 |  |  |  | 0 | unshift(@out, 0xc0 | $ch), next if $ch < 0x20; | 
| 4617 | 0 |  |  |  |  | 0 | unshift(@out, 0x80 | ($ch & 0x3f)); | 
| 4618 | 0 |  |  |  |  | 0 | $ch >>= 6; | 
| 4619 | 0 | 0 |  |  |  | 0 | unshift(@out, 0xe0 | $ch), next if $ch < 0x10; | 
| 4620 | 0 |  |  |  |  | 0 | unshift(@out, 0x80 | ($ch & 0x3f)); | 
| 4621 | 0 |  |  |  |  | 0 | $ch >>= 6; | 
| 4622 | 0 |  |  |  |  | 0 | unshift(@out, 0xf0 | ($ch & 0x07)); | 
| 4623 |  |  |  |  |  |  | } | 
| 4624 | 0 |  |  |  |  | 0 | return pack('C*', @out); | 
| 4625 |  |  |  |  |  |  | } | 
| 4626 |  |  |  |  |  |  |  | 
| 4627 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4628 |  |  |  |  |  |  | # Unpack numbers from UTF8 string | 
| 4629 |  |  |  |  |  |  | # Inputs: 0) UTF-8 string | 
| 4630 |  |  |  |  |  |  | # Returns: List of Unicode numbers (sets $evalWarning on error) | 
| 4631 |  |  |  |  |  |  | sub UnpackUTF8($) | 
| 4632 |  |  |  |  |  |  | { | 
| 4633 | 0 |  |  | 0 | 0 | 0 | my (@out, $pos); | 
| 4634 | 0 |  |  |  |  | 0 | pos($_[0]) = $pos = 0;  # start at beginning of string | 
| 4635 | 0 |  |  |  |  | 0 | for (;;) { | 
| 4636 | 0 |  |  |  |  | 0 | my ($ch, $newPos, $val, $byte); | 
| 4637 | 0 | 0 |  |  |  | 0 | if ($_[0] =~ /([\x80-\xff])/g) { | 
| 4638 | 0 |  |  |  |  | 0 | $ch = ord($1); | 
| 4639 | 0 |  |  |  |  | 0 | $newPos = pos($_[0]) - 1; | 
| 4640 |  |  |  |  |  |  | } else { | 
| 4641 | 0 |  |  |  |  | 0 | $newPos = length $_[0]; | 
| 4642 |  |  |  |  |  |  | } | 
| 4643 |  |  |  |  |  |  | # unpack 7-bit characters | 
| 4644 | 0 |  |  |  |  | 0 | my $len = $newPos - $pos; | 
| 4645 | 0 | 0 |  |  |  | 0 | push @out, unpack("x${pos}C$len",$_[0]) if $len; | 
| 4646 | 0 | 0 |  |  |  | 0 | last unless defined $ch; | 
| 4647 | 0 |  |  |  |  | 0 | $pos = $newPos + 1; | 
| 4648 |  |  |  |  |  |  | # minimum lead byte for 2-byte sequence is 0xc2 (overlong sequences | 
| 4649 |  |  |  |  |  |  | # not allowed), 0xf8-0xfd are restricted by RFC 3629 (no 5 or 6 byte | 
| 4650 |  |  |  |  |  |  | # sequences), and 0xfe and 0xff are not valid in UTF-8 strings | 
| 4651 | 0 | 0 | 0 |  |  | 0 | if ($ch < 0xc2 or $ch >= 0xf8) { | 
| 4652 | 0 |  |  |  |  | 0 | push @out, ord('?');    # invalid UTF-8 | 
| 4653 | 0 |  |  |  |  | 0 | $evalWarning = 'Bad UTF-8'; | 
| 4654 | 0 |  |  |  |  | 0 | next; | 
| 4655 |  |  |  |  |  |  | } | 
| 4656 |  |  |  |  |  |  | # decode 2, 3 and 4-byte sequences | 
| 4657 | 0 |  |  |  |  | 0 | my $n = 1; | 
| 4658 | 0 | 0 |  |  |  | 0 | if ($ch < 0xe0) { | 
|  |  | 0 |  |  |  |  |  | 
| 4659 | 0 |  |  |  |  | 0 | $val = $ch & 0x1f;      # 2-byte sequence | 
| 4660 |  |  |  |  |  |  | } elsif ($ch < 0xf0) { | 
| 4661 | 0 |  |  |  |  | 0 | $val = $ch & 0x0f;      # 3-byte sequence | 
| 4662 | 0 |  |  |  |  | 0 | ++$n; | 
| 4663 |  |  |  |  |  |  | } else { | 
| 4664 | 0 |  |  |  |  | 0 | $val = $ch & 0x07;      # 4-byte sequence | 
| 4665 | 0 |  |  |  |  | 0 | $n += 2; | 
| 4666 |  |  |  |  |  |  | } | 
| 4667 | 0 | 0 |  |  |  | 0 | unless ($_[0] =~ /\G([\x80-\xbf]{$n})/g) { | 
| 4668 | 0 |  |  |  |  | 0 | pos($_[0]) = $pos;      # restore position | 
| 4669 | 0 |  |  |  |  | 0 | push @out, ord('?');    # invalid UTF-8 | 
| 4670 | 0 |  |  |  |  | 0 | $evalWarning = 'Bad UTF-8'; | 
| 4671 | 0 |  |  |  |  | 0 | next; | 
| 4672 |  |  |  |  |  |  | } | 
| 4673 | 0 |  |  |  |  | 0 | foreach $byte (unpack 'C*', $1) { | 
| 4674 | 0 |  |  |  |  | 0 | $val = ($val << 6) | ($byte & 0x3f); | 
| 4675 |  |  |  |  |  |  | } | 
| 4676 | 0 |  |  |  |  | 0 | push @out, $val;    # save Unicode character value | 
| 4677 | 0 |  |  |  |  | 0 | $pos += $n;         # position at end of UTF-8 character | 
| 4678 |  |  |  |  |  |  | } | 
| 4679 | 0 |  |  |  |  | 0 | return @out; | 
| 4680 |  |  |  |  |  |  | } | 
| 4681 |  |  |  |  |  |  |  | 
| 4682 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4683 |  |  |  |  |  |  | # Generate a new, random GUID | 
| 4684 |  |  |  |  |  |  | # Inputs: | 
| 4685 |  |  |  |  |  |  | # Returns: GUID string | 
| 4686 |  |  |  |  |  |  | my $guidCount; | 
| 4687 |  |  |  |  |  |  | sub NewGUID() | 
| 4688 |  |  |  |  |  |  | { | 
| 4689 | 58 |  |  | 58 | 0 | 1435 | my @tm = localtime time; | 
| 4690 | 58 | 100 | 66 |  |  | 635 | $guidCount = 0 unless defined $guidCount and ++$guidCount < 0x100; | 
| 4691 | 58 |  |  |  |  | 1749 | return sprintf('%.4d%.2d%.2d%.2d%.2d%.2d%.2X%.4X%.4X%.4X%.4X', | 
| 4692 |  |  |  |  |  |  | $tm[5]+1900, $tm[4]+1, $tm[3], $tm[2], $tm[1], $tm[0], $guidCount, | 
| 4693 |  |  |  |  |  |  | $$ & 0xffff, rand(0x10000), rand(0x10000), rand(0x10000)); | 
| 4694 |  |  |  |  |  |  | } | 
| 4695 |  |  |  |  |  |  |  | 
| 4696 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4697 |  |  |  |  |  |  | # Make TIFF header for raw data | 
| 4698 |  |  |  |  |  |  | # Inputs: 0) width, 1) height, 2) num colour components, 3) bits, 4) resolution | 
| 4699 |  |  |  |  |  |  | #         5) color-map data for palette-color image (8 or 16 bit) | 
| 4700 |  |  |  |  |  |  | # Returns: TIFF header | 
| 4701 |  |  |  |  |  |  | # Notes: Multi-byte data must be little-endian | 
| 4702 |  |  |  |  |  |  | sub MakeTiffHeader($$$$;$$) | 
| 4703 |  |  |  |  |  |  | { | 
| 4704 | 0 |  |  | 0 | 0 | 0 | my ($w, $h, $cols, $bits, $res, $cmap) = @_; | 
| 4705 | 0 | 0 |  |  |  | 0 | $res or $res = 72; | 
| 4706 | 0 |  |  |  |  | 0 | my $saveOrder = GetByteOrder(); | 
| 4707 | 0 |  |  |  |  | 0 | SetByteOrder('II'); | 
| 4708 | 0 | 0 |  |  |  | 0 | if (not $cmap) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 4709 | 0 |  |  |  |  | 0 | $cmap = ''; | 
| 4710 |  |  |  |  |  |  | } elsif (length $cmap == 3 * 2**$bits) { | 
| 4711 |  |  |  |  |  |  | # convert to short | 
| 4712 | 0 |  |  |  |  | 0 | $cmap = pack 'v*', map { $_ | ($_<<8) } unpack 'C*', $cmap; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4713 |  |  |  |  |  |  | } elsif (length $cmap != 6 * 2**$bits) { | 
| 4714 | 0 |  |  |  |  | 0 | $cmap = ''; | 
| 4715 |  |  |  |  |  |  | } | 
| 4716 | 0 | 0 |  |  |  | 0 | my $cmo = $cmap ? 12 : 0;   # offset due to ColorMap IFD entry | 
| 4717 | 0 | 0 |  |  |  | 0 | my $hdr = | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 4718 |  |  |  |  |  |  | "\x49\x49\x2a\0\x08\0\0\0\x0e\0" .                  # 0x00 14 menu entries: | 
| 4719 |  |  |  |  |  |  | "\xfe\x00\x04\0\x01\0\0\0\x00\0\0\0" .              # 0x0a SubfileType = 0 | 
| 4720 |  |  |  |  |  |  | "\x00\x01\x04\0\x01\0\0\0" . Set32u($w) .           # 0x16 ImageWidth | 
| 4721 |  |  |  |  |  |  | "\x01\x01\x04\0\x01\0\0\0" . Set32u($h) .           # 0x22 ImageHeight | 
| 4722 |  |  |  |  |  |  | "\x02\x01\x03\0" . Set32u($cols) .                  # 0x2e BitsPerSample | 
| 4723 |  |  |  |  |  |  | Set32u($cols == 1 ? $bits : 0xb6 + $cmo) . | 
| 4724 |  |  |  |  |  |  | "\x03\x01\x03\0\x01\0\0\0\x01\0\0\0" .              # 0x3a Compression = 1 | 
| 4725 |  |  |  |  |  |  | "\x06\x01\x03\0\x01\0\0\0" .                        # 0x46 PhotometricInterpretation | 
| 4726 |  |  |  |  |  |  | Set32u($cmap ? 3 : $cols == 1 ? 1 : 2) . | 
| 4727 |  |  |  |  |  |  | "\x11\x01\x04\0\x01\0\0\0" .                        # 0x52 StripOffsets | 
| 4728 |  |  |  |  |  |  | Set32u(0xcc + $cmo + length($cmap)) . | 
| 4729 |  |  |  |  |  |  | "\x15\x01\x03\0\x01\0\0\0" . Set32u($cols) .        # 0x5e SamplesPerPixel | 
| 4730 |  |  |  |  |  |  | "\x16\x01\x04\0\x01\0\0\0" . Set32u($h) .           # 0x6a RowsPerStrip | 
| 4731 |  |  |  |  |  |  | "\x17\x01\x04\0\x01\0\0\0" .                        # 0x76 StripByteCounts | 
| 4732 |  |  |  |  |  |  | Set32u($w * $h * $cols * int(($bits+7)/8)) . | 
| 4733 |  |  |  |  |  |  | "\x1a\x01\x05\0\x01\0\0\0" . Set32u(0xbc + $cmo) .  # 0x82 XResolution | 
| 4734 |  |  |  |  |  |  | "\x1b\x01\x05\0\x01\0\0\0" . Set32u(0xc4 + $cmo) .  # 0x8e YResolution | 
| 4735 |  |  |  |  |  |  | "\x1c\x01\x03\0\x01\0\0\0\x01\0\0\0" .              # 0x9a PlanarConfiguration = 1 | 
| 4736 |  |  |  |  |  |  | "\x28\x01\x03\0\x01\0\0\0\x02\0\0\0" .              # 0xa6 ResolutionUnit = 2 | 
| 4737 |  |  |  |  |  |  | ($cmap ?                                            # 0xb2 ColorMap [optional] | 
| 4738 |  |  |  |  |  |  | "\x40\x01\x03\0" . Set32u(3 * 2**$bits) . "\xd8\0\0\0" : '') . | 
| 4739 |  |  |  |  |  |  | "\0\0\0\0" .                                        # 0xb2+$cmo (no IFD1) | 
| 4740 |  |  |  |  |  |  | (Set16u($bits) x 3) .                               # 0xb6+$cmo BitsPerSample value | 
| 4741 |  |  |  |  |  |  | Set32u($res) . "\x01\0\0\0" .                       # 0xbc+$cmo XResolution = 72 | 
| 4742 |  |  |  |  |  |  | Set32u($res) . "\x01\0\0\0" .                       # 0xc4+$cmo YResolution = 72 | 
| 4743 |  |  |  |  |  |  | $cmap;                                              # 0xcc or 0xd8 (cmap and data go here) | 
| 4744 | 0 |  |  |  |  | 0 | SetByteOrder($saveOrder); | 
| 4745 | 0 |  |  |  |  | 0 | return $hdr; | 
| 4746 |  |  |  |  |  |  | } | 
| 4747 |  |  |  |  |  |  |  | 
| 4748 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4749 |  |  |  |  |  |  | # Return current time in EXIF format | 
| 4750 |  |  |  |  |  |  | # Inputs: 0) [optional] ExifTool ref, 1) flag to include timezone (0 to disable, | 
| 4751 |  |  |  |  |  |  | #            undef or 1 to include) | 
| 4752 |  |  |  |  |  |  | # Returns: time string | 
| 4753 |  |  |  |  |  |  | # - a consistent value is returned for each processed file | 
| 4754 |  |  |  |  |  |  | sub TimeNow(;$$) | 
| 4755 |  |  |  |  |  |  | { | 
| 4756 | 58 |  |  | 58 | 0 | 251 | my ($self, $tzFlag) = @_; | 
| 4757 | 58 |  |  |  |  | 353 | my $timeNow; | 
| 4758 | 58 | 50 |  |  |  | 284 | ref $self or $tzFlag = $self, $self = { }; | 
| 4759 | 58 | 50 |  |  |  | 248 | if ($$self{Now}) { | 
| 4760 | 0 |  |  |  |  | 0 | $timeNow = $$self{Now}[0]; | 
| 4761 |  |  |  |  |  |  | } else { | 
| 4762 | 58 |  |  |  |  | 316 | my $time = time(); | 
| 4763 | 58 |  |  |  |  | 2456 | my @tm = localtime $time; | 
| 4764 | 58 |  |  |  |  | 461 | my $tz = TimeZoneString(\@tm, $time); | 
| 4765 | 58 |  |  |  |  | 482 | $timeNow = sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d", | 
| 4766 |  |  |  |  |  |  | $tm[5]+1900, $tm[4]+1, $tm[3], | 
| 4767 |  |  |  |  |  |  | $tm[2], $tm[1], $tm[0]); | 
| 4768 | 58 |  |  |  |  | 329 | $$self{Now} = [ $timeNow, $tz ]; | 
| 4769 |  |  |  |  |  |  | } | 
| 4770 | 58 | 50 | 33 |  |  | 604 | $timeNow .= $$self{Now}[1] if $tzFlag or not defined $tzFlag; | 
| 4771 | 58 |  |  |  |  | 369 | return $timeNow; | 
| 4772 |  |  |  |  |  |  | } | 
| 4773 |  |  |  |  |  |  |  | 
| 4774 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4775 |  |  |  |  |  |  | # Inverse date/time print conversion (reformat to YYYY:mm:dd HH:MM:SS[.ss][+-HH:MM|Z]) | 
| 4776 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) Date/Time string, 2) timezone flag: | 
| 4777 |  |  |  |  |  |  | #               0     - remove timezone and sub-seconds if they exist | 
| 4778 |  |  |  |  |  |  | #               1     - add timezone if it doesn't exist | 
| 4779 |  |  |  |  |  |  | #               undef - leave timezone alone | 
| 4780 |  |  |  |  |  |  | #         3) flag to allow date-only (YYYY, YYYY:mm or YYYY:mm:dd) or time without seconds | 
| 4781 |  |  |  |  |  |  | # Returns: formatted date/time string (or undef and issues warning on error) | 
| 4782 |  |  |  |  |  |  | # Notes: currently accepts different separators, but doesn't use DateFormat yet | 
| 4783 |  |  |  |  |  |  | my $strptimeLib; # strptime library name if available | 
| 4784 |  |  |  |  |  |  | sub InverseDateTime($$;$$) | 
| 4785 |  |  |  |  |  |  | { | 
| 4786 | 437 |  |  | 437 | 0 | 1291 | my ($self, $val, $tzFlag, $dateOnly) = @_; | 
| 4787 | 437 |  |  |  |  | 852 | my ($rtnVal, $tz); | 
| 4788 | 437 |  |  |  |  | 1181 | my $fmt = $$self{OPTIONS}{DateFormat}; | 
| 4789 |  |  |  |  |  |  | # strip off timezone first if it exists | 
| 4790 | 437 | 100 | 66 |  |  | 3877 | if (not $fmt and $val =~ s/([-+])(\d{1,2}):?(\d{2})\s*(DST)?$//i) { | 
|  |  | 50 | 33 |  |  |  |  | 
| 4791 | 6 |  |  |  |  | 52 | $tz = sprintf("$1%.2d:$3", $2); | 
| 4792 |  |  |  |  |  |  | } elsif (not $fmt and $val =~ s/Z$//i) { | 
| 4793 | 0 |  |  |  |  | 0 | $tz = 'Z'; | 
| 4794 |  |  |  |  |  |  | } else { | 
| 4795 | 431 |  |  |  |  | 889 | $tz = ''; | 
| 4796 |  |  |  |  |  |  | # allow special value of 'now' | 
| 4797 | 431 | 50 |  |  |  | 1300 | return $self->TimeNow($tzFlag) if lc($val) eq 'now'; | 
| 4798 |  |  |  |  |  |  | } | 
| 4799 |  |  |  |  |  |  | # only convert date if a format was specified and the date is recognizable | 
| 4800 | 437 | 50 |  |  |  | 1061 | if ($fmt) { | 
| 4801 | 0 | 0 |  |  |  | 0 | unless (defined $strptimeLib) { | 
| 4802 | 0 | 0 |  |  |  | 0 | if (eval { require POSIX::strptime }) { | 
|  | 0 | 0 |  |  |  | 0 |  | 
| 4803 | 0 |  |  |  |  | 0 | $strptimeLib = 'POSIX::strptime'; | 
| 4804 | 0 |  |  |  |  | 0 | } elsif (eval { require Time::Piece }) { | 
| 4805 | 0 |  |  |  |  | 0 | $strptimeLib = 'Time::Piece'; | 
| 4806 |  |  |  |  |  |  | # (call use_locale() to convert localized date/time, | 
| 4807 |  |  |  |  |  |  | #  only available in Time::Piece 1.32 and later) | 
| 4808 | 0 |  |  |  |  | 0 | eval { Time::Piece->use_locale() }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4809 |  |  |  |  |  |  | } else { | 
| 4810 | 0 |  |  |  |  | 0 | $strptimeLib = ''; | 
| 4811 |  |  |  |  |  |  | } | 
| 4812 |  |  |  |  |  |  | } | 
| 4813 |  |  |  |  |  |  | # handle factional seconds (%f), but only at the end of the string | 
| 4814 | 0 | 0 | 0 |  |  | 0 | my $fs = ($fmt =~ s/%f$// and $val =~ s/(\.\d+)\s*$//) ? $1 : ''; | 
| 4815 | 0 |  |  |  |  | 0 | my ($lib, $wrn, @a); | 
| 4816 | 0 |  |  |  |  | 0 | TryLib: for ($lib=$strptimeLib; ; $lib='') { | 
| 4817 | 0 | 0 |  |  |  | 0 | if (not $lib) { | 
|  |  | 0 |  |  |  |  |  | 
| 4818 | 0 | 0 |  |  |  | 0 | last unless $$self{OPTIONS}{StrictDate}; | 
| 4819 | 0 |  | 0 |  |  | 0 | warn $wrn || "Install POSIX::strptime or Time::Piece for inverse date/time conversions\n"; | 
| 4820 | 0 |  |  |  |  | 0 | return undef; | 
| 4821 |  |  |  |  |  |  | } elsif ($lib eq 'POSIX::strptime') { | 
| 4822 | 0 |  |  |  |  | 0 | @a = eval { POSIX::strptime($val, $fmt) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4823 |  |  |  |  |  |  | } else { | 
| 4824 |  |  |  |  |  |  | # protect against a negative epoch time, it can cause a hard crash in Windows | 
| 4825 | 0 | 0 | 0 |  |  | 0 | if ($^O eq 'MSWin32' and $fmt =~ /%s/ and $val =~ /-\d/) { | 
|  |  |  | 0 |  |  |  |  | 
| 4826 | 0 |  |  |  |  | 0 | warn "Can't convert negative epoch time\n"; | 
| 4827 | 0 |  |  |  |  | 0 | return undef; | 
| 4828 |  |  |  |  |  |  | } | 
| 4829 | 0 |  |  |  |  | 0 | @a = eval { | 
| 4830 | 0 |  |  |  |  | 0 | my $t = Time::Piece->strptime($val, $fmt); | 
| 4831 | 0 |  |  |  |  | 0 | return ($t->sec, $t->min, $t->hour, $t->mday, $t->_mon, $t->_year); | 
| 4832 |  |  |  |  |  |  | }; | 
| 4833 |  |  |  |  |  |  | } | 
| 4834 | 0 | 0 | 0 |  |  | 0 | if (defined $a[5] and length $a[5]) { | 
| 4835 | 0 |  |  |  |  | 0 | $a[5] += 1900; # add 1900 to year | 
| 4836 |  |  |  |  |  |  | } else { | 
| 4837 | 0 |  |  |  |  | 0 | $wrn = "Invalid date/time (no year) using $lib\n"; | 
| 4838 | 0 |  |  |  |  | 0 | next; | 
| 4839 |  |  |  |  |  |  | } | 
| 4840 | 0 | 0 | 0 |  |  | 0 | ++$a[4] if defined $a[4] and length $a[4];  # add 1 to month | 
| 4841 | 0 |  |  |  |  | 0 | my $i; | 
| 4842 | 0 |  |  |  |  | 0 | foreach $i (0..4) { | 
| 4843 | 0 | 0 | 0 |  |  | 0 | if (not defined $a[$i] or not length $a[$i]) { | 
|  |  | 0 |  |  |  |  |  | 
| 4844 | 0 | 0 | 0 |  |  | 0 | if ($i < 2 or $dateOnly) { # (allow missing minutes/seconds) | 
| 4845 | 0 |  |  |  |  | 0 | $a[$i] = '  '; | 
| 4846 |  |  |  |  |  |  | } else { | 
| 4847 | 0 |  |  |  |  | 0 | $wrn = "Incomplete date/time specification using $lib\n"; | 
| 4848 | 0 |  |  |  |  | 0 | next TryLib; | 
| 4849 |  |  |  |  |  |  | } | 
| 4850 |  |  |  |  |  |  | } elsif (length($a[$i]) < 2) { | 
| 4851 | 0 |  |  |  |  | 0 | $a[$i] = "0$a[$i]"; # pad to 2 digits if necessary | 
| 4852 |  |  |  |  |  |  | } | 
| 4853 |  |  |  |  |  |  | } | 
| 4854 | 0 |  |  |  |  | 0 | $val = join(':', @a[5,4,3]) . ' ' . join(':', @a[2,1,0]) . $fs; | 
| 4855 | 0 |  |  |  |  | 0 | last; | 
| 4856 |  |  |  |  |  |  | } | 
| 4857 |  |  |  |  |  |  | } | 
| 4858 | 437 | 100 |  |  |  | 2020 | if ($val =~ /(\d{4})/g) {           # get YYYY | 
| 4859 | 430 |  |  |  |  | 1067 | my $yr = $1; | 
| 4860 | 430 |  |  |  |  | 2578 | my @a = ($val =~ /\d{1,2}/g);   # get mm, dd, HH, and maybe MM, SS | 
| 4861 | 430 |  | 66 |  |  | 2570 | length($_) < 2 and $_ = "0$_" foreach @a;   # pad to 2 digits if necessary | 
| 4862 | 430 | 100 |  |  |  | 1142 | if (@a >= 3) { | 
|  |  | 50 |  |  |  |  |  | 
| 4863 | 404 |  |  |  |  | 748 | my $ss = $a[4];             # get SS | 
| 4864 | 404 |  |  |  |  | 1023 | push @a, '00' while @a < 5; # add MM, SS if not given | 
| 4865 |  |  |  |  |  |  | # get sub-seconds if they exist (must be after SS, and have leading ".") | 
| 4866 | 404 | 100 | 100 |  |  | 1395 | my $fs = (@a > 5 and $val =~ /(\.\d+)\s*$/) ? $1 : ''; | 
| 4867 |  |  |  |  |  |  | # add/remove timezone if necessary | 
| 4868 | 404 | 100 |  |  |  | 1355 | if ($tzFlag) { | 
|  |  | 100 |  |  |  |  |  | 
| 4869 | 34 | 50 |  |  |  | 124 | if (not $tz) { | 
| 4870 | 34 | 50 |  |  |  | 75 | if (eval { require Time::Local }) { | 
|  | 34 |  |  |  |  | 985 |  | 
| 4871 |  |  |  |  |  |  | # determine timezone offset for this time | 
| 4872 | 34 |  |  |  |  | 2715 | my @args = ($a[4],$a[3],$a[2],$a[1],$a[0]-1,$yr); | 
| 4873 | 34 |  |  |  |  | 168 | my $diff = Time::Local::timegm(@args) - TimeLocal(@args); | 
| 4874 | 34 |  |  |  |  | 154 | $tz = TimeZoneString($diff / 60); | 
| 4875 |  |  |  |  |  |  | } else { | 
| 4876 | 0 |  |  |  |  | 0 | $tz = 'Z';  # don't know time zone | 
| 4877 |  |  |  |  |  |  | } | 
| 4878 |  |  |  |  |  |  | } | 
| 4879 |  |  |  |  |  |  | } elsif (defined $tzFlag) { | 
| 4880 | 92 |  |  |  |  | 268 | $tz = $fs = ''; # remove timezone and sub-seconds | 
| 4881 |  |  |  |  |  |  | } | 
| 4882 | 404 | 100 | 66 |  |  | 1820 | if (defined $ss and $ss < 60) { | 
|  |  | 50 |  |  |  |  |  | 
| 4883 | 403 |  |  |  |  | 1109 | $ss = ":$ss"; | 
| 4884 |  |  |  |  |  |  | } elsif ($dateOnly) { | 
| 4885 | 1 |  |  |  |  | 4 | $ss = ''; | 
| 4886 |  |  |  |  |  |  | } else { | 
| 4887 | 0 |  |  |  |  | 0 | $ss = ':00'; | 
| 4888 |  |  |  |  |  |  | } | 
| 4889 |  |  |  |  |  |  | # construct properly formatted date/time string | 
| 4890 | 404 | 50 | 33 |  |  | 1936 | if ($a[0] < 1 or $a[0] > 12) { | 
| 4891 | 0 |  |  |  |  | 0 | warn "Month '$a[0]' out of range 1..12\n"; | 
| 4892 | 0 |  |  |  |  | 0 | return undef; | 
| 4893 |  |  |  |  |  |  | } | 
| 4894 | 404 | 50 | 33 |  |  | 1665 | if ($a[1] < 1 or $a[1] > 31) { | 
| 4895 | 0 |  |  |  |  | 0 | warn "Day '$a[1]' out of range 1..31\n"; | 
| 4896 | 0 |  |  |  |  | 0 | return undef; | 
| 4897 |  |  |  |  |  |  | } | 
| 4898 | 404 | 50 |  |  |  | 1029 | $a[2] > 24 and warn("Hour '$a[2]' out of range 0..24\n"), return undef; | 
| 4899 | 404 | 50 |  |  |  | 912 | $a[3] > 59 and warn("Minutes '$a[3]' out of range 0..59\n"), return undef; | 
| 4900 | 404 |  |  |  |  | 1656 | $rtnVal = "$yr:$a[0]:$a[1] $a[2]:$a[3]$ss$fs$tz"; | 
| 4901 |  |  |  |  |  |  | } elsif ($dateOnly) { | 
| 4902 | 26 |  |  |  |  | 103 | $rtnVal = join ':', $yr, @a; | 
| 4903 |  |  |  |  |  |  | } | 
| 4904 |  |  |  |  |  |  | } | 
| 4905 | 437 | 100 |  |  |  | 1140 | $rtnVal or warn "Invalid date/time (use YYYY:mm:dd HH:MM:SS[.ss][+/-HH:MM|Z])\n"; | 
| 4906 | 437 |  |  |  |  | 3940 | return $rtnVal; | 
| 4907 |  |  |  |  |  |  | } | 
| 4908 |  |  |  |  |  |  |  | 
| 4909 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4910 |  |  |  |  |  |  | # Set byte order according to our current preferences | 
| 4911 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) default byte order | 
| 4912 |  |  |  |  |  |  | # Returns: new byte order ('II' or 'MM') and sets current byte order | 
| 4913 |  |  |  |  |  |  | # Notes: takes the first of the following that is valid: | 
| 4914 |  |  |  |  |  |  | #  1) ByteOrder option | 
| 4915 |  |  |  |  |  |  | #  2) new value for ExifByteOrder | 
| 4916 |  |  |  |  |  |  | #  3) default byte order passed to this routine | 
| 4917 |  |  |  |  |  |  | #  4) makenote byte order from last file read | 
| 4918 |  |  |  |  |  |  | #  5) big endian | 
| 4919 |  |  |  |  |  |  | sub SetPreferredByteOrder($;$) | 
| 4920 |  |  |  |  |  |  | { | 
| 4921 | 44 |  |  | 44 | 0 | 172 | my ($self, $default) = @_; | 
| 4922 |  |  |  |  |  |  | my $byteOrder = $self->Options('ByteOrder') || | 
| 4923 |  |  |  |  |  |  | $self->GetNewValue('ExifByteOrder') || | 
| 4924 | 44 |  | 100 |  |  | 197 | $default || $$self{MAKER_NOTE_BYTE_ORDER} || 'MM'; | 
| 4925 | 44 | 50 |  |  |  | 262 | unless (SetByteOrder($byteOrder)) { | 
| 4926 | 0 | 0 |  |  |  | 0 | warn "Invalid byte order '${byteOrder}'\n" if $self->Options('Verbose'); | 
| 4927 | 0 |  | 0 |  |  | 0 | $byteOrder = $$self{MAKER_NOTE_BYTE_ORDER} || 'MM'; | 
| 4928 | 0 |  |  |  |  | 0 | SetByteOrder($byteOrder); | 
| 4929 |  |  |  |  |  |  | } | 
| 4930 | 44 |  |  |  |  | 265 | return GetByteOrder(); | 
| 4931 |  |  |  |  |  |  | } | 
| 4932 |  |  |  |  |  |  |  | 
| 4933 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4934 |  |  |  |  |  |  | # Assemble a continuing fraction into a rational value | 
| 4935 |  |  |  |  |  |  | # Inputs: 0) numerator, 1) denominator | 
| 4936 |  |  |  |  |  |  | #         2-N) list of fraction denominators, deepest first | 
| 4937 |  |  |  |  |  |  | # Returns: numerator, denominator (in list context) | 
| 4938 |  |  |  |  |  |  | sub AssembleRational($$@) | 
| 4939 |  |  |  |  |  |  | { | 
| 4940 | 4967 | 100 |  | 4967 | 0 | 10978 | @_ < 3 and return @_; | 
| 4941 | 3417 |  |  |  |  | 6028 | my ($num, $denom, $frac) = splice(@_, 0, 3); | 
| 4942 | 3417 |  |  |  |  | 6421 | return AssembleRational($frac*$num+$denom, $num, @_); | 
| 4943 |  |  |  |  |  |  | } | 
| 4944 |  |  |  |  |  |  |  | 
| 4945 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4946 |  |  |  |  |  |  | # Convert a floating point number (or 'inf' or 'undef' or a fraction) into a rational | 
| 4947 |  |  |  |  |  |  | # Inputs: 0) floating point number, 1) optional maximum value (defaults to 0x7fffffff) | 
| 4948 |  |  |  |  |  |  | # Returns: numerator, denominator (in list context) | 
| 4949 |  |  |  |  |  |  | # Notes: | 
| 4950 |  |  |  |  |  |  | # - the returned rational will be accurate to at least 8 significant figures if possible | 
| 4951 |  |  |  |  |  |  | # - eg. an input of 3.14159265358979 returns a rational of 104348/33215, | 
| 4952 |  |  |  |  |  |  | #   which equals    3.14159265392142 and is accurate to 10 significant figures | 
| 4953 |  |  |  |  |  |  | # - the returned rational will be reduced to the lowest common denominator except when | 
| 4954 |  |  |  |  |  |  | #   the input is a fraction in which case the input is returned unchanged | 
| 4955 |  |  |  |  |  |  | # - these routines were a bit tricky, but fun to write! | 
| 4956 |  |  |  |  |  |  | sub Rationalize($;$) | 
| 4957 |  |  |  |  |  |  | { | 
| 4958 | 739 |  |  | 739 | 0 | 1655 | my $val = shift; | 
| 4959 | 739 | 50 |  |  |  | 1999 | return (1, 0) if $val eq 'inf'; | 
| 4960 | 739 | 50 |  |  |  | 1833 | return (0, 0) if $val eq 'undef'; | 
| 4961 | 739 | 100 |  |  |  | 2540 | return ($1,$2) if $val =~ m{^([-+]?\d+)/(\d+)$}; # accept fractional values | 
| 4962 |  |  |  |  |  |  | # Note: Just testing "if $val" doesn't work because '0.0' is true!  (ugghh!) | 
| 4963 | 723 | 100 |  |  |  | 2529 | return (0, 1) if $val == 0; | 
| 4964 | 685 | 100 |  |  |  | 1712 | my $sign = $val < 0 ? ($val = -$val, -1) : 1; | 
| 4965 | 685 |  |  |  |  | 1740 | my ($num, $denom, @fracs); | 
| 4966 | 685 |  |  |  |  | 1227 | my $frac = $val; | 
| 4967 | 685 |  | 100 |  |  | 2154 | my $maxInt = shift || 0x7fffffff; | 
| 4968 | 685 |  |  |  |  | 1102 | for (;;) { | 
| 4969 | 1550 |  |  |  |  | 4395 | my ($n, $d) = AssembleRational(int($frac + 0.5), 1, @fracs); | 
| 4970 | 1550 | 50 | 33 |  |  | 6049 | if ($n > $maxInt or $d > $maxInt) { | 
| 4971 | 0 | 0 |  |  |  | 0 | last if defined $num; | 
| 4972 | 0 | 0 |  |  |  | 0 | return ($sign, $maxInt) if $val < 1; | 
| 4973 | 0 |  |  |  |  | 0 | return ($sign * $maxInt, 1); | 
| 4974 |  |  |  |  |  |  | } | 
| 4975 | 1550 |  |  |  |  | 2884 | ($num, $denom) = ($n, $d);      # save last good values | 
| 4976 | 1550 |  |  |  |  | 3327 | my $err = ($n/$d-$val) / $val;  # get error of this rational | 
| 4977 | 1550 | 100 |  |  |  | 3744 | last if abs($err) < 1e-8;       # all done if error is small | 
| 4978 | 865 |  |  |  |  | 1378 | my $int = int($frac); | 
| 4979 | 865 |  |  |  |  | 1604 | unshift @fracs, $int; | 
| 4980 | 865 | 50 |  |  |  | 1774 | last unless $frac -= $int; | 
| 4981 | 865 |  |  |  |  | 1435 | $frac = 1 / $frac; | 
| 4982 |  |  |  |  |  |  | } | 
| 4983 | 685 |  |  |  |  | 2674 | return ($num * $sign, $denom); | 
| 4984 |  |  |  |  |  |  | } | 
| 4985 |  |  |  |  |  |  |  | 
| 4986 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4987 |  |  |  |  |  |  | # Utility routines to for writing binary data values | 
| 4988 |  |  |  |  |  |  | # Inputs: 0) value, 1) data ref, 2) offset | 
| 4989 |  |  |  |  |  |  | # Notes: prototype is (@) so values can be passed from list if desired | 
| 4990 |  |  |  |  |  |  | sub Set16s(@) | 
| 4991 |  |  |  |  |  |  | { | 
| 4992 | 188 |  |  | 188 | 0 | 333 | my $val = shift; | 
| 4993 | 188 | 100 |  |  |  | 467 | $val < 0 and $val += 0x10000; | 
| 4994 | 188 |  |  |  |  | 452 | return Set16u($val, @_); | 
| 4995 |  |  |  |  |  |  | } | 
| 4996 |  |  |  |  |  |  | sub Set32s(@) | 
| 4997 |  |  |  |  |  |  | { | 
| 4998 | 67 |  |  | 67 | 0 | 138 | my $val = shift; | 
| 4999 | 67 | 100 |  |  |  | 216 | $val < 0 and $val += 0xffffffff, ++$val; | 
| 5000 | 67 |  |  |  |  | 204 | return Set32u($val, @_); | 
| 5001 |  |  |  |  |  |  | } | 
| 5002 |  |  |  |  |  |  | sub Set64u(@) | 
| 5003 |  |  |  |  |  |  | { | 
| 5004 | 28 |  |  | 28 | 0 | 50 | my $val = $_[0]; | 
| 5005 | 28 |  |  |  |  | 63 | my $hi = int($val / 4294967296); | 
| 5006 | 28 |  |  |  |  | 73 | my $lo = Set32u($val - $hi * 4294967296); | 
| 5007 | 28 |  |  |  |  | 64 | $hi = Set32u($hi); | 
| 5008 | 28 | 100 |  |  |  | 70 | $val = GetByteOrder() eq 'MM' ? $hi . $lo : $lo . $hi; | 
| 5009 | 28 | 100 |  |  |  | 79 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 27 |  |  |  |  | 68 |  | 
| 5010 | 28 |  |  |  |  | 58 | return $val; | 
| 5011 |  |  |  |  |  |  | } | 
| 5012 |  |  |  |  |  |  | sub Set64s(@) | 
| 5013 |  |  |  |  |  |  | { | 
| 5014 | 0 |  |  | 0 | 0 | 0 | my $val = shift; | 
| 5015 | 0 | 0 |  |  |  | 0 | $val < 0 and $val += 4294967296 * 4294967296; # (temporary hack won't really work due to round-off errors) | 
| 5016 | 0 |  |  |  |  | 0 | return Set64u($val, @_); | 
| 5017 |  |  |  |  |  |  | } | 
| 5018 |  |  |  |  |  |  | sub SetRational64u(@) { | 
| 5019 | 428 |  |  | 428 | 0 | 1326 | my ($numer,$denom) = Rationalize($_[0],0xffffffff); | 
| 5020 | 428 |  |  |  |  | 1304 | my $val = Set32u($numer) . Set32u($denom); | 
| 5021 | 428 | 50 |  |  |  | 1308 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5022 | 428 |  |  |  |  | 1466 | return $val; | 
| 5023 |  |  |  |  |  |  | } | 
| 5024 |  |  |  |  |  |  | sub SetRational64s(@) { | 
| 5025 | 42 |  |  | 42 | 0 | 194 | my ($numer,$denom) = Rationalize($_[0]); | 
| 5026 | 42 |  |  |  |  | 242 | my $val = Set32s($numer) . Set32u($denom); | 
| 5027 | 42 | 50 |  |  |  | 173 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5028 | 42 |  |  |  |  | 166 | return $val; | 
| 5029 |  |  |  |  |  |  | } | 
| 5030 |  |  |  |  |  |  | sub SetRational32u(@) { | 
| 5031 | 0 |  |  | 0 | 0 | 0 | my ($numer,$denom) = Rationalize($_[0],0xffff); | 
| 5032 | 0 |  |  |  |  | 0 | my $val = Set16u($numer) . Set16u($denom); | 
| 5033 | 0 | 0 |  |  |  | 0 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5034 | 0 |  |  |  |  | 0 | return $val; | 
| 5035 |  |  |  |  |  |  | } | 
| 5036 |  |  |  |  |  |  | sub SetRational32s(@) { | 
| 5037 | 0 |  |  | 0 | 0 | 0 | my ($numer,$denom) = Rationalize($_[0],0x7fff); | 
| 5038 | 0 |  |  |  |  | 0 | my $val = Set16s($numer) . Set16u($denom); | 
| 5039 | 0 | 0 |  |  |  | 0 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5040 | 0 |  |  |  |  | 0 | return $val; | 
| 5041 |  |  |  |  |  |  | } | 
| 5042 |  |  |  |  |  |  | sub SetFixed16u(@) { | 
| 5043 | 0 |  |  | 0 | 0 | 0 | my $val = int(shift() * 0x100 + 0.5); | 
| 5044 | 0 |  |  |  |  | 0 | return Set16u($val, @_); | 
| 5045 |  |  |  |  |  |  | } | 
| 5046 |  |  |  |  |  |  | sub SetFixed16s(@) { | 
| 5047 | 0 |  |  | 0 | 0 | 0 | my $val = shift; | 
| 5048 | 0 | 0 |  |  |  | 0 | return Set16s(int($val * 0x100 + ($val < 0 ? -0.5 : 0.5)), @_); | 
| 5049 |  |  |  |  |  |  | } | 
| 5050 |  |  |  |  |  |  | sub SetFixed32u(@) { | 
| 5051 | 0 |  |  | 0 | 0 | 0 | my $val = int(shift() * 0x10000 + 0.5); | 
| 5052 | 0 |  |  |  |  | 0 | return Set32u($val, @_); | 
| 5053 |  |  |  |  |  |  | } | 
| 5054 |  |  |  |  |  |  | sub SetFixed32s(@) { | 
| 5055 | 12 |  |  | 12 | 0 | 23 | my $val = shift; | 
| 5056 | 12 | 100 |  |  |  | 82 | return Set32s(int($val * 0x10000 + ($val < 0 ? -0.5 : 0.5)), @_); | 
| 5057 |  |  |  |  |  |  | } | 
| 5058 |  |  |  |  |  |  | sub SetFloat(@) { | 
| 5059 | 61 |  |  | 61 | 0 | 493 | my $val = SwapBytes(pack('f',$_[0]), 4); | 
| 5060 | 61 | 50 |  |  |  | 446 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5061 | 61 |  |  |  |  | 824 | return $val; | 
| 5062 |  |  |  |  |  |  | } | 
| 5063 |  |  |  |  |  |  | sub SetDouble(@) { | 
| 5064 |  |  |  |  |  |  | # swap 32-bit words (ARM quirk) and bytes if necessary | 
| 5065 | 63 |  |  | 63 | 0 | 589 | my $val = SwapBytes(SwapWords(pack('d',$_[0])), 8); | 
| 5066 | 63 | 50 |  |  |  | 300 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5067 | 63 |  |  |  |  | 388 | return $val; | 
| 5068 |  |  |  |  |  |  | } | 
| 5069 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5070 |  |  |  |  |  |  | # hash lookups for writing binary data values | 
| 5071 |  |  |  |  |  |  | my %writeValueProc = ( | 
| 5072 |  |  |  |  |  |  | int8s => \&Set8s, | 
| 5073 |  |  |  |  |  |  | int8u => \&Set8u, | 
| 5074 |  |  |  |  |  |  | int16s => \&Set16s, | 
| 5075 |  |  |  |  |  |  | int16u => \&Set16u, | 
| 5076 |  |  |  |  |  |  | int16uRev => \&Set16uRev, | 
| 5077 |  |  |  |  |  |  | int32s => \&Set32s, | 
| 5078 |  |  |  |  |  |  | int32u => \&Set32u, | 
| 5079 |  |  |  |  |  |  | int64s => \&Set64s, | 
| 5080 |  |  |  |  |  |  | int64u => \&Set64u, | 
| 5081 |  |  |  |  |  |  | rational32s => \&SetRational32s, | 
| 5082 |  |  |  |  |  |  | rational32u => \&SetRational32u, | 
| 5083 |  |  |  |  |  |  | rational64s => \&SetRational64s, | 
| 5084 |  |  |  |  |  |  | rational64u => \&SetRational64u, | 
| 5085 |  |  |  |  |  |  | fixed16u => \&SetFixed16u, | 
| 5086 |  |  |  |  |  |  | fixed16s => \&SetFixed16s, | 
| 5087 |  |  |  |  |  |  | fixed32u => \&SetFixed32u, | 
| 5088 |  |  |  |  |  |  | fixed32s => \&SetFixed32s, | 
| 5089 |  |  |  |  |  |  | float => \&SetFloat, | 
| 5090 |  |  |  |  |  |  | double => \&SetDouble, | 
| 5091 |  |  |  |  |  |  | ifd => \&Set32u, | 
| 5092 |  |  |  |  |  |  | ); | 
| 5093 |  |  |  |  |  |  | # verify that we can write floats on this platform | 
| 5094 |  |  |  |  |  |  | { | 
| 5095 |  |  |  |  |  |  | my %writeTest = ( | 
| 5096 |  |  |  |  |  |  | float =>  [ -3.14159, 'c0490fd0' ], | 
| 5097 |  |  |  |  |  |  | double => [ -3.14159, 'c00921f9f01b866e' ], | 
| 5098 |  |  |  |  |  |  | ); | 
| 5099 |  |  |  |  |  |  | my $format; | 
| 5100 |  |  |  |  |  |  | my $oldOrder = GetByteOrder(); | 
| 5101 |  |  |  |  |  |  | SetByteOrder('MM'); | 
| 5102 |  |  |  |  |  |  | foreach $format (keys %writeTest) { | 
| 5103 |  |  |  |  |  |  | my ($val, $hex) = @{$writeTest{$format}}; | 
| 5104 |  |  |  |  |  |  | # add floating point entries if we can write them | 
| 5105 |  |  |  |  |  |  | next if unpack('H*', &{$writeValueProc{$format}}($val)) eq $hex; | 
| 5106 |  |  |  |  |  |  | delete $writeValueProc{$format};    # we can't write them | 
| 5107 |  |  |  |  |  |  | } | 
| 5108 |  |  |  |  |  |  | SetByteOrder($oldOrder); | 
| 5109 |  |  |  |  |  |  | } | 
| 5110 |  |  |  |  |  |  |  | 
| 5111 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5112 |  |  |  |  |  |  | # write binary data value (with current byte ordering) | 
| 5113 |  |  |  |  |  |  | # Inputs: 0) value, 1) format string | 
| 5114 |  |  |  |  |  |  | #         2) number of values: | 
| 5115 |  |  |  |  |  |  | #               undef = 1 for numerical types, or data length for string/undef types | 
| 5116 |  |  |  |  |  |  | #                  -1 = number of space-delimited values in the input string | 
| 5117 |  |  |  |  |  |  | #         3) optional data reference, 4) value offset (may be negative for bytes from end) | 
| 5118 |  |  |  |  |  |  | # Returns: packed value (and sets value in data) or undef on error | 
| 5119 |  |  |  |  |  |  | # Notes: May modify input value to round for integer formats | 
| 5120 |  |  |  |  |  |  | sub WriteValue($$;$$$$) | 
| 5121 |  |  |  |  |  |  | { | 
| 5122 | 1598 |  |  | 1598 | 0 | 4155 | my ($val, $format, $count, $dataPt, $offset) = @_; | 
| 5123 | 1598 |  |  |  |  | 3454 | my $proc = $writeValueProc{$format}; | 
| 5124 | 1598 |  |  |  |  | 2791 | my $packed; | 
| 5125 |  |  |  |  |  |  |  | 
| 5126 | 1598 | 100 | 66 |  |  | 4142 | if ($proc) { | 
|  |  | 50 |  |  |  |  |  | 
| 5127 | 1238 |  |  |  |  | 4085 | my @vals = split(' ',$val); | 
| 5128 | 1238 | 100 |  |  |  | 2681 | if ($count) { | 
| 5129 | 661 | 100 |  |  |  | 1764 | $count = @vals if $count < 0; | 
| 5130 |  |  |  |  |  |  | } else { | 
| 5131 | 577 |  |  |  |  | 1023 | $count = 1;   # assume 1 if count not specified | 
| 5132 |  |  |  |  |  |  | } | 
| 5133 | 1238 |  |  |  |  | 2143 | $packed = ''; | 
| 5134 | 1238 |  |  |  |  | 2957 | while ($count--) { | 
| 5135 | 1719 |  |  |  |  | 3010 | $val = shift @vals; | 
| 5136 | 1719 | 50 |  |  |  | 3883 | return undef unless defined $val; | 
| 5137 |  |  |  |  |  |  | # validate numerical formats | 
| 5138 | 1719 | 100 |  |  |  | 6774 | if ($format =~ /^int/) { | 
|  |  | 100 |  |  |  |  |  | 
| 5139 | 1232 | 50 | 33 |  |  | 3472 | unless (IsInt($val) or IsHex($val)) { | 
| 5140 | 0 | 0 |  |  |  | 0 | return undef unless IsFloat($val); | 
| 5141 |  |  |  |  |  |  | # round to nearest integer | 
| 5142 | 0 | 0 |  |  |  | 0 | $val = int($val + ($val < 0 ? -0.5 : 0.5)); | 
| 5143 | 0 |  |  |  |  | 0 | $_[0] = $val; | 
| 5144 |  |  |  |  |  |  | } | 
| 5145 |  |  |  |  |  |  | } elsif (not IsFloat($val)) { | 
| 5146 | 7 | 50 | 33 |  |  | 151 | return undef unless $format =~ /^rational/ and ($val eq 'inf' or | 
|  |  |  | 33 |  |  |  |  | 
| 5147 |  |  |  |  |  |  | $val eq 'undef' or IsRational($val)); | 
| 5148 |  |  |  |  |  |  | } | 
| 5149 | 1719 |  |  |  |  | 4664 | $packed .= &$proc($val); | 
| 5150 |  |  |  |  |  |  | } | 
| 5151 |  |  |  |  |  |  | } elsif ($format eq 'string' or $format eq 'undef') { | 
| 5152 | 360 | 100 |  |  |  | 1100 | $format eq 'string' and $val .= "\0";   # null-terminate strings | 
| 5153 | 360 | 100 | 66 |  |  | 1247 | if ($count and $count > 0) { | 
| 5154 | 61 |  |  |  |  | 177 | my $diff = $count - length($val); | 
| 5155 | 61 | 100 |  |  |  | 186 | if ($diff) { | 
| 5156 |  |  |  |  |  |  | #warn "wrong string length!\n"; | 
| 5157 |  |  |  |  |  |  | # adjust length of string to match specified count | 
| 5158 | 29 | 100 |  |  |  | 90 | if ($diff < 0) { | 
| 5159 | 22 | 50 |  |  |  | 60 | if ($format eq 'string') { | 
| 5160 | 22 | 50 |  |  |  | 59 | return undef unless $count; | 
| 5161 | 22 |  |  |  |  | 66 | $val = substr($val, 0, $count - 1) . "\0"; | 
| 5162 |  |  |  |  |  |  | } else { | 
| 5163 | 0 |  |  |  |  | 0 | $val = substr($val, 0, $count); | 
| 5164 |  |  |  |  |  |  | } | 
| 5165 |  |  |  |  |  |  | } else { | 
| 5166 | 7 |  |  |  |  | 24 | $val .= "\0" x $diff; | 
| 5167 |  |  |  |  |  |  | } | 
| 5168 |  |  |  |  |  |  | } | 
| 5169 |  |  |  |  |  |  | } else { | 
| 5170 | 299 |  |  |  |  | 605 | $count = length($val); | 
| 5171 |  |  |  |  |  |  | } | 
| 5172 | 360 | 100 |  |  |  | 809 | $dataPt and substr($$dataPt, $offset, $count) = $val; | 
| 5173 | 360 |  |  |  |  | 1185 | return $val; | 
| 5174 |  |  |  |  |  |  | } else { | 
| 5175 | 0 |  |  |  |  | 0 | warn "Sorry, Can't write $format values on this platform\n"; | 
| 5176 | 0 |  |  |  |  | 0 | return undef; | 
| 5177 |  |  |  |  |  |  | } | 
| 5178 | 1238 | 100 |  |  |  | 3182 | $dataPt and substr($$dataPt, $offset, length($packed)) = $packed; | 
| 5179 | 1238 |  |  |  |  | 3387 | return $packed; | 
| 5180 |  |  |  |  |  |  | } | 
| 5181 |  |  |  |  |  |  |  | 
| 5182 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5183 |  |  |  |  |  |  | # Encode bit mask (the inverse of DecodeBits()) | 
| 5184 |  |  |  |  |  |  | # Inputs: 0) value to encode, 1) Reference to hash for encoding (or undef) | 
| 5185 |  |  |  |  |  |  | #         2) optional number of bits per word (defaults to 32), 3) total bits | 
| 5186 |  |  |  |  |  |  | # Returns: bit mask or undef on error (plus error string in list context) | 
| 5187 |  |  |  |  |  |  | sub EncodeBits($$;$$) | 
| 5188 |  |  |  |  |  |  | { | 
| 5189 | 100 |  |  | 100 | 0 | 305 | my ($val, $lookup, $bits, $num) = @_; | 
| 5190 | 100 | 100 |  |  |  | 311 | $bits or $bits = 32; | 
| 5191 | 100 | 100 |  |  |  | 291 | $num or $num = $bits; | 
| 5192 | 100 |  |  |  |  | 399 | my $words = int(($num + $bits - 1) / $bits); | 
| 5193 | 100 |  |  |  |  | 337 | my @outVal = (0) x $words; | 
| 5194 | 100 | 100 |  |  |  | 316 | if ($val ne '(none)') { | 
| 5195 | 82 |  |  |  |  | 382 | my @vals = split /\s*,\s*/, $val; | 
| 5196 | 82 |  |  |  |  | 268 | foreach $val (@vals) { | 
| 5197 | 42 |  |  |  |  | 84 | my $bit; | 
| 5198 | 42 | 50 |  |  |  | 116 | if ($lookup) { | 
| 5199 | 42 |  |  |  |  | 150 | $bit = ReverseLookup($val, $lookup); | 
| 5200 |  |  |  |  |  |  | # (Note: may get non-numerical $bit values from Unknown() tags) | 
| 5201 | 42 | 100 |  |  |  | 159 | unless (defined $bit) { | 
| 5202 | 33 | 50 |  |  |  | 124 | if ($val =~ /\[(\d+)\]/) { # numerical bit specification | 
| 5203 | 0 |  |  |  |  | 0 | $bit = $1; | 
| 5204 |  |  |  |  |  |  | } else { | 
| 5205 |  |  |  |  |  |  | # don't return error string unless more than one value | 
| 5206 | 33 | 100 | 66 |  |  | 215 | return undef unless @vals > 1 and wantarray; | 
| 5207 | 2 |  |  |  |  | 16 | return (undef, "no match for '${val}'"); | 
| 5208 |  |  |  |  |  |  | } | 
| 5209 |  |  |  |  |  |  | } | 
| 5210 |  |  |  |  |  |  | } else { | 
| 5211 | 0 |  |  |  |  | 0 | $bit = $val; | 
| 5212 |  |  |  |  |  |  | } | 
| 5213 | 9 | 50 | 33 |  |  | 41 | unless (IsInt($bit) and $bit < $num) { | 
| 5214 | 0 | 0 |  |  |  | 0 | return undef unless wantarray; | 
| 5215 | 0 | 0 |  |  |  | 0 | return (undef, IsInt($bit) ? 'bit number too high' : 'not an integer'); | 
| 5216 |  |  |  |  |  |  | } | 
| 5217 | 9 |  |  |  |  | 39 | my $word = int($bit / $bits); | 
| 5218 | 9 |  |  |  |  | 56 | $outVal[$word] |= (1 << ($bit - $word * $bits)); | 
| 5219 |  |  |  |  |  |  | } | 
| 5220 |  |  |  |  |  |  | } | 
| 5221 | 67 |  |  |  |  | 430 | return "@outVal"; | 
| 5222 |  |  |  |  |  |  | } | 
| 5223 |  |  |  |  |  |  |  | 
| 5224 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5225 |  |  |  |  |  |  | # get current position in output file (or end of file if a scalar reference) | 
| 5226 |  |  |  |  |  |  | # Inputs: 0) file or scalar reference | 
| 5227 |  |  |  |  |  |  | # Returns: Current position or -1 on error | 
| 5228 |  |  |  |  |  |  | sub Tell($) | 
| 5229 |  |  |  |  |  |  | { | 
| 5230 | 324 |  |  | 324 | 0 | 719 | my $outfile = shift; | 
| 5231 | 324 | 100 |  |  |  | 1334 | if (UNIVERSAL::isa($outfile,'GLOB')) { | 
| 5232 | 295 |  |  |  |  | 1878 | return tell($outfile); | 
| 5233 |  |  |  |  |  |  | } else { | 
| 5234 | 29 |  |  |  |  | 207 | return length($$outfile); | 
| 5235 |  |  |  |  |  |  | } | 
| 5236 |  |  |  |  |  |  | } | 
| 5237 |  |  |  |  |  |  |  | 
| 5238 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5239 |  |  |  |  |  |  | # write to file or memory | 
| 5240 |  |  |  |  |  |  | # Inputs: 0) file or scalar reference, 1-N) list of stuff to write | 
| 5241 |  |  |  |  |  |  | # Returns: true on success | 
| 5242 |  |  |  |  |  |  | sub Write($@) | 
| 5243 |  |  |  |  |  |  | { | 
| 5244 | 3726 |  |  | 3726 | 0 | 6196 | my $outfile = shift; | 
| 5245 | 3726 | 100 |  |  |  | 12750 | if (UNIVERSAL::isa($outfile,'GLOB')) { | 
|  |  | 50 |  |  |  |  |  | 
| 5246 | 2301 |  |  |  |  | 18942 | return print $outfile @_; | 
| 5247 |  |  |  |  |  |  | } elsif (ref $outfile eq 'SCALAR') { | 
| 5248 | 1425 |  |  |  |  | 6423 | $$outfile .= join('', @_); | 
| 5249 | 1425 |  |  |  |  | 5414 | return 1; | 
| 5250 |  |  |  |  |  |  | } | 
| 5251 | 0 |  |  |  |  | 0 | return 0; | 
| 5252 |  |  |  |  |  |  | } | 
| 5253 |  |  |  |  |  |  |  | 
| 5254 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5255 |  |  |  |  |  |  | # Write trailer buffer to file (applying fixups if necessary) | 
| 5256 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) trailer dirInfo ref, 2) output file ref | 
| 5257 |  |  |  |  |  |  | # Returns: 1 on success | 
| 5258 |  |  |  |  |  |  | sub WriteTrailerBuffer($$$) | 
| 5259 |  |  |  |  |  |  | { | 
| 5260 | 12 |  |  | 12 | 0 | 47 | my ($self, $trailInfo, $outfile) = @_; | 
| 5261 | 12 | 50 |  |  |  | 62 | if ($$self{DEL_GROUP}{Trailer}) { | 
| 5262 | 0 |  |  |  |  | 0 | $self->VPrint(0, "  Deleting trailer ($$trailInfo{Offset} bytes)\n"); | 
| 5263 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 5264 | 0 |  |  |  |  | 0 | return 1; | 
| 5265 |  |  |  |  |  |  | } | 
| 5266 | 12 |  |  |  |  | 71 | my $pos = Tell($outfile); | 
| 5267 | 12 |  |  |  |  | 34 | my $trailPt = $$trailInfo{OutFile}; | 
| 5268 |  |  |  |  |  |  | # apply fixup if necessary (AFCP requires this) | 
| 5269 | 12 | 100 |  |  |  | 60 | if ($$trailInfo{Fixup}) { | 
| 5270 | 8 | 50 |  |  |  | 40 | if ($pos > 0) { | 
| 5271 |  |  |  |  |  |  | # shift offsets to final AFCP location and write it out | 
| 5272 | 8 |  |  |  |  | 23 | $$trailInfo{Fixup}{Shift} += $pos; | 
| 5273 | 8 |  |  |  |  | 40 | $$trailInfo{Fixup}->ApplyFixup($trailPt); | 
| 5274 |  |  |  |  |  |  | } else { | 
| 5275 | 0 |  |  |  |  | 0 | $self->Error("Can't get file position for trailer offset fixup",1); | 
| 5276 |  |  |  |  |  |  | } | 
| 5277 |  |  |  |  |  |  | } | 
| 5278 | 12 |  |  |  |  | 61 | return Write($outfile, $$trailPt); | 
| 5279 |  |  |  |  |  |  | } | 
| 5280 |  |  |  |  |  |  |  | 
| 5281 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5282 |  |  |  |  |  |  | # Add trailers as a block | 
| 5283 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) [optional] trailer data raf, | 
| 5284 |  |  |  |  |  |  | #         1 or 2-N) trailer types to add (or none to add all) | 
| 5285 |  |  |  |  |  |  | # Returns: new trailer ref, or undef | 
| 5286 |  |  |  |  |  |  | # - increments CHANGED if trailer was added | 
| 5287 |  |  |  |  |  |  | sub AddNewTrailers($;@) | 
| 5288 |  |  |  |  |  |  | { | 
| 5289 | 129 |  |  | 129 | 0 | 505 | my ($self, @types) = @_; | 
| 5290 | 129 |  |  |  |  | 265 | my $trailPt; | 
| 5291 | 129 | 100 |  |  |  | 491 | ref $types[0] and $trailPt = shift @types; | 
| 5292 | 129 | 100 |  |  |  | 529 | $types[0] or shift @types; # (in case undef data ref is passed) | 
| 5293 |  |  |  |  |  |  | # add all possible trailers if none specified (currently only CanonVRD) | 
| 5294 | 129 | 100 |  |  |  | 683 | @types or @types = qw(CanonVRD CanonDR4); | 
| 5295 |  |  |  |  |  |  | # add trailers as a block (if not done already) | 
| 5296 | 129 |  |  |  |  | 323 | my $type; | 
| 5297 | 129 |  |  |  |  | 464 | foreach $type (@types) { | 
| 5298 | 251 | 100 |  |  |  | 1306 | next unless $$self{NEW_VALUE}{$Image::ExifTool::Extra{$type}}; | 
| 5299 | 10 | 100 |  |  |  | 58 | next if $$self{"Did$type"}; | 
| 5300 | 9 | 100 |  |  |  | 45 | my $val = $self->GetNewValue($type) or next; | 
| 5301 |  |  |  |  |  |  | # DR4 record must be wrapped in VRD trailer package | 
| 5302 | 8 | 100 |  |  |  | 40 | if ($type eq 'CanonDR4') { | 
| 5303 | 3 | 100 |  |  |  | 19 | next if $$self{DidCanonVRD};    # (only allow one VRD trailer) | 
| 5304 | 2 |  |  |  |  | 25 | require Image::ExifTool::CanonVRD; | 
| 5305 | 2 |  |  |  |  | 20 | $val = Image::ExifTool::CanonVRD::WrapDR4($val); | 
| 5306 | 2 |  |  |  |  | 9 | $$self{DidCanonVRD} = 1; | 
| 5307 |  |  |  |  |  |  | } | 
| 5308 | 7 | 50 |  |  |  | 55 | my $verb = $trailPt ? 'Writing' : 'Adding'; | 
| 5309 | 7 |  |  |  |  | 59 | $self->VPrint(0, "  $verb $type as a block\n"); | 
| 5310 | 7 | 50 |  |  |  | 35 | if ($trailPt) { | 
| 5311 | 0 |  |  |  |  | 0 | $$trailPt .= $val; | 
| 5312 |  |  |  |  |  |  | } else { | 
| 5313 | 7 |  |  |  |  | 19 | $trailPt = \$val; | 
| 5314 |  |  |  |  |  |  | } | 
| 5315 | 7 |  |  |  |  | 32 | $$self{"Did$type"} = 1; | 
| 5316 | 7 |  |  |  |  | 25 | ++$$self{CHANGED}; | 
| 5317 |  |  |  |  |  |  | } | 
| 5318 | 129 |  |  |  |  | 443 | return $trailPt; | 
| 5319 |  |  |  |  |  |  | } | 
| 5320 |  |  |  |  |  |  |  | 
| 5321 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5322 |  |  |  |  |  |  | # Write segment, splitting up into multiple segments if necessary | 
| 5323 |  |  |  |  |  |  | # Inputs: 0) file or scalar reference, 1) segment marker | 
| 5324 |  |  |  |  |  |  | #         2) segment header, 3) segment data ref, 4) segment type | 
| 5325 |  |  |  |  |  |  | # Returns: number of segments written, or 0 on error | 
| 5326 |  |  |  |  |  |  | # Notes: Writes a single empty segment if data is empty | 
| 5327 |  |  |  |  |  |  | sub WriteMultiSegment($$$$;$) | 
| 5328 |  |  |  |  |  |  | { | 
| 5329 | 110 |  |  | 110 | 0 | 521 | my ($outfile, $marker, $header, $dataPt, $type) = @_; | 
| 5330 | 110 | 100 |  |  |  | 464 | $type or $type = ''; | 
| 5331 | 110 |  |  |  |  | 296 | my $len = length($$dataPt); | 
| 5332 | 110 |  |  |  |  | 421 | my $hdr = "\xff" . chr($marker); | 
| 5333 | 110 |  |  |  |  | 210 | my $count = 0; | 
| 5334 | 110 |  |  |  |  | 295 | my $maxLen = $maxSegmentLen - length($header); | 
| 5335 | 110 | 100 |  |  |  | 438 | $maxLen -= 2 if $type eq 'ICC'; # leave room for segment counters | 
| 5336 | 110 |  |  |  |  | 434 | my $num = int(($len + $maxLen - 1) / $maxLen);  # number of segments to write | 
| 5337 | 110 |  |  |  |  | 243 | my $n = 0; | 
| 5338 |  |  |  |  |  |  | # write data, splitting into multiple segments if necessary | 
| 5339 |  |  |  |  |  |  | # (each segment gets its own header) | 
| 5340 | 110 |  |  |  |  | 217 | for (;;) { | 
| 5341 | 110 |  |  |  |  | 221 | ++$count; | 
| 5342 | 110 |  |  |  |  | 251 | my $size = $len - $n; | 
| 5343 | 110 | 50 |  |  |  | 388 | if ($size > $maxLen) { | 
| 5344 | 0 |  |  |  |  | 0 | $size = $maxLen; | 
| 5345 |  |  |  |  |  |  | # avoid starting an Extended EXIF segment with a valid TIFF header | 
| 5346 |  |  |  |  |  |  | # (because we would interpret that as a separate EXIF segment) | 
| 5347 | 0 | 0 | 0 |  |  | 0 | --$size if $type eq 'EXIF' and $n+$maxLen <= $len-4 and | 
|  |  |  | 0 |  |  |  |  | 
| 5348 |  |  |  |  |  |  | substr($$dataPt, $n+$maxLen, 4) =~ /^(MM\0\x2a|II\x2a\0)/; | 
| 5349 |  |  |  |  |  |  | } | 
| 5350 | 110 |  |  |  |  | 615 | my $buff = substr($$dataPt,$n,$size); | 
| 5351 | 110 |  |  |  |  | 301 | $n += $size; | 
| 5352 | 110 |  |  |  |  | 290 | $size += length($header); | 
| 5353 | 110 | 100 |  |  |  | 496 | if ($type eq 'ICC') { | 
| 5354 | 3 |  |  |  |  | 21 | $buff = pack('CC', $count, $num) . $buff; | 
| 5355 | 3 |  |  |  |  | 14 | $size += 2; | 
| 5356 |  |  |  |  |  |  | } | 
| 5357 |  |  |  |  |  |  | # write the new segment with appropriate header | 
| 5358 | 110 |  |  |  |  | 506 | my $segHdr = $hdr . pack('n', $size + 2); | 
| 5359 | 110 | 50 |  |  |  | 423 | Write($outfile, $segHdr, $header, $buff) or return 0; | 
| 5360 | 110 | 50 |  |  |  | 522 | last if $n >= $len; | 
| 5361 |  |  |  |  |  |  | } | 
| 5362 | 110 |  |  |  |  | 409 | return $count; | 
| 5363 |  |  |  |  |  |  | } | 
| 5364 |  |  |  |  |  |  |  | 
| 5365 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5366 |  |  |  |  |  |  | # Write XMP segment(s) to JPEG file | 
| 5367 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) outfile ref, 2) XMP data ref, | 
| 5368 |  |  |  |  |  |  | #         3) extended XMP data ref, 4) 32-char extended XMP GUID (or undef if no extended data) | 
| 5369 |  |  |  |  |  |  | # Returns: true on success, false on write error | 
| 5370 |  |  |  |  |  |  | sub WriteMultiXMP($$$$$) | 
| 5371 |  |  |  |  |  |  | { | 
| 5372 | 33 |  |  | 33 | 0 | 141 | my ($self, $outfile, $dataPt, $extPt, $guid) = @_; | 
| 5373 | 33 |  |  |  |  | 77 | my $success = 1; | 
| 5374 |  |  |  |  |  |  |  | 
| 5375 |  |  |  |  |  |  | # write main XMP segment | 
| 5376 | 33 |  |  |  |  | 116 | my $size = length($$dataPt) + length($xmpAPP1hdr); | 
| 5377 | 33 | 50 |  |  |  | 132 | if ($size > $maxXMPLen) { | 
| 5378 | 0 |  |  |  |  | 0 | $self->Error("XMP block too large for JPEG segment! ($size bytes)", 1); | 
| 5379 | 0 |  |  |  |  | 0 | return 1; | 
| 5380 |  |  |  |  |  |  | } | 
| 5381 | 33 |  |  |  |  | 238 | my $app1hdr = "\xff\xe1" . pack('n', $size + 2); | 
| 5382 | 33 | 50 |  |  |  | 204 | Write($outfile, $app1hdr, $xmpAPP1hdr, $$dataPt) or $success = 0; | 
| 5383 |  |  |  |  |  |  | # write extended XMP segment(s) if necessary | 
| 5384 | 33 | 50 |  |  |  | 159 | if (defined $guid) { | 
| 5385 | 0 |  |  |  |  | 0 | $size = length($$extPt); | 
| 5386 | 0 |  |  |  |  | 0 | my $maxLen = $maxXMPLen - 75; # maximum size without 75-byte header | 
| 5387 | 0 |  |  |  |  | 0 | my $off; | 
| 5388 | 0 |  |  |  |  | 0 | for ($off=0; $off<$size; $off+=$maxLen) { | 
| 5389 |  |  |  |  |  |  | # header(75) = signature(35) + guid(32) + size(4) + offset(4) | 
| 5390 | 0 |  |  |  |  | 0 | my $len = $size - $off; | 
| 5391 | 0 | 0 |  |  |  | 0 | $len = $maxLen if $len > $maxLen; | 
| 5392 | 0 |  |  |  |  | 0 | $app1hdr = "\xff\xe1" . pack('n', $len + 75 + 2); | 
| 5393 | 0 |  |  |  |  | 0 | $self->VPrint(0, "Writing extended XMP segment ($len bytes)\n"); | 
| 5394 | 0 | 0 |  |  |  | 0 | Write($outfile, $app1hdr, $xmpExtAPP1hdr, $guid, pack('N2', $size, $off), | 
| 5395 |  |  |  |  |  |  | substr($$extPt, $off, $len)) or $success = 0; | 
| 5396 |  |  |  |  |  |  | } | 
| 5397 |  |  |  |  |  |  | } | 
| 5398 | 33 |  |  |  |  | 170 | return $success; | 
| 5399 |  |  |  |  |  |  | } | 
| 5400 |  |  |  |  |  |  |  | 
| 5401 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5402 |  |  |  |  |  |  | # WriteJPEG : Write JPEG image | 
| 5403 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) dirInfo reference | 
| 5404 |  |  |  |  |  |  | # Returns: 1 on success, 0 if this wasn't a valid JPEG file, or -1 if | 
| 5405 |  |  |  |  |  |  | #          an output file was specified and a write error occurred | 
| 5406 |  |  |  |  |  |  | sub WriteJPEG($$) | 
| 5407 |  |  |  |  |  |  | { | 
| 5408 | 110 |  |  | 110 | 0 | 364 | my ($self, $dirInfo) = @_; | 
| 5409 | 110 |  |  |  |  | 396 | my $outfile = $$dirInfo{OutFile}; | 
| 5410 | 110 |  |  |  |  | 320 | my $raf = $$dirInfo{RAF}; | 
| 5411 | 110 |  |  |  |  | 307 | my ($ch, $s, $length,$err, %doneDir, $isEXV, $creatingEXV); | 
| 5412 | 110 |  |  |  |  | 379 | my $verbose = $$self{OPTIONS}{Verbose}; | 
| 5413 | 110 |  |  |  |  | 3859 | my $out = $$self{OPTIONS}{TextOut}; | 
| 5414 | 110 |  |  |  |  | 287 | my $rtnVal = 0; | 
| 5415 | 110 |  |  |  |  | 463 | my %dumpParms = ( Out => $out ); | 
| 5416 | 110 |  |  |  |  | 292 | my ($writeBuffer, $oldOutfile); # used to buffer writing until PreviewImage position is known | 
| 5417 |  |  |  |  |  |  |  | 
| 5418 |  |  |  |  |  |  | # check to be sure this is a valid JPG or EXV file | 
| 5419 | 110 | 100 | 100 |  |  | 580 | unless ($raf->Read($s,2) == 2 and $s eq "\xff\xd8") { | 
| 5420 | 2 | 100 | 66 |  |  | 20 | if (defined $s and length $s) { | 
| 5421 | 1 | 50 | 33 |  |  | 12 | return 0 unless $s eq "\xff\x01" and $raf->Read($s,5) == 5 and $s eq 'Exiv2'; | 
|  |  |  | 33 |  |  |  |  | 
| 5422 |  |  |  |  |  |  | } else { | 
| 5423 | 1 | 50 |  |  |  | 5 | return 0 unless $$self{FILE_TYPE} eq 'EXV'; | 
| 5424 | 1 |  |  |  |  | 2 | $s = 'Exiv2'; | 
| 5425 | 1 |  |  |  |  | 3 | $creatingEXV = 1; | 
| 5426 |  |  |  |  |  |  | } | 
| 5427 | 2 | 50 |  |  |  | 11 | Write($outfile,"\xff\x01") or $err = 1; | 
| 5428 | 2 |  |  |  |  | 6 | $isEXV = 1; | 
| 5429 |  |  |  |  |  |  | } | 
| 5430 | 110 | 50 |  |  |  | 741 | $dumpParms{MaxLen} = 128 unless $verbose > 3; | 
| 5431 |  |  |  |  |  |  |  | 
| 5432 | 110 |  |  |  |  | 337 | delete $$self{PREVIEW_INFO};   # reset preview information | 
| 5433 | 110 |  |  |  |  | 307 | delete $$self{DEL_PREVIEW};    # reset flag to delete preview | 
| 5434 |  |  |  |  |  |  |  | 
| 5435 | 110 | 50 |  |  |  | 559 | Write($outfile, $s) or $err = 1; | 
| 5436 |  |  |  |  |  |  | # figure out what segments we need to write for the tags we have set | 
| 5437 | 110 |  |  |  |  | 463 | my $addDirs = $$self{ADD_DIRS}; | 
| 5438 | 110 |  |  |  |  | 309 | my $editDirs = $$self{EDIT_DIRS}; | 
| 5439 | 110 |  |  |  |  | 361 | my $delGroup = $$self{DEL_GROUP}; | 
| 5440 | 110 |  |  |  |  | 339 | my $path = $$self{PATH}; | 
| 5441 | 110 |  |  |  |  | 311 | my $pn = scalar @$path; | 
| 5442 |  |  |  |  |  |  |  | 
| 5443 |  |  |  |  |  |  | # set input record separator to 0xff (the JPEG marker) to make reading quicker | 
| 5444 | 110 |  |  |  |  | 1322 | local $/ = "\xff"; | 
| 5445 |  |  |  |  |  |  | # | 
| 5446 |  |  |  |  |  |  | # pre-scan image to determine if any create-able segment already exists | 
| 5447 |  |  |  |  |  |  | # | 
| 5448 | 110 |  |  |  |  | 598 | my $pos = $raf->Tell(); | 
| 5449 | 110 |  |  |  |  | 401 | my ($marker, @dirOrder, %dirCount); | 
| 5450 | 110 |  |  |  |  | 282 | Prescan: for (;;) { | 
| 5451 |  |  |  |  |  |  | # read up to next marker (JPEG markers begin with 0xff) | 
| 5452 | 792 | 100 |  |  |  | 2569 | $raf->ReadLine($s) or last; | 
| 5453 |  |  |  |  |  |  | # JPEG markers can be padded with unlimited 0xff's | 
| 5454 | 791 |  |  |  |  | 1471 | for (;;) { | 
| 5455 | 791 | 50 |  |  |  | 2287 | $raf->Read($ch, 1) or last Prescan; | 
| 5456 | 791 |  |  |  |  | 1578 | $marker = ord($ch); | 
| 5457 | 791 | 50 |  |  |  | 2082 | last unless $marker == 0xff; | 
| 5458 |  |  |  |  |  |  | } | 
| 5459 | 791 |  |  |  |  | 1277 | my $dirName; | 
| 5460 |  |  |  |  |  |  | # stop pre-scan at SOS (end of meta information) or EOI (end of image) | 
| 5461 | 791 | 100 | 100 |  |  | 3061 | if ($marker == 0xda or $marker == 0xd9) { | 
| 5462 | 109 |  |  |  |  | 588 | $dirName = $jpegMarker{$marker}; | 
| 5463 | 109 |  |  |  |  | 387 | push(@dirOrder, $dirName); | 
| 5464 | 109 |  |  |  |  | 374 | $dirCount{$dirName} = 1; | 
| 5465 | 109 |  |  |  |  | 257 | last; | 
| 5466 |  |  |  |  |  |  | } | 
| 5467 |  |  |  |  |  |  | # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc) | 
| 5468 | 682 | 100 | 66 |  |  | 5653 | if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) { | 
|  |  | 50 | 100 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 5469 | 108 | 50 |  |  |  | 435 | last unless $raf->Seek(7, 1); | 
| 5470 |  |  |  |  |  |  | # read data for all markers except stand-alone | 
| 5471 |  |  |  |  |  |  | # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7) | 
| 5472 |  |  |  |  |  |  | } elsif ($marker!=0x00 and $marker!=0x01 and ($marker<0xd0 or $marker>0xd7)) { | 
| 5473 |  |  |  |  |  |  | # read record length word | 
| 5474 | 574 | 50 |  |  |  | 1533 | last unless $raf->Read($s, 2) == 2; | 
| 5475 | 574 |  |  |  |  | 2039 | my $len = unpack('n',$s);   # get data length | 
| 5476 | 574 | 50 | 33 |  |  | 2629 | last unless defined($len) and $len >= 2; | 
| 5477 | 574 |  |  |  |  | 1114 | $len -= 2;  # subtract size of length word | 
| 5478 | 574 | 100 |  |  |  | 1475 | if (($marker & 0xf0) == 0xe0) {  # is this an APP segment? | 
| 5479 | 347 | 100 |  |  |  | 818 | my $n = $len < 64 ? $len : 64; | 
| 5480 | 347 | 50 |  |  |  | 994 | $raf->Read($s, $n) == $n or last; | 
| 5481 | 347 |  |  |  |  | 648 | $len -= $n; | 
| 5482 |  |  |  |  |  |  | # Note: only necessary to recognize APP segments that we can create, | 
| 5483 |  |  |  |  |  |  | # or delete as a group (and the names below should match @delGroups) | 
| 5484 | 347 | 100 |  |  |  | 1929 | if ($marker == 0xe0) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 5485 | 45 | 100 |  |  |  | 270 | $s =~ /^JFIF\0/         and $dirName = 'JFIF'; | 
| 5486 | 45 | 100 |  |  |  | 191 | $s =~ /^JFXX\0\x10/     and $dirName = 'JFXX'; | 
| 5487 | 45 | 100 |  |  |  | 203 | $s =~ /^(II|MM).{4}HEAPJPGM/s and $dirName = 'CIFF'; | 
| 5488 |  |  |  |  |  |  | } elsif ($marker == 0xe1) { | 
| 5489 | 84 | 100 |  |  |  | 1373 | if ($s =~ /^(.{0,4})$exifAPP1hdr(.{1,4})/is) { | 
| 5490 | 60 |  |  |  |  | 220 | $dirName = 'IFD0'; | 
| 5491 | 60 |  |  |  |  | 353 | my ($junk, $bytes) = ($1, $2); | 
| 5492 |  |  |  |  |  |  | # support multi-segment EXIF | 
| 5493 | 60 | 0 | 66 |  |  | 369 | if (@dirOrder and $dirOrder[-1] =~ /^(IFD0|ExtendedEXIF)$/ and | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 5494 |  |  |  |  |  |  | not length $junk and $bytes !~ /^(MM\0\x2a|II\x2a\0)/) | 
| 5495 |  |  |  |  |  |  | { | 
| 5496 | 0 |  |  |  |  | 0 | $dirName = 'ExtendedEXIF'; | 
| 5497 |  |  |  |  |  |  | } | 
| 5498 |  |  |  |  |  |  | } | 
| 5499 | 84 | 100 |  |  |  | 880 | $s =~ /^$xmpAPP1hdr/    and $dirName = 'XMP'; | 
| 5500 | 84 | 100 |  |  |  | 723 | $s =~ /^$xmpExtAPP1hdr/ and $dirName = 'XMP'; | 
| 5501 |  |  |  |  |  |  | } elsif ($marker == 0xe2) { | 
| 5502 | 55 | 100 |  |  |  | 246 | $s =~ /^ICC_PROFILE\0/  and $dirName = 'ICC_Profile'; | 
| 5503 | 55 | 100 |  |  |  | 246 | $s =~ /^FPXR\0/         and $dirName = 'FlashPix'; | 
| 5504 | 55 | 100 |  |  |  | 190 | $s =~ /^MPF\0/          and $dirName = 'MPF'; | 
| 5505 |  |  |  |  |  |  | } elsif ($marker == 0xe3) { | 
| 5506 | 9 | 50 |  |  |  | 100 | $s =~ /^(Meta|META|Exif)\0\0/ and $dirName = 'Meta'; | 
| 5507 |  |  |  |  |  |  | } elsif ($marker == 0xe5) { | 
| 5508 | 9 | 50 |  |  |  | 112 | $s =~ /^RMETA\0/        and $dirName = 'RMETA'; | 
| 5509 |  |  |  |  |  |  | } elsif ($marker == 0xec) { | 
| 5510 | 19 | 100 |  |  |  | 152 | $s =~ /^Ducky/          and $dirName = 'Ducky'; | 
| 5511 |  |  |  |  |  |  | } elsif ($marker == 0xed) { | 
| 5512 | 29 | 100 |  |  |  | 354 | $s =~ /^$psAPP13hdr/    and $dirName = 'Photoshop'; | 
| 5513 |  |  |  |  |  |  | } elsif ($marker == 0xee) { | 
| 5514 | 16 | 50 |  |  |  | 176 | $s =~ /^Adobe/          and $dirName = 'Adobe'; | 
| 5515 |  |  |  |  |  |  | } | 
| 5516 |  |  |  |  |  |  | # initialize doneDir as a flag that the directory exists | 
| 5517 |  |  |  |  |  |  | # (unless we are deleting it anyway) | 
| 5518 | 347 | 100 | 100 |  |  | 1900 | $doneDir{$dirName} = 0 if defined $dirName and not $$delGroup{$dirName}; | 
| 5519 |  |  |  |  |  |  | } | 
| 5520 | 574 | 50 |  |  |  | 1712 | $raf->Seek($len, 1) or last; | 
| 5521 |  |  |  |  |  |  | } | 
| 5522 | 682 | 100 |  |  |  | 3070 | $dirName or $dirName = JpegMarkerName($marker); | 
| 5523 | 682 |  | 100 |  |  | 3332 | $dirCount{$dirName} = ($dirCount{$dirName} || 0) + 1; | 
| 5524 | 682 |  |  |  |  | 1738 | push @dirOrder, $dirName; | 
| 5525 |  |  |  |  |  |  | } | 
| 5526 | 110 | 100 | 100 |  |  | 783 | unless ($marker and $marker == 0xda) { | 
| 5527 | 2 | 50 |  |  |  | 8 | $isEXV or $self->Error('Corrupted JPEG image'), return 1; | 
| 5528 | 2 | 50 | 66 |  |  | 20 | $marker and $marker != 0xd9 and $self->Error('Corrupted EXV file'), return 1; | 
| 5529 |  |  |  |  |  |  | } | 
| 5530 | 110 | 50 |  |  |  | 460 | $raf->Seek($pos, 0) or $self->Error('Seek error'), return 1; | 
| 5531 |  |  |  |  |  |  | # | 
| 5532 |  |  |  |  |  |  | # re-write the image | 
| 5533 |  |  |  |  |  |  | # | 
| 5534 | 110 |  |  |  |  | 788 | my ($combinedSegData, $segPos, $firstSegPos, %extendedXMP); | 
| 5535 | 110 |  |  |  |  | 0 | my (@iccChunk, $iccChunkCount, $iccChunksTotal); | 
| 5536 |  |  |  |  |  |  | # read through each segment in the JPEG file | 
| 5537 | 110 |  |  |  |  | 270 | Marker: for (;;) { | 
| 5538 |  |  |  |  |  |  |  | 
| 5539 |  |  |  |  |  |  | # read up to next marker (JPEG markers begin with 0xff) | 
| 5540 | 792 |  |  |  |  | 1308 | my $segJunk; | 
| 5541 | 792 | 100 |  |  |  | 2853 | $raf->ReadLine($segJunk) or $segJunk = ''; | 
| 5542 |  |  |  |  |  |  | # remove the 0xff but write the rest of the junk up to this point | 
| 5543 |  |  |  |  |  |  | # (this will handle the data after the first 7 bytes of SOF segments) | 
| 5544 | 792 |  |  |  |  | 1846 | chomp($segJunk); | 
| 5545 | 792 | 100 |  |  |  | 2219 | Write($outfile, $segJunk) if length $segJunk; | 
| 5546 |  |  |  |  |  |  | # JPEG markers can be padded with unlimited 0xff's | 
| 5547 | 792 |  |  |  |  | 1332 | for (;;) { | 
| 5548 | 792 | 100 |  |  |  | 2347 | if ($raf->Read($ch, 1)) { | 
|  |  | 50 |  |  |  |  |  | 
| 5549 | 791 |  |  |  |  | 1605 | $marker = ord($ch); | 
| 5550 | 791 | 50 |  |  |  | 2180 | last unless $marker == 0xff; | 
| 5551 |  |  |  |  |  |  | } elsif ($creatingEXV) { | 
| 5552 |  |  |  |  |  |  | # create EXV from scratch | 
| 5553 | 1 |  |  |  |  | 5 | $marker = 0xd9; # EOI | 
| 5554 | 1 |  |  |  |  | 6 | push @dirOrder, 'EOI'; | 
| 5555 | 1 |  |  |  |  | 4 | $dirCount{EOI} = 1; | 
| 5556 | 1 |  |  |  |  | 4 | last; | 
| 5557 |  |  |  |  |  |  | } else { | 
| 5558 | 0 |  |  |  |  | 0 | $self->Error('Format error'); | 
| 5559 | 0 |  |  |  |  | 0 | return 1; | 
| 5560 |  |  |  |  |  |  | } | 
| 5561 |  |  |  |  |  |  | } | 
| 5562 |  |  |  |  |  |  | # read the segment data | 
| 5563 | 792 |  |  |  |  | 1645 | my $segData; | 
| 5564 |  |  |  |  |  |  | # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc) | 
| 5565 | 792 | 100 | 66 |  |  | 8157 | if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 5566 | 108 | 50 |  |  |  | 542 | last unless $raf->Read($segData, 7) == 7; | 
| 5567 |  |  |  |  |  |  | # read data for all markers except stand-alone | 
| 5568 |  |  |  |  |  |  | # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, EOI, RST0-RST7) | 
| 5569 |  |  |  |  |  |  | } elsif ($marker!=0x00 and $marker!=0x01 and $marker!=0xd9 and | 
| 5570 |  |  |  |  |  |  | ($marker<0xd0 or $marker>0xd7)) | 
| 5571 |  |  |  |  |  |  | { | 
| 5572 |  |  |  |  |  |  | # read record length word | 
| 5573 | 682 | 50 |  |  |  | 1833 | last unless $raf->Read($s, 2) == 2; | 
| 5574 | 682 |  |  |  |  | 2070 | my $len = unpack('n',$s);   # get data length | 
| 5575 | 682 | 50 | 33 |  |  | 3139 | last unless defined($len) and $len >= 2; | 
| 5576 | 682 |  |  |  |  | 1948 | $segPos = $raf->Tell(); | 
| 5577 | 682 |  |  |  |  | 1291 | $len -= 2;  # subtract size of length word | 
| 5578 | 682 | 50 |  |  |  | 1728 | last unless $raf->Read($segData, $len) == $len; | 
| 5579 |  |  |  |  |  |  | } | 
| 5580 |  |  |  |  |  |  | # initialize variables for this segment | 
| 5581 | 792 |  |  |  |  | 2480 | my $hdr = "\xff" . chr($marker);    # segment header | 
| 5582 | 792 |  |  |  |  | 2411 | my $markerName = JpegMarkerName($marker); | 
| 5583 | 792 |  |  |  |  | 1963 | my $dirName = shift @dirOrder;      # get directory name | 
| 5584 |  |  |  |  |  |  | # | 
| 5585 |  |  |  |  |  |  | # create all segments that must come before this one | 
| 5586 |  |  |  |  |  |  | # (nothing comes before SOI or after SOS) | 
| 5587 |  |  |  |  |  |  | # | 
| 5588 | 792 |  |  |  |  | 2388 | while ($markerName ne 'SOI') { | 
| 5589 | 792 | 100 | 100 |  |  | 2634 | if (exists $$addDirs{JFIF} and not defined $doneDir{JFIF}) { | 
| 5590 | 1 |  |  |  |  | 4 | $doneDir{JFIF} = 1; | 
| 5591 | 1 | 50 |  |  |  | 5 | if (defined $doneDir{Adobe}) { | 
| 5592 |  |  |  |  |  |  | # JFIF overrides Adobe APP14 colour components, so don't allow this | 
| 5593 |  |  |  |  |  |  | # (ref https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html) | 
| 5594 | 1 |  |  |  |  | 6 | $self->Warn('Not creating JFIF in JPEG with Adobe APP14'); | 
| 5595 |  |  |  |  |  |  | } else { | 
| 5596 | 0 | 0 |  |  |  | 0 | if ($verbose) { | 
| 5597 | 0 |  |  |  |  | 0 | print $out "Creating APP0:\n"; | 
| 5598 | 0 |  |  |  |  | 0 | print $out "  Creating JFIF with default values\n"; | 
| 5599 |  |  |  |  |  |  | } | 
| 5600 | 0 |  |  |  |  | 0 | my $jfif = "\x01\x02\x01\0\x48\0\x48\0\0"; | 
| 5601 | 0 |  |  |  |  | 0 | SetByteOrder('MM'); | 
| 5602 | 0 |  |  |  |  | 0 | my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main'); | 
| 5603 | 0 |  |  |  |  | 0 | my %dirInfo = ( | 
| 5604 |  |  |  |  |  |  | DataPt   => \$jfif, | 
| 5605 |  |  |  |  |  |  | DirStart => 0, | 
| 5606 |  |  |  |  |  |  | DirLen   => length $jfif, | 
| 5607 |  |  |  |  |  |  | Parent   => 'JFIF', | 
| 5608 |  |  |  |  |  |  | ); | 
| 5609 |  |  |  |  |  |  | # must temporarily remove JFIF from DEL_GROUP so we can | 
| 5610 |  |  |  |  |  |  | # delete JFIF and add it back again in a single step | 
| 5611 | 0 |  |  |  |  | 0 | my $delJFIF = $$delGroup{JFIF}; | 
| 5612 | 0 |  |  |  |  | 0 | delete $$delGroup{JFIF}; | 
| 5613 | 0 |  |  |  |  | 0 | $$path[$pn] = 'JFIF'; | 
| 5614 | 0 |  |  |  |  | 0 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 5615 | 0 | 0 |  |  |  | 0 | $$delGroup{JFIF} = $delJFIF if defined $delJFIF; | 
| 5616 | 0 | 0 | 0 |  |  | 0 | if (defined $newData and length $newData) { | 
| 5617 | 0 |  |  |  |  | 0 | my $app0hdr = "\xff\xe0" . pack('n', length($newData) + 7); | 
| 5618 | 0 | 0 |  |  |  | 0 | Write($outfile,$app0hdr,"JFIF\0",$newData) or $err = 1; | 
| 5619 |  |  |  |  |  |  | } | 
| 5620 |  |  |  |  |  |  | } | 
| 5621 |  |  |  |  |  |  | } | 
| 5622 |  |  |  |  |  |  | # don't create anything before APP0 or APP1 EXIF (containing IFD0) | 
| 5623 | 792 | 100 | 100 |  |  | 4848 | last if $markerName eq 'APP0' or $dirCount{IFD0} or $dirCount{ExtendedEXIF}; | 
|  |  |  | 66 |  |  |  |  | 
| 5624 |  |  |  |  |  |  | # EXIF information must come immediately after APP0 | 
| 5625 | 687 | 100 | 100 |  |  | 2683 | if (exists $$addDirs{IFD0} and not defined $doneDir{IFD0}) { | 
| 5626 | 31 |  |  |  |  | 101 | $doneDir{IFD0} = 1; | 
| 5627 | 31 | 100 |  |  |  | 132 | $verbose and print $out "Creating APP1:\n"; | 
| 5628 |  |  |  |  |  |  | # write new EXIF data | 
| 5629 | 31 |  |  |  |  | 130 | $$self{TIFF_TYPE} = 'APP1'; | 
| 5630 | 31 |  |  |  |  | 147 | my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main'); | 
| 5631 | 31 |  |  |  |  | 210 | my %dirInfo = ( | 
| 5632 |  |  |  |  |  |  | DirName => 'IFD0', | 
| 5633 |  |  |  |  |  |  | Parent  => 'APP1', | 
| 5634 |  |  |  |  |  |  | ); | 
| 5635 | 31 |  |  |  |  | 111 | $$path[$pn] = 'APP1'; | 
| 5636 | 31 |  |  |  |  | 242 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF); | 
| 5637 | 31 | 100 | 66 |  |  | 334 | if (defined $buff and length $buff) { | 
| 5638 | 29 | 50 |  |  |  | 182 | if (length($buff) + length($exifAPP1hdr) > $maxSegmentLen) { | 
| 5639 | 0 | 0 |  |  |  | 0 | if ($self->Options('NoMultiExif')) { | 
| 5640 | 0 |  |  |  |  | 0 | $self->Error('EXIF is too large for JPEG segment'); | 
| 5641 |  |  |  |  |  |  | } else { | 
| 5642 | 0 |  |  |  |  | 0 | $self->Warn('Creating multi-segment EXIF',1); | 
| 5643 |  |  |  |  |  |  | } | 
| 5644 |  |  |  |  |  |  | } | 
| 5645 |  |  |  |  |  |  | # switch to buffered output if required | 
| 5646 | 29 | 50 | 33 |  |  | 315 | if (($$self{PREVIEW_INFO} or $$self{LeicaTrailer}) and not $oldOutfile) { | 
|  |  |  | 33 |  |  |  |  | 
| 5647 | 0 |  |  |  |  | 0 | $writeBuffer = ''; | 
| 5648 | 0 |  |  |  |  | 0 | $oldOutfile = $outfile; | 
| 5649 | 0 |  |  |  |  | 0 | $outfile = \$writeBuffer; | 
| 5650 |  |  |  |  |  |  | # account for segment, EXIF and TIFF headers | 
| 5651 | 0 | 0 |  |  |  | 0 | $$self{PREVIEW_INFO}{Fixup}{Start} += 18 if $$self{PREVIEW_INFO}; | 
| 5652 | 0 | 0 |  |  |  | 0 | $$self{LeicaTrailer}{Fixup}{Start} += 18 if $$self{LeicaTrailer}; | 
| 5653 |  |  |  |  |  |  | } | 
| 5654 |  |  |  |  |  |  | # write as multi-segment | 
| 5655 | 29 |  |  |  |  | 163 | my $n = WriteMultiSegment($outfile, 0xe1, $exifAPP1hdr, \$buff, 'EXIF'); | 
| 5656 | 29 | 50 | 33 |  |  | 247 | if (not $n) { | 
|  |  | 50 |  |  |  |  |  | 
| 5657 | 0 |  |  |  |  | 0 | $err = 1; | 
| 5658 |  |  |  |  |  |  | } elsif ($n > 1 and $oldOutfile) { | 
| 5659 |  |  |  |  |  |  | # (punt on this because updating the pointers would be a real pain) | 
| 5660 | 0 |  |  |  |  | 0 | $self->Error("Can't write multi-segment EXIF with external pointers"); | 
| 5661 |  |  |  |  |  |  | } | 
| 5662 | 29 |  |  |  |  | 168 | ++$$self{CHANGED}; | 
| 5663 |  |  |  |  |  |  | } | 
| 5664 |  |  |  |  |  |  | } | 
| 5665 |  |  |  |  |  |  | # APP13 Photoshop segment next | 
| 5666 | 687 | 100 |  |  |  | 1747 | last if $dirCount{Photoshop}; | 
| 5667 | 505 | 100 | 100 |  |  | 1689 | if (exists $$addDirs{Photoshop} and not defined $doneDir{Photoshop}) { | 
| 5668 | 19 |  |  |  |  | 77 | $doneDir{Photoshop} = 1; | 
| 5669 | 19 | 50 |  |  |  | 77 | $verbose and print $out "Creating APP13:\n"; | 
| 5670 |  |  |  |  |  |  | # write new APP13 Photoshop record to memory | 
| 5671 | 19 |  |  |  |  | 98 | my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main'); | 
| 5672 | 19 |  |  |  |  | 112 | my %dirInfo = ( | 
| 5673 |  |  |  |  |  |  | Parent => 'APP13', | 
| 5674 |  |  |  |  |  |  | ); | 
| 5675 | 19 |  |  |  |  | 80 | $$path[$pn] = 'APP13'; | 
| 5676 | 19 |  |  |  |  | 117 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 5677 | 19 | 50 | 33 |  |  | 163 | if (defined $buff and length $buff) { | 
| 5678 | 19 | 50 |  |  |  | 193 | WriteMultiSegment($outfile, 0xed, $psAPP13hdr, \$buff) or $err = 1; | 
| 5679 | 19 |  |  |  |  | 112 | ++$$self{CHANGED}; | 
| 5680 |  |  |  |  |  |  | } | 
| 5681 |  |  |  |  |  |  | } | 
| 5682 |  |  |  |  |  |  | # then APP1 XMP segment | 
| 5683 | 505 | 100 |  |  |  | 1342 | last if $dirCount{XMP}; | 
| 5684 | 490 | 100 | 100 |  |  | 1684 | if (exists $$addDirs{XMP} and not defined $doneDir{XMP}) { | 
| 5685 | 26 |  |  |  |  | 95 | $doneDir{XMP} = 1; | 
| 5686 | 26 | 50 |  |  |  | 155 | $verbose and print $out "Creating APP1:\n"; | 
| 5687 |  |  |  |  |  |  | # write new XMP data | 
| 5688 | 26 |  |  |  |  | 149 | my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main'); | 
| 5689 | 26 |  |  |  |  | 222 | my %dirInfo = ( | 
| 5690 |  |  |  |  |  |  | Parent      => 'APP1', | 
| 5691 |  |  |  |  |  |  | # specify MaxDataLen so XMP is split if required | 
| 5692 |  |  |  |  |  |  | MaxDataLen  => $maxXMPLen - length($xmpAPP1hdr), | 
| 5693 |  |  |  |  |  |  | ); | 
| 5694 | 26 |  |  |  |  | 104 | $$path[$pn] = 'APP1'; | 
| 5695 | 26 |  |  |  |  | 154 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 5696 | 26 | 50 | 33 |  |  | 273 | if (defined $buff and length $buff) { | 
| 5697 |  |  |  |  |  |  | WriteMultiXMP($self, $outfile, \$buff, $dirInfo{ExtendedXMP}, | 
| 5698 | 26 | 50 |  |  |  | 191 | $dirInfo{ExtendedGUID}) or $err = 1; | 
| 5699 |  |  |  |  |  |  | } | 
| 5700 |  |  |  |  |  |  | } | 
| 5701 |  |  |  |  |  |  | # then APP2 ICC_Profile segment | 
| 5702 | 490 | 100 |  |  |  | 1396 | last if $dirCount{ICC_Profile}; | 
| 5703 | 485 | 100 | 100 |  |  | 1433 | if (exists $$addDirs{ICC_Profile} and not defined $doneDir{ICC_Profile}) { | 
| 5704 | 3 |  |  |  |  | 12 | $doneDir{ICC_Profile} = 1; | 
| 5705 | 3 | 50 | 66 |  |  | 28 | next if $$delGroup{ICC_Profile} and $$delGroup{ICC_Profile} != 2; | 
| 5706 | 3 | 50 |  |  |  | 16 | $verbose and print $out "Creating APP2:\n"; | 
| 5707 |  |  |  |  |  |  | # write new ICC_Profile data | 
| 5708 | 3 |  |  |  |  | 16 | my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main'); | 
| 5709 | 3 |  |  |  |  | 19 | my %dirInfo = ( | 
| 5710 |  |  |  |  |  |  | Parent   => 'APP2', | 
| 5711 |  |  |  |  |  |  | ); | 
| 5712 | 3 |  |  |  |  | 13 | $$path[$pn] = 'APP2'; | 
| 5713 | 3 |  |  |  |  | 18 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 5714 | 3 | 50 | 33 |  |  | 31 | if (defined $buff and length $buff) { | 
| 5715 | 3 | 50 |  |  |  | 19 | WriteMultiSegment($outfile, 0xe2, "ICC_PROFILE\0", \$buff, 'ICC') or $err = 1; | 
| 5716 | 3 |  |  |  |  | 15 | ++$$self{CHANGED}; | 
| 5717 |  |  |  |  |  |  | } | 
| 5718 |  |  |  |  |  |  | } | 
| 5719 |  |  |  |  |  |  | # then APP12 Ducky segment | 
| 5720 | 485 | 100 |  |  |  | 1146 | last if $dirCount{Ducky}; | 
| 5721 | 484 | 100 | 100 |  |  | 1417 | if (exists $$addDirs{Ducky} and not defined $doneDir{Ducky}) { | 
| 5722 | 2 |  |  |  |  | 6 | $doneDir{Ducky} = 1; | 
| 5723 | 2 | 50 |  |  |  | 10 | $verbose and print $out "Creating APP12 Ducky:\n"; | 
| 5724 |  |  |  |  |  |  | # write new Ducky segment data | 
| 5725 | 2 |  |  |  |  | 12 | my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky'); | 
| 5726 | 2 |  |  |  |  | 11 | my %dirInfo = ( | 
| 5727 |  |  |  |  |  |  | Parent   => 'APP12', | 
| 5728 |  |  |  |  |  |  | ); | 
| 5729 | 2 |  |  |  |  | 8 | $$path[$pn] = 'APP12'; | 
| 5730 | 2 |  |  |  |  | 10 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 5731 | 2 | 50 | 33 |  |  | 23 | if (defined $buff and length $buff) { | 
| 5732 | 2 |  |  |  |  | 6 | my $size = length($buff) + 5; | 
| 5733 | 2 | 50 |  |  |  | 8 | if ($size <= $maxSegmentLen) { | 
| 5734 |  |  |  |  |  |  | # write the new segment with appropriate header | 
| 5735 | 2 |  |  |  |  | 10 | my $app12hdr = "\xff\xec" . pack('n', $size + 2); | 
| 5736 | 2 | 50 |  |  |  | 10 | Write($outfile, $app12hdr, 'Ducky', $buff) or $err = 1; | 
| 5737 |  |  |  |  |  |  | } else { | 
| 5738 | 0 |  |  |  |  | 0 | $self->Warn("APP12 Ducky segment too large! ($size bytes)"); | 
| 5739 |  |  |  |  |  |  | } | 
| 5740 |  |  |  |  |  |  | } | 
| 5741 |  |  |  |  |  |  | } | 
| 5742 |  |  |  |  |  |  | # then APP14 Adobe segment | 
| 5743 | 484 | 100 |  |  |  | 1186 | last if $dirCount{Adobe}; | 
| 5744 | 459 | 50 | 33 |  |  | 1330 | if (exists $$addDirs{Adobe} and not defined $doneDir{Adobe}) { | 
| 5745 | 0 |  |  |  |  | 0 | $doneDir{Adobe} = 1; | 
| 5746 | 0 |  |  |  |  | 0 | my $buff = $self->GetNewValue('Adobe'); | 
| 5747 | 0 | 0 |  |  |  | 0 | if ($buff) { | 
| 5748 | 0 | 0 |  |  |  | 0 | $verbose and print $out "Creating APP14:\n  Creating Adobe segment\n"; | 
| 5749 | 0 |  |  |  |  | 0 | my $size = length($buff); | 
| 5750 | 0 | 0 |  |  |  | 0 | if ($size <= $maxSegmentLen) { | 
| 5751 |  |  |  |  |  |  | # write the new segment with appropriate header | 
| 5752 | 0 |  |  |  |  | 0 | my $app14hdr = "\xff\xee" . pack('n', $size + 2); | 
| 5753 | 0 | 0 |  |  |  | 0 | Write($outfile, $app14hdr, $buff) or $err = 1; | 
| 5754 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 5755 |  |  |  |  |  |  | } else { | 
| 5756 | 0 |  |  |  |  | 0 | $self->Warn("APP14 Adobe segment too large! ($size bytes)"); | 
| 5757 |  |  |  |  |  |  | } | 
| 5758 |  |  |  |  |  |  | } | 
| 5759 |  |  |  |  |  |  | } | 
| 5760 |  |  |  |  |  |  | # finally, COM segment | 
| 5761 | 459 | 100 |  |  |  | 1087 | last if $dirCount{COM}; | 
| 5762 | 439 | 100 | 100 |  |  | 1330 | if (exists $$addDirs{COM} and not defined $doneDir{COM}) { | 
| 5763 | 5 |  |  |  |  | 13 | $doneDir{COM} = 1; | 
| 5764 | 5 | 50 | 33 |  |  | 21 | next if $$delGroup{File} and $$delGroup{File} != 2; | 
| 5765 | 5 |  |  |  |  | 19 | my $newComment = $self->GetNewValue('Comment'); | 
| 5766 | 5 | 50 |  |  |  | 31 | if (defined $newComment) { | 
| 5767 | 5 | 50 |  |  |  | 19 | if ($verbose) { | 
| 5768 | 0 |  |  |  |  | 0 | print $out "Creating COM:\n"; | 
| 5769 | 0 |  |  |  |  | 0 | $self->VerboseValue('+ Comment', $newComment); | 
| 5770 |  |  |  |  |  |  | } | 
| 5771 | 5 | 50 |  |  |  | 25 | WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1; | 
| 5772 | 5 |  |  |  |  | 17 | ++$$self{CHANGED}; | 
| 5773 |  |  |  |  |  |  | } | 
| 5774 |  |  |  |  |  |  | } | 
| 5775 | 439 |  |  |  |  | 759 | last;   # didn't want to loop anyway | 
| 5776 |  |  |  |  |  |  | } | 
| 5777 | 792 |  |  |  |  | 1753 | $$path[$pn] = $markerName; | 
| 5778 |  |  |  |  |  |  | # decrement counter for this directory since we are about to process it | 
| 5779 | 792 |  |  |  |  | 1895 | --$dirCount{$dirName}; | 
| 5780 |  |  |  |  |  |  | # | 
| 5781 |  |  |  |  |  |  | # rewrite existing segments | 
| 5782 |  |  |  |  |  |  | # | 
| 5783 |  |  |  |  |  |  | # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc) | 
| 5784 | 792 | 100 | 66 |  |  | 7589 | if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 5785 | 108 | 100 |  |  |  | 505 | $verbose and print $out "JPEG $markerName:\n"; | 
| 5786 | 108 | 50 |  |  |  | 394 | Write($outfile, $hdr, $segData) or $err = 1; | 
| 5787 | 108 |  |  |  |  | 366 | next; | 
| 5788 |  |  |  |  |  |  | } elsif ($marker == 0xda) {             # SOS | 
| 5789 | 108 |  |  |  |  | 440 | pop @$path; | 
| 5790 | 108 | 100 |  |  |  | 442 | $verbose and print $out "JPEG SOS\n"; | 
| 5791 |  |  |  |  |  |  | # write SOS segment | 
| 5792 | 108 |  |  |  |  | 524 | $s = pack('n', length($segData) + 2); | 
| 5793 | 108 | 50 |  |  |  | 372 | Write($outfile, $hdr, $s, $segData) or $err = 1; | 
| 5794 | 108 |  |  |  |  | 412 | my ($buff, $endPos, $trailInfo); | 
| 5795 | 108 |  |  |  |  | 348 | my $delPreview = $$self{DEL_PREVIEW}; | 
| 5796 | 108 | 100 |  |  |  | 769 | $trailInfo = IdentifyTrailer($raf) unless $$delGroup{Trailer}; | 
| 5797 | 108 |  |  |  |  | 1054 | my $nvTrail = $self->GetNewValueHash($Image::ExifTool::Extra{Trailer}); | 
| 5798 | 108 | 100 | 66 |  |  | 1544 | unless ($oldOutfile or $delPreview or $trailInfo or $$delGroup{Trailer} or $nvTrail) { | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 5799 |  |  |  |  |  |  | # blindly copy the rest of the file | 
| 5800 | 92 |  |  |  |  | 445 | while ($raf->Read($buff, 65536)) { | 
| 5801 | 92 | 50 |  |  |  | 551 | Write($outfile, $buff) or $err = 1, last; | 
| 5802 |  |  |  |  |  |  | } | 
| 5803 | 92 |  |  |  |  | 311 | $rtnVal = 1;  # success unless we have a file write error | 
| 5804 | 92 |  |  |  |  | 276 | last;         # all done | 
| 5805 |  |  |  |  |  |  | } | 
| 5806 |  |  |  |  |  |  | # write the rest of the image (as quickly as possible) up to the EOI | 
| 5807 | 16 |  |  |  |  | 48 | my $endedWithFF; | 
| 5808 | 16 |  |  |  |  | 41 | for (;;) { | 
| 5809 | 16 | 50 |  |  |  | 75 | my $n = $raf->Read($buff, 65536) or last Marker; | 
| 5810 | 16 | 50 | 33 |  |  | 252 | if (($endedWithFF and $buff =~ m/^\xd9/sg) or | 
|  |  |  | 33 |  |  |  |  | 
| 5811 |  |  |  |  |  |  | $buff =~ m/\xff\xd9/sg) | 
| 5812 |  |  |  |  |  |  | { | 
| 5813 | 16 |  |  |  |  | 56 | $rtnVal = 1; # the JPEG is OK | 
| 5814 |  |  |  |  |  |  | # write up to the EOI | 
| 5815 | 16 |  |  |  |  | 49 | my $pos = pos($buff); | 
| 5816 | 16 | 50 |  |  |  | 78 | Write($outfile, substr($buff, 0, $pos)) or $err = 1; | 
| 5817 | 16 |  |  |  |  | 120 | $buff = substr($buff, $pos); | 
| 5818 | 16 |  |  |  |  | 47 | last; | 
| 5819 |  |  |  |  |  |  | } | 
| 5820 | 0 | 0 |  |  |  | 0 | unless ($n == 65536) { | 
| 5821 | 0 |  |  |  |  | 0 | $self->Error('JPEG EOI marker not found'); | 
| 5822 | 0 |  |  |  |  | 0 | last Marker; | 
| 5823 |  |  |  |  |  |  | } | 
| 5824 | 0 | 0 |  |  |  | 0 | Write($outfile, $buff) or $err = 1; | 
| 5825 | 0 | 0 |  |  |  | 0 | $endedWithFF = substr($buff, 65535, 1) eq "\xff" ? 1 : 0; | 
| 5826 |  |  |  |  |  |  | } | 
| 5827 |  |  |  |  |  |  | # remember position of last data copied | 
| 5828 | 16 |  |  |  |  | 116 | $endPos = $raf->Tell() - length($buff); | 
| 5829 |  |  |  |  |  |  | # write new trailer if specified | 
| 5830 | 16 | 50 |  |  |  | 83 | if ($nvTrail) { | 
| 5831 |  |  |  |  |  |  | # access new value directly to avoid copying a potentially very large data block | 
| 5832 | 0 | 0 | 0 |  |  | 0 | if ($$nvTrail{Value} and $$nvTrail{Value}[0]) { # (note: "0" will also delete the trailer) | 
|  |  | 0 | 0 |  |  |  |  | 
| 5833 | 0 |  |  |  |  | 0 | $self->VPrint(0, '  Writing new trailer'); | 
| 5834 | 0 | 0 |  |  |  | 0 | Write($outfile, $$nvTrail{Value}[0]) or $err = 1; | 
| 5835 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 5836 |  |  |  |  |  |  | } elsif ($raf->Seek(0, 2) and $raf->Tell() != $endPos) { | 
| 5837 | 0 |  |  |  |  | 0 | $self->VPrint(0, '  Deleting trailer (', $raf->Tell() - $endPos, ' bytes)'); | 
| 5838 | 0 |  |  |  |  | 0 | ++$$self{CHANGED};  # changed if there was previously a trailer | 
| 5839 |  |  |  |  |  |  | } | 
| 5840 | 0 |  |  |  |  | 0 | last;   # all done | 
| 5841 |  |  |  |  |  |  | } | 
| 5842 |  |  |  |  |  |  | # rewrite existing trailers | 
| 5843 | 16 | 100 |  |  |  | 84 | if ($trailInfo) { | 
| 5844 | 11 |  |  |  |  | 35 | my $tbuf = ''; | 
| 5845 | 11 |  |  |  |  | 54 | $raf->Seek(-length($buff), 1);  # seek back to just after EOI | 
| 5846 | 11 |  |  |  |  | 96 | $$trailInfo{OutFile} = \$tbuf;  # rewrite the trailer | 
| 5847 | 11 |  |  |  |  | 44 | $$trailInfo{ScanForAFCP} = 1;   # scan if necessary | 
| 5848 | 11 | 50 |  |  |  | 67 | $self->ProcessTrailers($trailInfo) or undef $trailInfo; | 
| 5849 |  |  |  |  |  |  | } | 
| 5850 | 16 | 100 |  |  |  | 83 | if (not $oldOutfile) { | 
|  |  | 50 |  |  |  |  |  | 
| 5851 |  |  |  |  |  |  | # do nothing special | 
| 5852 |  |  |  |  |  |  | } elsif ($$self{LeicaTrailer}) { | 
| 5853 | 0 |  |  |  |  | 0 | my $trailLen; | 
| 5854 | 0 | 0 |  |  |  | 0 | if ($trailInfo) { | 
| 5855 | 0 |  |  |  |  | 0 | $trailLen = $$trailInfo{DataPos} - $endPos; | 
| 5856 |  |  |  |  |  |  | } else { | 
| 5857 | 0 | 0 |  |  |  | 0 | $raf->Seek(0, 2) or $err = 1; | 
| 5858 | 0 |  |  |  |  | 0 | $trailLen = $raf->Tell() - $endPos; | 
| 5859 |  |  |  |  |  |  | } | 
| 5860 | 0 |  |  |  |  | 0 | my $fixup = $$self{LeicaTrailer}{Fixup}; | 
| 5861 | 0 |  |  |  |  | 0 | $$self{LeicaTrailer}{TrailPos} = $endPos; | 
| 5862 | 0 |  |  |  |  | 0 | $$self{LeicaTrailer}{TrailLen} = $trailLen; | 
| 5863 |  |  |  |  |  |  | # get _absolute_ position of new Leica trailer | 
| 5864 | 0 |  |  |  |  | 0 | my $absPos = Tell($oldOutfile) + length($$outfile); | 
| 5865 | 0 |  |  |  |  | 0 | require Image::ExifTool::Panasonic; | 
| 5866 | 0 |  |  |  |  | 0 | my $dat = Image::ExifTool::Panasonic::ProcessLeicaTrailer($self, $absPos); | 
| 5867 |  |  |  |  |  |  | # allow some junk before Leica trailer (just in case) | 
| 5868 | 0 |  |  |  |  | 0 | my $junk = $$self{LeicaTrailerPos} - $endPos; | 
| 5869 |  |  |  |  |  |  | # set MakerNote pointer and size (subtract 10 for segment and EXIF headers) | 
| 5870 | 0 |  |  |  |  | 0 | $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', length($$outfile) - 10 + $junk); | 
| 5871 |  |  |  |  |  |  | # use this fixup to set the size too (sneaky) | 
| 5872 | 0 | 0 |  |  |  | 0 | my $trailSize = defined($dat) ? length($dat) - $junk : $$self{LeicaTrailer}{Size}; | 
| 5873 | 0 |  |  |  |  | 0 | $$fixup{Start} -= 4;  $$fixup{Shift} += 4; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5874 | 0 | 0 |  |  |  | 0 | $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', $trailSize) if defined $trailSize; | 
| 5875 | 0 |  |  |  |  | 0 | $$fixup{Start} += 4;  $$fixup{Shift} -= 4; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5876 |  |  |  |  |  |  | # clean up and write the buffered data | 
| 5877 | 0 |  |  |  |  | 0 | $outfile = $oldOutfile; | 
| 5878 | 0 |  |  |  |  | 0 | undef $oldOutfile; | 
| 5879 | 0 | 0 |  |  |  | 0 | Write($outfile, $writeBuffer) or $err = 1; | 
| 5880 | 0 |  |  |  |  | 0 | undef $writeBuffer; | 
| 5881 | 0 | 0 |  |  |  | 0 | if (defined $dat) { | 
| 5882 | 0 | 0 |  |  |  | 0 | Write($outfile, $dat) or $err = 1;  # write new Leica trailer | 
| 5883 | 0 |  |  |  |  | 0 | $delPreview = 1;                    # delete existing Leica trailer | 
| 5884 |  |  |  |  |  |  | } | 
| 5885 |  |  |  |  |  |  | } else { | 
| 5886 |  |  |  |  |  |  | # locate preview image and fix up preview offsets | 
| 5887 | 1 | 50 |  |  |  | 6 | my $scanLen = $$self{Make} =~ /^SONY/i ? 65536 : 1024; | 
| 5888 | 1 | 50 |  |  |  | 6 | if (length($buff) < $scanLen) { # make sure we have enough trailer to scan | 
| 5889 | 1 |  |  |  |  | 3 | my $buf2; | 
| 5890 | 1 | 50 |  |  |  | 5 | $buff .= $buf2 if $raf->Read($buf2, $scanLen - length($buff)); | 
| 5891 |  |  |  |  |  |  | } | 
| 5892 |  |  |  |  |  |  | # get new preview image position, relative to EXIF base | 
| 5893 | 1 |  |  |  |  | 6 | my $newPos = length($$outfile) - 10; # (subtract 10 for segment and EXIF headers) | 
| 5894 | 1 |  |  |  |  | 3 | my $junkLen; | 
| 5895 |  |  |  |  |  |  | # adjust position if image isn't at the start (eg. Olympus E-1/E-300) | 
| 5896 | 1 | 50 |  |  |  | 5 | if ($buff =~ /(\xff\xd8\xff.|.\xd8\xff\xdb)(..)/sg) { | 
| 5897 | 0 |  |  |  |  | 0 | my ($jpegHdr, $segLen) = ($1, $2); | 
| 5898 | 0 |  |  |  |  | 0 | $junkLen = pos($buff) - 6; | 
| 5899 |  |  |  |  |  |  | # Sony previewimage trailer has a 32 byte header | 
| 5900 | 0 | 0 | 0 |  |  | 0 | if ($$self{Make} =~ /^SONY/i and $junkLen > 32) { | 
| 5901 |  |  |  |  |  |  | # with some newer Sony models, the makernotes preview pointer | 
| 5902 |  |  |  |  |  |  | # points to JPEG at end of EXIF inside MPImage preview (what a pain!) | 
| 5903 | 0 | 0 |  |  |  | 0 | if ($jpegHdr eq "\xff\xd8\xff\xe1") {   # is the first segment EXIF? | 
| 5904 | 0 |  |  |  |  | 0 | $segLen = unpack('n', $segLen);     # the EXIF segment length | 
| 5905 |  |  |  |  |  |  | # Sony PreviewImage starts with last 2 bytes of EXIF segment | 
| 5906 |  |  |  |  |  |  | # (and first byte is usually "\0", not "\xff", so don't check this) | 
| 5907 | 0 | 0 | 0 |  |  | 0 | if (length($buff) > $junkLen + $segLen + 6 and | 
| 5908 |  |  |  |  |  |  | substr($buff, $junkLen + $segLen + 3, 3) eq "\xd8\xff\xdb") | 
| 5909 |  |  |  |  |  |  | { | 
| 5910 | 0 |  |  |  |  | 0 | $junkLen += $segLen + 2; | 
| 5911 |  |  |  |  |  |  | # (note: this will not copy the trailer after PreviewImage, | 
| 5912 |  |  |  |  |  |  | #  which is a 14kB block full of zeros for the A77) | 
| 5913 |  |  |  |  |  |  | } | 
| 5914 |  |  |  |  |  |  | } | 
| 5915 | 0 |  |  |  |  | 0 | $junkLen -= 32; | 
| 5916 |  |  |  |  |  |  | } | 
| 5917 | 0 |  |  |  |  | 0 | $newPos += $junkLen; | 
| 5918 |  |  |  |  |  |  | } | 
| 5919 |  |  |  |  |  |  | # fix up the preview offsets to point to the start of the new image | 
| 5920 | 1 |  |  |  |  | 4 | my $previewInfo = $$self{PREVIEW_INFO}; | 
| 5921 | 1 |  |  |  |  | 4 | delete $$self{PREVIEW_INFO}; | 
| 5922 | 1 |  |  |  |  | 3 | my $fixup = $$previewInfo{Fixup}; | 
| 5923 | 1 |  | 50 |  |  | 7 | $newPos += ($$previewInfo{BaseShift} || 0); | 
| 5924 |  |  |  |  |  |  | # adjust to absolute file offset if necessary (Samsung STMN) | 
| 5925 | 1 | 50 |  |  |  | 5 | $newPos += Tell($oldOutfile) + 10 if $$previewInfo{Absolute}; | 
| 5926 | 1 | 50 |  |  |  | 4 | if ($$previewInfo{Relative}) { | 
|  |  | 0 |  |  |  |  |  | 
| 5927 |  |  |  |  |  |  | # adjust for our base by looking at how far the pointer got shifted | 
| 5928 | 1 |  | 50 |  |  | 6 | $newPos -= ($fixup->GetMarkerPointers($outfile, 'PreviewImage') || 0); | 
| 5929 |  |  |  |  |  |  | } elsif ($$previewInfo{ChangeBase}) { | 
| 5930 |  |  |  |  |  |  | # Leica S2 uses relative offsets for the preview only (leica sucks) | 
| 5931 | 0 |  |  |  |  | 0 | my $makerOffset = $fixup->GetMarkerPointers($outfile, 'LeicaTrailer'); | 
| 5932 | 0 | 0 |  |  |  | 0 | $newPos -= $makerOffset if $makerOffset; | 
| 5933 |  |  |  |  |  |  | } | 
| 5934 | 1 |  |  |  |  | 6 | $fixup->SetMarkerPointers($outfile, 'PreviewImage', $newPos); | 
| 5935 |  |  |  |  |  |  | # clean up and write the buffered data | 
| 5936 | 1 |  |  |  |  | 5 | $outfile = $oldOutfile; | 
| 5937 | 1 |  |  |  |  | 4 | undef $oldOutfile; | 
| 5938 | 1 | 50 |  |  |  | 5 | Write($outfile, $writeBuffer) or $err = 1; | 
| 5939 | 1 |  |  |  |  | 33 | undef $writeBuffer; | 
| 5940 |  |  |  |  |  |  | # write preview image | 
| 5941 | 1 | 50 |  |  |  | 18 | if ($$previewInfo{Data} ne 'LOAD_PREVIEW') { | 
| 5942 |  |  |  |  |  |  | # write any junk that existed before the preview image | 
| 5943 | 0 | 0 | 0 |  |  | 0 | Write($outfile, substr($buff,0,$junkLen)) or $err = 1 if $junkLen; | 
| 5944 |  |  |  |  |  |  | # write the saved preview image | 
| 5945 | 0 | 0 |  |  |  | 0 | Write($outfile, $$previewInfo{Data}) or $err = 1; | 
| 5946 | 0 |  |  |  |  | 0 | delete $$previewInfo{Data}; | 
| 5947 |  |  |  |  |  |  | # (don't increment CHANGED because we could be rewriting existing preview) | 
| 5948 | 0 |  |  |  |  | 0 | $delPreview = 1;    # remove old preview | 
| 5949 |  |  |  |  |  |  | } | 
| 5950 |  |  |  |  |  |  | } | 
| 5951 |  |  |  |  |  |  | # copy over preview image if necessary | 
| 5952 | 16 | 50 |  |  |  | 70 | unless ($delPreview) { | 
| 5953 | 16 |  |  |  |  | 41 | my $extra; | 
| 5954 | 16 | 100 |  |  |  | 68 | if ($trailInfo) { | 
| 5955 |  |  |  |  |  |  | # copy everything up to start of first processed trailer | 
| 5956 | 11 |  |  |  |  | 37 | $extra = $$trailInfo{DataPos} - $endPos; | 
| 5957 |  |  |  |  |  |  | } else { | 
| 5958 |  |  |  |  |  |  | # copy everything up to end of file | 
| 5959 | 5 | 50 |  |  |  | 38 | $raf->Seek(0, 2) or $err = 1; | 
| 5960 | 5 |  |  |  |  | 26 | $extra = $raf->Tell() - $endPos; | 
| 5961 |  |  |  |  |  |  | } | 
| 5962 | 16 | 100 |  |  |  | 69 | if ($extra > 0) { | 
| 5963 | 3 | 100 |  |  |  | 13 | if ($$delGroup{Trailer}) { | 
| 5964 | 2 | 50 |  |  |  | 8 | $verbose and print $out "  Deleting unknown trailer ($extra bytes)\n"; | 
| 5965 | 2 |  |  |  |  | 7 | ++$$self{CHANGED}; | 
| 5966 |  |  |  |  |  |  | } else { | 
| 5967 |  |  |  |  |  |  | # copy over unknown trailer | 
| 5968 | 1 | 50 |  |  |  | 3 | $verbose and print $out "  Preserving unknown trailer ($extra bytes)\n"; | 
| 5969 | 1 | 50 |  |  |  | 4 | $raf->Seek($endPos, 0) or $err = 1; | 
| 5970 | 1 | 50 |  |  |  | 7 | CopyBlock($raf, $outfile, $extra) or $err = 1; | 
| 5971 |  |  |  |  |  |  | } | 
| 5972 |  |  |  |  |  |  | } | 
| 5973 |  |  |  |  |  |  | } | 
| 5974 |  |  |  |  |  |  | # write trailer if necessary | 
| 5975 | 16 | 100 |  |  |  | 58 | if ($trailInfo) { | 
| 5976 | 11 | 50 |  |  |  | 68 | $self->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1; | 
| 5977 | 11 |  |  |  |  | 88 | undef $trailInfo; | 
| 5978 |  |  |  |  |  |  | } | 
| 5979 | 16 |  |  |  |  | 62 | last;   # all done parsing file | 
| 5980 |  |  |  |  |  |  |  | 
| 5981 |  |  |  |  |  |  | } elsif ($marker==0xd9 and $isEXV) { | 
| 5982 |  |  |  |  |  |  | # write EXV EOI (any trailer will be lost) | 
| 5983 | 2 | 50 |  |  |  | 8 | Write($outfile, "\xff\xd9") or $err = 1; | 
| 5984 | 2 |  |  |  |  | 8 | $rtnVal = 1; | 
| 5985 | 2 |  |  |  |  | 11 | last; | 
| 5986 |  |  |  |  |  |  |  | 
| 5987 |  |  |  |  |  |  | } elsif ($marker==0x00 or $marker==0x01 or ($marker>=0xd0 and $marker<=0xd7)) { | 
| 5988 | 0 | 0 | 0 |  |  | 0 | $verbose and $marker and print $out "JPEG $markerName:\n"; | 
| 5989 |  |  |  |  |  |  | # handle stand-alone markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7) | 
| 5990 | 0 | 0 |  |  |  | 0 | Write($outfile, $hdr) or $err = 1; | 
| 5991 | 0 |  |  |  |  | 0 | next; | 
| 5992 |  |  |  |  |  |  | } | 
| 5993 |  |  |  |  |  |  | # | 
| 5994 |  |  |  |  |  |  | # NOTE: A 'next' statement after this point will cause $$segDataPt | 
| 5995 |  |  |  |  |  |  | #       not to be written if there is an output file, so in this case | 
| 5996 |  |  |  |  |  |  | #       the $$self{CHANGED} flags must be updated | 
| 5997 |  |  |  |  |  |  | # | 
| 5998 | 574 |  |  |  |  | 1195 | my $segDataPt = \$segData; | 
| 5999 | 574 |  |  |  |  | 1110 | $length = length($segData); | 
| 6000 | 574 | 100 |  |  |  | 1343 | if ($verbose) { | 
| 6001 | 2 |  |  |  |  | 8 | print $out "JPEG $markerName ($length bytes):\n"; | 
| 6002 | 2 | 50 | 33 |  |  | 8 | if ($verbose > 2 and $markerName =~ /^APP/) { | 
| 6003 | 0 |  |  |  |  | 0 | HexDump($segDataPt, undef, %dumpParms); | 
| 6004 |  |  |  |  |  |  | } | 
| 6005 |  |  |  |  |  |  | } | 
| 6006 |  |  |  |  |  |  | # group delete of APP segments | 
| 6007 | 574 | 100 |  |  |  | 1579 | if ($$delGroup{$dirName}) { | 
| 6008 | 55 | 50 |  |  |  | 120 | $verbose and print $out "  Deleting $dirName segment\n"; | 
| 6009 | 55 | 100 |  |  |  | 156 | $self->Warn('ICC_Profile deleted. Image colors may be affected') if $dirName eq 'ICC_Profile'; | 
| 6010 | 55 |  |  |  |  | 99 | ++$$self{CHANGED}; | 
| 6011 | 55 |  |  |  |  | 152 | next Marker; | 
| 6012 |  |  |  |  |  |  | } | 
| 6013 | 519 |  |  |  |  | 953 | my ($segType, $del); | 
| 6014 |  |  |  |  |  |  | # rewrite this segment only if we are changing a tag which is contained in its | 
| 6015 |  |  |  |  |  |  | # directory (or deleting '*', in which case we need to identify the segment type) | 
| 6016 | 519 |  | 100 |  |  | 2473 | while (exists $$editDirs{$markerName} or $$delGroup{'*'}) { | 
| 6017 | 131 | 100 |  |  |  | 931 | if ($marker == 0xe0) {              # APP0 (JFIF, CIFF) | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 6018 | 31 | 100 |  |  |  | 330 | if ($$segDataPt =~ /^JFIF\0/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 6019 | 11 |  |  |  |  | 31 | $segType = 'JFIF'; | 
| 6020 | 11 | 50 |  |  |  | 57 | $$delGroup{JFIF} and $del = 1, last; | 
| 6021 | 11 | 50 |  |  |  | 47 | last unless $$editDirs{JFIF}; | 
| 6022 | 11 |  |  |  |  | 65 | SetByteOrder('MM'); | 
| 6023 | 11 |  |  |  |  | 52 | my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main'); | 
| 6024 | 11 |  |  |  |  | 103 | my %dirInfo = ( | 
| 6025 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6026 |  |  |  |  |  |  | DataPos  => $segPos, | 
| 6027 |  |  |  |  |  |  | DataLen  => $length, | 
| 6028 |  |  |  |  |  |  | DirStart => 5,     # directory starts after identifier | 
| 6029 |  |  |  |  |  |  | DirLen   => $length-5, | 
| 6030 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6031 |  |  |  |  |  |  | ); | 
| 6032 | 11 |  |  |  |  | 69 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 6033 | 11 | 50 | 33 |  |  | 124 | if (defined $newData and length $newData) { | 
| 6034 | 11 |  |  |  |  | 66 | $$segDataPt = "JFIF\0" . $newData; | 
| 6035 |  |  |  |  |  |  | } | 
| 6036 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^JFXX\0\x10/) { | 
| 6037 | 8 |  |  |  |  | 27 | $segType = 'JFXX'; | 
| 6038 | 8 | 100 |  |  |  | 40 | $$delGroup{JFIF} and $del = 1; | 
| 6039 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^(II|MM).{4}HEAPJPGM/s) { | 
| 6040 | 6 |  |  |  |  | 22 | $segType = 'CIFF'; | 
| 6041 | 6 | 50 |  |  |  | 24 | $$delGroup{CIFF} and $del = 1, last; | 
| 6042 | 6 | 100 |  |  |  | 27 | last unless $$editDirs{CIFF}; | 
| 6043 | 4 |  |  |  |  | 14 | my $newData = ''; | 
| 6044 | 4 |  |  |  |  | 32 | my %dirInfo = ( | 
| 6045 |  |  |  |  |  |  | RAF => new File::RandomAccess($segDataPt), | 
| 6046 |  |  |  |  |  |  | OutFile => \$newData, | 
| 6047 |  |  |  |  |  |  | ); | 
| 6048 | 4 |  |  |  |  | 39 | require Image::ExifTool::CanonRaw; | 
| 6049 | 4 | 50 |  |  |  | 34 | if (Image::ExifTool::CanonRaw::WriteCRW($self, \%dirInfo) > 0) { | 
| 6050 | 4 | 50 |  |  |  | 14 | if (length $newData) { | 
| 6051 | 4 |  |  |  |  | 16 | $$segDataPt = $newData; | 
| 6052 |  |  |  |  |  |  | } else { | 
| 6053 | 0 |  |  |  |  | 0 | undef $segDataPt; | 
| 6054 | 0 |  |  |  |  | 0 | $del = 1;   # delete this segment | 
| 6055 |  |  |  |  |  |  | } | 
| 6056 |  |  |  |  |  |  | } | 
| 6057 |  |  |  |  |  |  | } | 
| 6058 |  |  |  |  |  |  | } elsif ($marker == 0xe1) {         # APP1 (EXIF, XMP) | 
| 6059 |  |  |  |  |  |  | # check for EXIF data | 
| 6060 | 73 | 100 | 0 |  |  | 1481 | if ($$segDataPt =~ /^(.{0,4})$exifAPP1hdr/is) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 6061 | 52 |  |  |  |  | 172 | my $hdrLen = length $exifAPP1hdr; | 
| 6062 | 52 | 50 |  |  |  | 477 | if (length $1) { | 
|  |  | 50 |  |  |  |  |  | 
| 6063 | 0 |  |  |  |  | 0 | $hdrLen += length $1; | 
| 6064 | 0 |  |  |  |  | 0 | $self->Error('Unknown garbage at start of EXIF segment',1); | 
| 6065 |  |  |  |  |  |  | } elsif ($$segDataPt !~ /^Exif\0/) { | 
| 6066 | 0 |  |  |  |  | 0 | $self->Error('Incorrect EXIF segment identifier',1); | 
| 6067 |  |  |  |  |  |  | } | 
| 6068 | 52 |  |  |  |  | 1059 | $segType = 'EXIF'; | 
| 6069 | 52 | 100 |  |  |  | 758 | last unless $$editDirs{IFD0}; | 
| 6070 |  |  |  |  |  |  | # add this data to the combined data if it exists | 
| 6071 | 51 | 50 |  |  |  | 238 | if (defined $combinedSegData) { | 
| 6072 | 0 |  |  |  |  | 0 | $combinedSegData .= substr($$segDataPt,$hdrLen); | 
| 6073 | 0 |  |  |  |  | 0 | $segDataPt = \$combinedSegData; | 
| 6074 | 0 |  |  |  |  | 0 | $segPos = $firstSegPos; | 
| 6075 | 0 |  |  |  |  | 0 | $length = length $combinedSegData;  # update length | 
| 6076 |  |  |  |  |  |  | } | 
| 6077 |  |  |  |  |  |  | # peek ahead to see if the next segment is extended EXIF | 
| 6078 | 51 | 50 |  |  |  | 242 | if ($dirOrder[0] eq 'ExtendedEXIF') { | 
| 6079 |  |  |  |  |  |  | # initialize combined data if necessary | 
| 6080 | 0 | 0 |  |  |  | 0 | unless (defined $combinedSegData) { | 
| 6081 | 0 |  |  |  |  | 0 | $combinedSegData = $$segDataPt; | 
| 6082 | 0 |  |  |  |  | 0 | $firstSegPos = $segPos; | 
| 6083 | 0 |  |  |  |  | 0 | $self->Warn('File contains multi-segment EXIF',1); | 
| 6084 |  |  |  |  |  |  | } | 
| 6085 | 0 |  |  |  |  | 0 | next Marker;    # get the next segment to combine | 
| 6086 |  |  |  |  |  |  | } | 
| 6087 | 51 | 50 |  |  |  | 205 | $doneDir{IFD0} and $self->Warn('Multiple APP1 EXIF records'); | 
| 6088 | 51 |  |  |  |  | 164 | $doneDir{IFD0} = 1; | 
| 6089 |  |  |  |  |  |  | # check del groups now so we can change byte order in one step | 
| 6090 | 51 | 100 | 66 |  |  | 404 | if ($$delGroup{IFD0} or $$delGroup{EXIF}) { | 
| 6091 | 1 |  |  |  |  | 3 | delete $doneDir{IFD0};  # delete so we will create a new one | 
| 6092 | 1 |  |  |  |  | 3 | $del = 1; | 
| 6093 | 1 |  |  |  |  | 5 | last; | 
| 6094 |  |  |  |  |  |  | } | 
| 6095 |  |  |  |  |  |  | # rewrite EXIF as if this were a TIFF file in memory | 
| 6096 | 50 |  |  |  |  | 555 | my %dirInfo = ( | 
| 6097 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6098 |  |  |  |  |  |  | DataPos  => -$hdrLen, # (remember: relative to Base!) | 
| 6099 |  |  |  |  |  |  | DirStart => $hdrLen, | 
| 6100 |  |  |  |  |  |  | Base     => $segPos + $hdrLen, | 
| 6101 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6102 |  |  |  |  |  |  | DirName  => 'IFD0', | 
| 6103 |  |  |  |  |  |  | ); | 
| 6104 |  |  |  |  |  |  | # write new EXIF data to memory | 
| 6105 | 50 |  |  |  |  | 907 | my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main'); | 
| 6106 | 50 |  |  |  |  | 442 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF); | 
| 6107 | 50 | 50 |  |  |  | 253 | if (defined $buff) { | 
| 6108 | 50 |  |  |  |  | 162 | undef $$segDataPt;  # free the old buffer | 
| 6109 | 50 |  |  |  |  | 151 | $segDataPt = \$buff; | 
| 6110 |  |  |  |  |  |  | } else { | 
| 6111 | 0 | 0 |  |  |  | 0 | last Marker unless $self->Options('IgnoreMinorErrors'); | 
| 6112 |  |  |  |  |  |  | } | 
| 6113 |  |  |  |  |  |  | # delete segment if IFD contains no entries | 
| 6114 | 50 | 100 |  |  |  | 248 | length $$segDataPt or $del = 1, last; | 
| 6115 | 46 | 50 |  |  |  | 234 | if (length($$segDataPt) + length($exifAPP1hdr) > $maxSegmentLen) { | 
| 6116 | 0 | 0 |  |  |  | 0 | if ($self->Options('NoMultiExif')) { | 
| 6117 | 0 |  |  |  |  | 0 | $self->Error('EXIF is too large for JPEG segment'); | 
| 6118 |  |  |  |  |  |  | } else { | 
| 6119 | 0 |  |  |  |  | 0 | $self->Warn('Writing multi-segment EXIF',1); | 
| 6120 |  |  |  |  |  |  | } | 
| 6121 |  |  |  |  |  |  | } | 
| 6122 |  |  |  |  |  |  | # switch to buffered output if required | 
| 6123 | 46 | 100 | 66 |  |  | 432 | if (($$self{PREVIEW_INFO} or $$self{LeicaTrailer}) and not $oldOutfile) { | 
|  |  |  | 66 |  |  |  |  | 
| 6124 | 1 |  |  |  |  | 4 | $writeBuffer = ''; | 
| 6125 | 1 |  |  |  |  | 3 | $oldOutfile = $outfile; | 
| 6126 | 1 |  |  |  |  | 4 | $outfile = \$writeBuffer; | 
| 6127 |  |  |  |  |  |  | # must account for segment, EXIF and TIFF headers | 
| 6128 | 1 | 50 |  |  |  | 6 | $$self{PREVIEW_INFO}{Fixup}{Start} += 18 if $$self{PREVIEW_INFO}; | 
| 6129 | 1 | 50 |  |  |  | 6 | $$self{LeicaTrailer}{Fixup}{Start} += 18 if $$self{LeicaTrailer}; | 
| 6130 |  |  |  |  |  |  | } | 
| 6131 |  |  |  |  |  |  | # write as multi-segment | 
| 6132 | 46 |  |  |  |  | 307 | my $n = WriteMultiSegment($outfile, $marker, $exifAPP1hdr, $segDataPt, 'EXIF'); | 
| 6133 | 46 | 50 | 33 |  |  | 402 | if (not $n) { | 
|  |  | 50 |  |  |  |  |  | 
| 6134 | 0 |  |  |  |  | 0 | $err = 1; | 
| 6135 |  |  |  |  |  |  | } elsif ($n > 1 and $oldOutfile) { | 
| 6136 |  |  |  |  |  |  | # (punt on this because updating the pointers would be a real pain) | 
| 6137 | 0 |  |  |  |  | 0 | $self->Error("Can't write multi-segment EXIF with external pointers"); | 
| 6138 |  |  |  |  |  |  | } | 
| 6139 | 46 |  |  |  |  | 152 | undef $combinedSegData; | 
| 6140 | 46 |  |  |  |  | 114 | undef $$segDataPt; | 
| 6141 | 46 |  |  |  |  | 459 | next Marker; | 
| 6142 |  |  |  |  |  |  | # check for XMP data | 
| 6143 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^($xmpAPP1hdr|$xmpExtAPP1hdr)/) { | 
| 6144 | 21 |  |  |  |  | 80 | $segType = 'XMP'; | 
| 6145 | 21 | 50 |  |  |  | 94 | $$delGroup{XMP} and $del = 1, last; | 
| 6146 | 21 |  | 100 |  |  | 136 | $doneDir{XMP} = ($doneDir{XMP} || 0) + 1; | 
| 6147 | 21 | 100 |  |  |  | 94 | last unless $$editDirs{XMP}; | 
| 6148 | 14 | 100 |  |  |  | 65 | if ($doneDir{XMP} + $dirCount{XMP} > 1) { | 
| 6149 |  |  |  |  |  |  | # must assemble all XMP segments before writing | 
| 6150 | 3 |  |  |  |  | 7 | my ($guid, $extXMP); | 
| 6151 | 3 | 100 |  |  |  | 31 | if ($$segDataPt =~ /^$xmpExtAPP1hdr/) { | 
| 6152 |  |  |  |  |  |  | # save extended XMP data | 
| 6153 | 2 | 50 |  |  |  | 14 | if (length $$segDataPt < 75) { | 
| 6154 | 0 |  |  |  |  | 0 | $extendedXMP{Error} = 'Truncated data'; | 
| 6155 |  |  |  |  |  |  | } else { | 
| 6156 | 2 |  |  |  |  | 11 | my ($size, $off) = unpack('x67N2', $$segDataPt); | 
| 6157 | 2 |  |  |  |  | 9 | $guid = substr($$segDataPt, 35, 32); | 
| 6158 | 2 | 50 |  |  |  | 9 | if ($guid =~ /[^A-Za-z0-9]/) { # (technically, should be uppercase) | 
| 6159 | 0 |  |  |  |  | 0 | $extendedXMP{Error} = 'Invalid GUID'; | 
| 6160 |  |  |  |  |  |  | } else { | 
| 6161 |  |  |  |  |  |  | # remember extended data for each GUID | 
| 6162 | 2 |  |  |  |  | 8 | $extXMP = $extendedXMP{$guid}; | 
| 6163 | 2 | 100 |  |  |  | 7 | if ($extXMP) { | 
| 6164 | 1 | 50 |  |  |  | 7 | $size == $$extXMP{Size} or $extendedXMP{Error} = 'Inconsistent size'; | 
| 6165 |  |  |  |  |  |  | } else { | 
| 6166 | 1 |  |  |  |  | 9 | $extXMP = $extendedXMP{$guid} = { }; | 
| 6167 |  |  |  |  |  |  | } | 
| 6168 | 2 |  |  |  |  | 6 | $$extXMP{Size} = $size; | 
| 6169 | 2 |  |  |  |  | 9 | $$extXMP{$off} = substr($$segDataPt, 75); | 
| 6170 |  |  |  |  |  |  | } | 
| 6171 |  |  |  |  |  |  | } | 
| 6172 |  |  |  |  |  |  | } else { | 
| 6173 |  |  |  |  |  |  | # save all main XMP segments (should normally be only one) | 
| 6174 | 1 | 50 |  |  |  | 7 | $extendedXMP{Main} = [] unless $extendedXMP{Main}; | 
| 6175 | 1 |  |  |  |  | 3 | push @{$extendedXMP{Main}}, substr($$segDataPt, length $xmpAPP1hdr); | 
|  | 1 |  |  |  |  | 5 |  | 
| 6176 |  |  |  |  |  |  | } | 
| 6177 |  |  |  |  |  |  | # continue processing only if we have read all the segments | 
| 6178 | 3 | 100 |  |  |  | 15 | next Marker if $dirCount{XMP}; | 
| 6179 |  |  |  |  |  |  | # reconstruct an XMP super-segment | 
| 6180 | 1 |  |  |  |  | 8 | $$segDataPt = $xmpAPP1hdr; | 
| 6181 | 1 |  |  |  |  | 4 | my $goodGuid = ''; | 
| 6182 | 1 |  |  |  |  | 2 | foreach (@{$extendedXMP{Main}}) { | 
|  | 1 |  |  |  |  | 5 |  | 
| 6183 |  |  |  |  |  |  | # get the HasExtendedXMP GUID if it exists | 
| 6184 | 1 | 50 |  |  |  | 10 | if (/:HasExtendedXMP\s*(=\s*['"]|>)(\w{32})/) { | 
| 6185 |  |  |  |  |  |  | # warn of subsequent XMP blocks specifying a different | 
| 6186 |  |  |  |  |  |  | # HasExtendedXMP (have never seen this) | 
| 6187 | 1 | 50 | 33 |  |  | 6 | if ($goodGuid and $goodGuid ne $2) { | 
| 6188 | 0 |  |  |  |  | 0 | $self->WarnOnce('Multiple XMP segments specifying different extended XMP GUID'); | 
| 6189 |  |  |  |  |  |  | } | 
| 6190 | 1 |  |  |  |  | 4 | $goodGuid = $2; # GUID for the standard extended XMP | 
| 6191 |  |  |  |  |  |  | } | 
| 6192 | 1 |  |  |  |  | 4 | $$segDataPt .= $_; | 
| 6193 |  |  |  |  |  |  | } | 
| 6194 |  |  |  |  |  |  | # GUID of the extended XMP that we want to read | 
| 6195 | 1 |  | 50 |  |  | 6 | my $readGuid = $$self{OPTIONS}{ExtendedXMP} || 0; | 
| 6196 | 1 | 50 |  |  |  | 10 | $readGuid = $goodGuid if $readGuid eq '1'; | 
| 6197 | 1 |  |  |  |  | 6 | foreach $guid (sort keys %extendedXMP) { | 
| 6198 | 2 | 100 |  |  |  | 8 | next unless length $guid == 32;     # ignore other (internal) keys | 
| 6199 | 1 | 50 | 33 |  |  | 7 | if ($guid ne $readGuid and $readGuid ne '2') { | 
| 6200 | 0 | 0 |  |  |  | 0 | my $non = $guid eq $goodGuid ? '' : 'non-'; | 
| 6201 | 0 |  |  |  |  | 0 | $self->Warn("Ignored ${non}standard extended XMP (GUID $guid)"); | 
| 6202 | 0 |  |  |  |  | 0 | next; | 
| 6203 |  |  |  |  |  |  | } | 
| 6204 | 1 | 50 |  |  |  | 4 | if ($guid ne $goodGuid) { | 
| 6205 | 0 |  |  |  |  | 0 | $self->Warn("Reading non-standard extended XMP (GUID $guid)"); | 
| 6206 |  |  |  |  |  |  | } | 
| 6207 | 1 |  |  |  |  | 3 | $extXMP = $extendedXMP{$guid}; | 
| 6208 | 1 | 50 |  |  |  | 6 | next unless ref $extXMP eq 'HASH';  # (just to be safe) | 
| 6209 | 1 |  |  |  |  | 4 | my $size = $$extXMP{Size}; | 
| 6210 | 1 |  |  |  |  | 3 | my (@offsets, $off); | 
| 6211 | 1 |  |  |  |  | 8 | for ($off=0; $off<$size; ) { | 
| 6212 | 2 | 50 |  |  |  | 10 | last unless defined $$extXMP{$off}; | 
| 6213 | 2 |  |  |  |  | 5 | push @offsets, $off; | 
| 6214 | 2 |  |  |  |  | 5 | $off += length $$extXMP{$off}; | 
| 6215 |  |  |  |  |  |  | } | 
| 6216 | 1 | 50 |  |  |  | 5 | if ($off == $size) { | 
| 6217 |  |  |  |  |  |  | # add all XMP to super-segment | 
| 6218 | 1 |  |  |  |  | 7 | $$segDataPt .= $$extXMP{$_} foreach @offsets; | 
| 6219 |  |  |  |  |  |  | } else { | 
| 6220 | 0 |  |  |  |  | 0 | $self->Error("Incomplete extended XMP (GUID $guid)", 1); | 
| 6221 |  |  |  |  |  |  | } | 
| 6222 |  |  |  |  |  |  | } | 
| 6223 | 1 | 50 |  |  |  | 14 | $self->Error("$extendedXMP{Error} in extended XMP", 1) if $extendedXMP{Error}; | 
| 6224 |  |  |  |  |  |  | } | 
| 6225 | 12 |  |  |  |  | 38 | my $start = length $xmpAPP1hdr; | 
| 6226 | 12 |  |  |  |  | 60 | my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main'); | 
| 6227 | 12 |  |  |  |  | 129 | my %dirInfo = ( | 
| 6228 |  |  |  |  |  |  | DataPt     => $segDataPt, | 
| 6229 |  |  |  |  |  |  | DirStart   => $start, | 
| 6230 |  |  |  |  |  |  | Parent     => $markerName, | 
| 6231 |  |  |  |  |  |  | # limit XMP size and create extended XMP if necessary | 
| 6232 |  |  |  |  |  |  | MaxDataLen => $maxXMPLen - length($xmpAPP1hdr), | 
| 6233 |  |  |  |  |  |  | ); | 
| 6234 | 12 |  |  |  |  | 67 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 6235 | 12 | 100 |  |  |  | 59 | if (defined $newData) { | 
| 6236 | 9 |  |  |  |  | 28 | undef %extendedXMP; | 
| 6237 | 9 | 100 |  |  |  | 33 | if (length $newData) { | 
| 6238 |  |  |  |  |  |  | # write multi-segment XMP (XMP plus extended XMP if necessary) | 
| 6239 |  |  |  |  |  |  | WriteMultiXMP($self, $outfile, \$newData, $dirInfo{ExtendedXMP}, | 
| 6240 | 7 | 50 |  |  |  | 50 | $dirInfo{ExtendedGUID}) or $err = 1; | 
| 6241 | 7 |  |  |  |  | 25 | undef $$segDataPt;  # free the old buffer | 
| 6242 | 7 |  |  |  |  | 49 | next Marker; | 
| 6243 |  |  |  |  |  |  | } else { | 
| 6244 | 2 |  |  |  |  | 7 | $$segDataPt = '';   # delete the XMP | 
| 6245 |  |  |  |  |  |  | } | 
| 6246 |  |  |  |  |  |  | } else { | 
| 6247 | 3 | 50 |  |  |  | 15 | $verbose and print $out "    [XMP rewritten with no changes]\n"; | 
| 6248 | 3 | 50 |  |  |  | 16 | if ($doneDir{XMP} > 1) { | 
| 6249 |  |  |  |  |  |  | # re-write original multi-segment XMP | 
| 6250 | 0 |  |  |  |  | 0 | my ($dat, $guid, $extXMP, $off); | 
| 6251 | 0 |  |  |  |  | 0 | foreach $dat (@{$extendedXMP{Main}}) {      # main XMP | 
|  | 0 |  |  |  |  | 0 |  | 
| 6252 | 0 | 0 |  |  |  | 0 | next unless length $dat; | 
| 6253 | 0 |  |  |  |  | 0 | $s = pack('n', length($xmpAPP1hdr) + length($dat) + 2); | 
| 6254 | 0 | 0 |  |  |  | 0 | Write($outfile, $hdr, $s, $xmpAPP1hdr, $dat) or $err = 1; | 
| 6255 |  |  |  |  |  |  | } | 
| 6256 | 0 |  |  |  |  | 0 | foreach $guid (sort keys %extendedXMP) {    # extended XMP | 
| 6257 | 0 | 0 |  |  |  | 0 | next unless length $guid == 32; | 
| 6258 | 0 |  |  |  |  | 0 | $extXMP = $extendedXMP{$guid}; | 
| 6259 | 0 | 0 |  |  |  | 0 | next unless ref $extXMP eq 'HASH'; | 
| 6260 | 0 | 0 |  |  |  | 0 | my $size = $$extXMP{Size} or next; | 
| 6261 | 0 |  |  |  |  | 0 | for ($off=0; defined $$extXMP{$off}; $off += length $$extXMP{$off}) { | 
| 6262 | 0 |  |  |  |  | 0 | $s = pack('n', length($xmpExtAPP1hdr) + length($$extXMP{$off}) + 42); | 
| 6263 |  |  |  |  |  |  | Write($outfile, $hdr, $s, $xmpExtAPP1hdr, $guid, | 
| 6264 | 0 | 0 |  |  |  | 0 | pack('N2', $size, $off), $$extXMP{$off}) or $err = 1; | 
| 6265 |  |  |  |  |  |  | } | 
| 6266 |  |  |  |  |  |  | } | 
| 6267 | 0 |  |  |  |  | 0 | undef $$segDataPt;  # free the old buffer | 
| 6268 | 0 |  |  |  |  | 0 | undef %extendedXMP; | 
| 6269 | 0 |  |  |  |  | 0 | next Marker; | 
| 6270 |  |  |  |  |  |  | } | 
| 6271 |  |  |  |  |  |  | # continue on to re-write original single-segment XMP | 
| 6272 |  |  |  |  |  |  | } | 
| 6273 | 5 | 100 |  |  |  | 40 | $del = 1 unless length $$segDataPt; | 
| 6274 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^http/ or $$segDataPt =~ / | 
| 6275 | 0 |  |  |  |  | 0 | $self->Warn('Ignored APP1 XMP segment with non-standard header', 1); | 
| 6276 |  |  |  |  |  |  | } | 
| 6277 |  |  |  |  |  |  | } elsif ($marker == 0xe2) {         # APP2 (ICC Profile, FPXR, MPF) | 
| 6278 | 0 | 0 | 0 |  |  | 0 | if ($$segDataPt =~ /^ICC_PROFILE\0/ and $length >= 14) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 6279 | 0 |  |  |  |  | 0 | $segType = 'ICC_Profile'; | 
| 6280 | 0 | 0 |  |  |  | 0 | $$delGroup{ICC_Profile} and $del = 1, last; | 
| 6281 |  |  |  |  |  |  | # must concatenate blocks of profile | 
| 6282 | 0 |  |  |  |  | 0 | my $chunkNum = Get8u($segDataPt, 12); | 
| 6283 | 0 |  |  |  |  | 0 | my $chunksTot = Get8u($segDataPt, 13); | 
| 6284 | 0 | 0 |  |  |  | 0 | if (defined $iccChunksTotal) { | 
| 6285 |  |  |  |  |  |  | # abort parsing ICC_Profile if the total chunk count is inconsistent | 
| 6286 | 0 | 0 | 0 |  |  | 0 | if ($chunksTot != $iccChunksTotal and defined $iccChunkCount) { | 
| 6287 |  |  |  |  |  |  | # an error because the accumulated profile data will be lost | 
| 6288 | 0 |  |  |  |  | 0 | $self->Error('Inconsistent ICC_Profile chunk count', 1); | 
| 6289 | 0 |  |  |  |  | 0 | undef $iccChunkCount; # abort ICC_Profile parsing | 
| 6290 | 0 |  |  |  |  | 0 | undef $chunkNum;      # avoid 2nd warning below | 
| 6291 | 0 |  |  |  |  | 0 | ++$$self{CHANGED};    # we are deleting the bad chunks before this one | 
| 6292 |  |  |  |  |  |  | } | 
| 6293 |  |  |  |  |  |  | } else { | 
| 6294 | 0 |  |  |  |  | 0 | $iccChunkCount = 0; | 
| 6295 | 0 |  |  |  |  | 0 | $iccChunksTotal = $chunksTot; | 
| 6296 | 0 | 0 |  |  |  | 0 | $self->Warn('ICC_Profile chunk count is zero') if !$chunksTot; | 
| 6297 |  |  |  |  |  |  | } | 
| 6298 | 0 | 0 |  |  |  | 0 | if (defined $iccChunkCount) { | 
|  |  | 0 |  |  |  |  |  | 
| 6299 |  |  |  |  |  |  | # save this chunk | 
| 6300 | 0 | 0 |  |  |  | 0 | if (defined $iccChunk[$chunkNum]) { | 
| 6301 | 0 |  |  |  |  | 0 | $self->Warn("Duplicate ICC_Profile chunk number $chunkNum"); | 
| 6302 | 0 |  |  |  |  | 0 | $iccChunk[$chunkNum] .= substr($$segDataPt, 14); | 
| 6303 |  |  |  |  |  |  | } else { | 
| 6304 | 0 |  |  |  |  | 0 | $iccChunk[$chunkNum] = substr($$segDataPt, 14); | 
| 6305 |  |  |  |  |  |  | } | 
| 6306 |  |  |  |  |  |  | # continue accumulating chunks unless we have all of them | 
| 6307 | 0 | 0 |  |  |  | 0 | next Marker unless ++$iccChunkCount >= $iccChunksTotal; | 
| 6308 | 0 |  |  |  |  | 0 | undef $iccChunkCount;   # prevent reprocessing | 
| 6309 | 0 |  |  |  |  | 0 | $doneDir{ICC_Profile} = 1; | 
| 6310 |  |  |  |  |  |  | # combine the ICC_Profile chunks | 
| 6311 | 0 |  |  |  |  | 0 | my $icc_profile = ''; | 
| 6312 | 0 |  | 0 |  |  | 0 | defined $_ and $icc_profile .= $_ foreach @iccChunk; | 
| 6313 | 0 |  |  |  |  | 0 | undef @iccChunk;   # free memory | 
| 6314 | 0 |  |  |  |  | 0 | $segDataPt = \$icc_profile; | 
| 6315 | 0 |  |  |  |  | 0 | $length = length $icc_profile; | 
| 6316 | 0 |  |  |  |  | 0 | my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main'); | 
| 6317 | 0 |  |  |  |  | 0 | my %dirInfo = ( | 
| 6318 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6319 |  |  |  |  |  |  | DataPos  => $segPos + 14, | 
| 6320 |  |  |  |  |  |  | DataLen  => $length, | 
| 6321 |  |  |  |  |  |  | DirStart => 0, | 
| 6322 |  |  |  |  |  |  | DirLen   => $length, | 
| 6323 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6324 |  |  |  |  |  |  | ); | 
| 6325 | 0 |  |  |  |  | 0 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 6326 | 0 | 0 |  |  |  | 0 | if (defined $newData) { | 
| 6327 | 0 |  |  |  |  | 0 | undef $$segDataPt;  # free the old buffer | 
| 6328 | 0 |  |  |  |  | 0 | $segDataPt = \$newData; | 
| 6329 |  |  |  |  |  |  | } | 
| 6330 | 0 | 0 |  |  |  | 0 | length $$segDataPt or $del = 1, last; | 
| 6331 |  |  |  |  |  |  | # write as ICC multi-segment | 
| 6332 | 0 | 0 |  |  |  | 0 | WriteMultiSegment($outfile, $marker, "ICC_PROFILE\0", $segDataPt, 'ICC') or $err = 1; | 
| 6333 | 0 |  |  |  |  | 0 | undef $$segDataPt; | 
| 6334 | 0 |  |  |  |  | 0 | next Marker; | 
| 6335 |  |  |  |  |  |  | } elsif (defined $chunkNum) { | 
| 6336 | 0 |  |  |  |  | 0 | $self->WarnOnce('Invalid or extraneous ICC_Profile chunk(s)'); | 
| 6337 |  |  |  |  |  |  | # fall through to preserve this extra profile... | 
| 6338 |  |  |  |  |  |  | } | 
| 6339 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^FPXR\0/) { | 
| 6340 | 0 |  |  |  |  | 0 | $segType = 'FPXR'; | 
| 6341 | 0 | 0 |  |  |  | 0 | $$delGroup{FlashPix} and $del = 1; | 
| 6342 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^MPF\0/) { | 
| 6343 | 0 |  |  |  |  | 0 | $segType = 'MPF'; | 
| 6344 | 0 | 0 |  |  |  | 0 | $$delGroup{MPF} and $del = 1; | 
| 6345 |  |  |  |  |  |  | } | 
| 6346 |  |  |  |  |  |  | } elsif ($marker == 0xe3) {         # APP3 (Kodak Meta) | 
| 6347 | 1 | 50 |  |  |  | 9 | if ($$segDataPt =~ /^(Meta|META|Exif)\0\0/) { | 
| 6348 | 1 |  |  |  |  | 4 | $segType = 'Kodak Meta'; | 
| 6349 | 1 | 50 |  |  |  | 5 | $$delGroup{Meta} and $del = 1, last; | 
| 6350 | 1 | 50 |  |  |  | 5 | $doneDir{Meta} and $self->Warn('Multiple APP3 Meta segments'); | 
| 6351 | 1 |  |  |  |  | 2 | $doneDir{Meta} = 1; | 
| 6352 | 1 | 50 |  |  |  | 5 | last unless $$editDirs{Meta}; | 
| 6353 |  |  |  |  |  |  | # rewrite Meta IFD as if this were a TIFF file in memory | 
| 6354 | 1 |  |  |  |  | 8 | my %dirInfo = ( | 
| 6355 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6356 |  |  |  |  |  |  | DataPos  => -6, # (remember: relative to Base!) | 
| 6357 |  |  |  |  |  |  | DirStart => 6, | 
| 6358 |  |  |  |  |  |  | Base     => $segPos + 6, | 
| 6359 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6360 |  |  |  |  |  |  | DirName  => 'Meta', | 
| 6361 |  |  |  |  |  |  | ); | 
| 6362 |  |  |  |  |  |  | # write new data to memory | 
| 6363 | 1 |  |  |  |  | 7 | my $tagTablePtr = GetTagTable('Image::ExifTool::Kodak::Meta'); | 
| 6364 | 1 |  |  |  |  | 8 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF); | 
| 6365 | 1 | 50 |  |  |  | 5 | if (defined $buff) { | 
| 6366 |  |  |  |  |  |  | # update segment with new data | 
| 6367 | 1 |  |  |  |  | 5 | $$segDataPt = substr($$segDataPt,0,6) . $buff; | 
| 6368 |  |  |  |  |  |  | } else { | 
| 6369 | 0 | 0 |  |  |  | 0 | last Marker unless $self->Options('IgnoreMinorErrors'); | 
| 6370 |  |  |  |  |  |  | } | 
| 6371 |  |  |  |  |  |  | # delete segment if IFD contains no entries | 
| 6372 | 1 | 50 |  |  |  | 9 | $del = 1 unless length($$segDataPt) > 6; | 
| 6373 |  |  |  |  |  |  | } | 
| 6374 |  |  |  |  |  |  | } elsif ($marker == 0xe5) {         # APP5 (Ricoh RMETA) | 
| 6375 | 0 | 0 |  |  |  | 0 | if ($$segDataPt =~ /^RMETA\0/) { | 
| 6376 | 0 |  |  |  |  | 0 | $segType = 'Ricoh RMETA'; | 
| 6377 | 0 | 0 |  |  |  | 0 | $$delGroup{RMETA} and $del = 1; | 
| 6378 |  |  |  |  |  |  | } | 
| 6379 |  |  |  |  |  |  | } elsif ($marker == 0xec) {         # APP12 (Ducky) | 
| 6380 | 1 | 50 |  |  |  | 11 | if ($$segDataPt =~ /^Ducky/) { | 
| 6381 | 1 |  |  |  |  | 3 | $segType = 'Ducky'; | 
| 6382 | 1 | 50 |  |  |  | 7 | $$delGroup{Ducky} and $del = 1, last; | 
| 6383 | 1 | 50 |  |  |  | 7 | $doneDir{Ducky} and $self->Warn('Multiple APP12 Ducky segments'); | 
| 6384 | 1 |  |  |  |  | 3 | $doneDir{Ducky} = 1; | 
| 6385 | 1 | 50 |  |  |  | 6 | last unless $$editDirs{Ducky}; | 
| 6386 | 1 |  |  |  |  | 4 | my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky'); | 
| 6387 | 1 |  |  |  |  | 11 | my %dirInfo = ( | 
| 6388 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6389 |  |  |  |  |  |  | DataPos  => $segPos, | 
| 6390 |  |  |  |  |  |  | DataLen  => $length, | 
| 6391 |  |  |  |  |  |  | DirStart => 5,     # directory starts after identifier | 
| 6392 |  |  |  |  |  |  | DirLen   => $length-5, | 
| 6393 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6394 |  |  |  |  |  |  | ); | 
| 6395 | 1 |  |  |  |  | 5 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 6396 | 1 | 50 |  |  |  | 7 | if (defined $newData) { | 
| 6397 | 1 |  |  |  |  | 3 | undef $$segDataPt;  # free the old buffer | 
| 6398 |  |  |  |  |  |  | # add header to new segment unless empty | 
| 6399 | 1 | 50 |  |  |  | 6 | $newData = 'Ducky' . $newData if length $newData; | 
| 6400 | 1 |  |  |  |  | 3 | $segDataPt = \$newData; | 
| 6401 |  |  |  |  |  |  | } | 
| 6402 | 1 | 50 |  |  |  | 8 | $del = 1 unless length $$segDataPt; | 
| 6403 |  |  |  |  |  |  | } | 
| 6404 |  |  |  |  |  |  | } elsif ($marker == 0xed) {         # APP13 (Photoshop) | 
| 6405 | 9 | 100 |  |  |  | 164 | if ($$segDataPt =~ /^$psAPP13hdr/) { | 
| 6406 | 8 |  |  |  |  | 28 | $segType = 'Photoshop'; | 
| 6407 |  |  |  |  |  |  | # add this data to the combined data if it exists | 
| 6408 | 8 | 50 |  |  |  | 33 | if (defined $combinedSegData) { | 
| 6409 | 0 |  |  |  |  | 0 | $combinedSegData .= substr($$segDataPt,length($psAPP13hdr)); | 
| 6410 | 0 |  |  |  |  | 0 | $segDataPt = \$combinedSegData; | 
| 6411 | 0 |  |  |  |  | 0 | $length = length $combinedSegData;  # update length | 
| 6412 |  |  |  |  |  |  | } | 
| 6413 |  |  |  |  |  |  | # peek ahead to see if the next segment is photoshop data too | 
| 6414 | 8 | 50 |  |  |  | 36 | if ($dirOrder[0] eq 'Photoshop') { | 
| 6415 |  |  |  |  |  |  | # initialize combined data if necessary | 
| 6416 | 0 | 0 |  |  |  | 0 | $combinedSegData = $$segDataPt unless defined $combinedSegData; | 
| 6417 | 0 |  |  |  |  | 0 | next Marker;    # get the next segment to combine | 
| 6418 |  |  |  |  |  |  | } | 
| 6419 | 8 | 50 |  |  |  | 38 | if ($doneDir{Photoshop}) { | 
| 6420 | 0 |  |  |  |  | 0 | $self->Warn('Multiple Photoshop records'); | 
| 6421 |  |  |  |  |  |  | # only rewrite the first Photoshop segment when deleting this group | 
| 6422 |  |  |  |  |  |  | # (to remove multiples when deleting and adding back in one step) | 
| 6423 | 0 | 0 |  |  |  | 0 | $$delGroup{Photoshop} and $del = 1, last; | 
| 6424 |  |  |  |  |  |  | } | 
| 6425 | 8 |  |  |  |  | 27 | $doneDir{Photoshop} = 1; | 
| 6426 |  |  |  |  |  |  | # process APP13 Photoshop record | 
| 6427 | 8 |  |  |  |  | 42 | my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main'); | 
| 6428 | 8 |  |  |  |  | 76 | my %dirInfo = ( | 
| 6429 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6430 |  |  |  |  |  |  | DataPos  => $segPos, | 
| 6431 |  |  |  |  |  |  | DataLen  => $length, | 
| 6432 |  |  |  |  |  |  | DirStart => 14,     # directory starts after identifier | 
| 6433 |  |  |  |  |  |  | DirLen   => $length-14, | 
| 6434 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6435 |  |  |  |  |  |  | ); | 
| 6436 | 8 |  |  |  |  | 46 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 6437 | 8 | 50 |  |  |  | 50 | if (defined $newData) { | 
| 6438 | 8 |  |  |  |  | 30 | undef $$segDataPt;  # free the old buffer | 
| 6439 | 8 |  |  |  |  | 30 | $segDataPt = \$newData; | 
| 6440 |  |  |  |  |  |  | } | 
| 6441 | 8 | 100 |  |  |  | 48 | length $$segDataPt or $del = 1, last; | 
| 6442 |  |  |  |  |  |  | # write as multi-segment | 
| 6443 | 6 | 50 |  |  |  | 30 | WriteMultiSegment($outfile, $marker, $psAPP13hdr, $segDataPt) or $err = 1; | 
| 6444 | 6 |  |  |  |  | 17 | undef $combinedSegData; | 
| 6445 | 6 |  |  |  |  | 22 | undef $$segDataPt; | 
| 6446 | 6 |  |  |  |  | 38 | next Marker; | 
| 6447 |  |  |  |  |  |  | } | 
| 6448 |  |  |  |  |  |  | } elsif ($marker == 0xee) {         # APP14 (Adobe) | 
| 6449 | 4 | 50 |  |  |  | 29 | if ($$segDataPt =~ /^Adobe/) { | 
| 6450 | 4 |  |  |  |  | 13 | $segType = 'Adobe'; | 
| 6451 |  |  |  |  |  |  | # delete it and replace it later if editing | 
| 6452 | 4 | 50 | 33 |  |  | 34 | if ($$delGroup{Adobe} or $$editDirs{Adobe}) { | 
| 6453 | 0 |  |  |  |  | 0 | $del = 1; | 
| 6454 | 0 |  |  |  |  | 0 | undef $doneDir{Adobe};  # so we can add it back again above | 
| 6455 |  |  |  |  |  |  | } | 
| 6456 |  |  |  |  |  |  | } | 
| 6457 |  |  |  |  |  |  | } elsif ($marker == 0xfe) {         # COM (JPEG comment) | 
| 6458 | 4 |  |  |  |  | 14 | my $newComment; | 
| 6459 | 4 | 50 |  |  |  | 18 | unless ($doneDir{COM}) { | 
| 6460 | 4 |  |  |  |  | 14 | $doneDir{COM} = 1; | 
| 6461 | 4 | 100 | 100 |  |  | 33 | unless ($$delGroup{File} and $$delGroup{File} != 2) { | 
| 6462 | 3 |  |  |  |  | 22 | my $tagInfo = $Image::ExifTool::Extra{Comment}; | 
| 6463 | 3 |  |  |  |  | 15 | my $nvHash = $self->GetNewValueHash($tagInfo); | 
| 6464 | 3 |  |  |  |  | 8 | my $val = $segData; | 
| 6465 | 3 |  |  |  |  | 13 | $val =~ s/\0+$//;   # allow for stupid software that adds NULL terminator | 
| 6466 | 3 | 50 | 33 |  |  | 13 | if ($self->IsOverwriting($nvHash, $val) or $$delGroup{File}) { | 
| 6467 | 3 |  |  |  |  | 13 | $newComment = $self->GetNewValue($nvHash); | 
| 6468 |  |  |  |  |  |  | } else { | 
| 6469 | 0 |  |  |  |  | 0 | delete $$editDirs{COM}; # we aren't editing COM after all | 
| 6470 | 0 |  |  |  |  | 0 | last; | 
| 6471 |  |  |  |  |  |  | } | 
| 6472 |  |  |  |  |  |  | } | 
| 6473 |  |  |  |  |  |  | } | 
| 6474 | 4 |  |  |  |  | 27 | $self->VerboseValue('- Comment', $$segDataPt); | 
| 6475 | 4 | 100 |  |  |  | 15 | if (defined $newComment) { | 
| 6476 |  |  |  |  |  |  | # write out the comments | 
| 6477 | 2 |  |  |  |  | 12 | $self->VerboseValue('+ Comment', $newComment); | 
| 6478 | 2 | 50 |  |  |  | 9 | WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1; | 
| 6479 |  |  |  |  |  |  | } else { | 
| 6480 | 2 | 50 |  |  |  | 9 | $verbose and print $out "  Deleting COM segment\n"; | 
| 6481 |  |  |  |  |  |  | } | 
| 6482 | 4 |  |  |  |  | 9 | ++$$self{CHANGED};      # increment the changed flag | 
| 6483 | 4 |  |  |  |  | 8 | undef $segDataPt;       # don't write existing comment | 
| 6484 |  |  |  |  |  |  | } | 
| 6485 | 53 |  |  |  |  | 164 | last;   # didn't want to loop anyway | 
| 6486 |  |  |  |  |  |  | } | 
| 6487 |  |  |  |  |  |  |  | 
| 6488 |  |  |  |  |  |  | # delete necessary segments (including unknown segments if deleting all) | 
| 6489 | 458 | 100 | 100 |  |  | 2135 | if ($del or ($$delGroup{'*'} and not $segType and $marker>=0xe0 and $marker<=0xef)) { | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 6490 | 13 | 100 |  |  |  | 46 | $segType = 'unknown' unless $segType; | 
| 6491 | 13 | 50 |  |  |  | 46 | $verbose and print $out "  Deleting $markerName $segType segment\n"; | 
| 6492 | 13 |  |  |  |  | 42 | ++$$self{CHANGED}; | 
| 6493 | 13 |  |  |  |  | 43 | next Marker; | 
| 6494 |  |  |  |  |  |  | } | 
| 6495 |  |  |  |  |  |  | # write out this segment if $segDataPt is still defined | 
| 6496 | 445 | 100 | 66 |  |  | 1934 | if (defined $segDataPt and defined $$segDataPt) { | 
| 6497 |  |  |  |  |  |  | # write the data for this record (the data could have been | 
| 6498 |  |  |  |  |  |  | # modified, so recalculate the length word) | 
| 6499 | 441 |  |  |  |  | 874 | my $size = length($$segDataPt); | 
| 6500 | 441 | 50 |  |  |  | 1187 | if ($size > $maxSegmentLen) { | 
| 6501 | 0 | 0 |  |  |  | 0 | $segType or $segType = 'Unknown'; | 
| 6502 | 0 |  |  |  |  | 0 | $self->Error("$segType $markerName segment too large! ($size bytes)"); | 
| 6503 | 0 |  |  |  |  | 0 | $err = 1; | 
| 6504 |  |  |  |  |  |  | } else { | 
| 6505 | 441 |  |  |  |  | 1345 | $s = pack('n', length($$segDataPt) + 2); | 
| 6506 | 441 | 50 |  |  |  | 1724 | Write($outfile, $hdr, $s, $$segDataPt) or $err = 1; | 
| 6507 |  |  |  |  |  |  | } | 
| 6508 | 441 |  |  |  |  | 1124 | undef $$segDataPt;  # free the buffer | 
| 6509 | 441 |  |  |  |  | 1033 | undef $segDataPt; | 
| 6510 |  |  |  |  |  |  | } | 
| 6511 |  |  |  |  |  |  | } | 
| 6512 |  |  |  |  |  |  | # make sure the ICC_Profile was complete | 
| 6513 | 110 | 50 |  |  |  | 476 | $self->Error('Incomplete ICC_Profile record', 1) if defined $iccChunkCount; | 
| 6514 | 110 | 100 |  |  |  | 444 | pop @$path if @$path > $pn; | 
| 6515 |  |  |  |  |  |  | # if oldOutfile is still set, there was an error copying the JPEG | 
| 6516 | 110 | 50 |  |  |  | 384 | $oldOutfile and return 0; | 
| 6517 | 110 | 50 |  |  |  | 398 | if ($rtnVal) { | 
| 6518 |  |  |  |  |  |  | # add any new trailers we are creating | 
| 6519 | 110 |  |  |  |  | 630 | my $trailPt = $self->AddNewTrailers(); | 
| 6520 | 110 | 100 | 50 |  |  | 472 | Write($outfile, $$trailPt) or $err = 1 if $trailPt; | 
| 6521 |  |  |  |  |  |  | } | 
| 6522 |  |  |  |  |  |  | # set return value to -1 if we only had a write error | 
| 6523 | 110 | 50 | 33 |  |  | 698 | $rtnVal = -1 if $rtnVal and $err; | 
| 6524 | 110 | 50 | 66 |  |  | 505 | if ($creatingEXV and $rtnVal > 0 and not $$self{CHANGED}) { | 
|  |  |  | 66 |  |  |  |  | 
| 6525 | 0 |  |  |  |  | 0 | $self->Error('Nothing written'); | 
| 6526 | 0 |  |  |  |  | 0 | $rtnVal = -1; | 
| 6527 |  |  |  |  |  |  | } | 
| 6528 | 110 |  |  |  |  | 1268 | return $rtnVal; | 
| 6529 |  |  |  |  |  |  | } | 
| 6530 |  |  |  |  |  |  |  | 
| 6531 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6532 |  |  |  |  |  |  | # Validate an image for writing | 
| 6533 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) raw value reference | 
| 6534 |  |  |  |  |  |  | # Returns: error string or undef on success | 
| 6535 |  |  |  |  |  |  | sub CheckImage($$) | 
| 6536 |  |  |  |  |  |  | { | 
| 6537 | 132 |  |  | 132 | 0 | 458 | my ($self, $valPtr) = @_; | 
| 6538 | 132 | 100 | 100 |  |  | 1108 | if (length($$valPtr) and $$valPtr!~/^\xff\xd8/ and not | 
|  |  |  | 100 |  |  |  |  | 
| 6539 |  |  |  |  |  |  | $self->Options('IgnoreMinorErrors')) | 
| 6540 |  |  |  |  |  |  | { | 
| 6541 | 25 |  |  |  |  | 253 | return '[Minor] Not a valid image'; | 
| 6542 |  |  |  |  |  |  | } | 
| 6543 | 107 |  |  |  |  | 921 | return undef; | 
| 6544 |  |  |  |  |  |  | } | 
| 6545 |  |  |  |  |  |  |  | 
| 6546 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6547 |  |  |  |  |  |  | # check a value for validity | 
| 6548 |  |  |  |  |  |  | # Inputs: 0) value reference, 1) format string, 2) optional count | 
| 6549 |  |  |  |  |  |  | # Returns: error string, or undef on success | 
| 6550 |  |  |  |  |  |  | # Notes: May modify value (if a count is specified for a string, it is null-padded | 
| 6551 |  |  |  |  |  |  | # to the specified length, and floating point values are rounded to integer if required) | 
| 6552 |  |  |  |  |  |  | sub CheckValue($$;$) | 
| 6553 |  |  |  |  |  |  | { | 
| 6554 | 19010 |  |  | 19010 | 0 | 47126 | my ($valPtr, $format, $count) = @_; | 
| 6555 | 19010 |  |  |  |  | 31529 | my (@vals, $val, $n); | 
| 6556 |  |  |  |  |  |  |  | 
| 6557 | 19010 | 100 | 100 |  |  | 70798 | if ($format eq 'string' or $format eq 'undef') { | 
| 6558 | 2449 | 100 | 66 |  |  | 10288 | return undef unless $count and $count > 0; | 
| 6559 | 315 |  |  |  |  | 821 | my $len = length($$valPtr); | 
| 6560 | 315 | 100 |  |  |  | 927 | if ($format eq 'string') { | 
| 6561 | 213 | 100 |  |  |  | 695 | $len >= $count and return 'String too long'; | 
| 6562 |  |  |  |  |  |  | } else { | 
| 6563 | 102 | 50 |  |  |  | 353 | $len > $count and return 'Data too long'; | 
| 6564 |  |  |  |  |  |  | } | 
| 6565 | 305 | 100 |  |  |  | 864 | if ($len < $count) { | 
| 6566 | 246 |  |  |  |  | 911 | $$valPtr .= "\0" x ($count - $len); | 
| 6567 |  |  |  |  |  |  | } | 
| 6568 | 305 |  |  |  |  | 1075 | return undef; | 
| 6569 |  |  |  |  |  |  | } | 
| 6570 | 16561 | 100 | 66 |  |  | 42905 | if ($count and $count != 1) { | 
| 6571 | 1922 |  |  |  |  | 6327 | @vals = split(' ',$$valPtr); | 
| 6572 | 1922 | 100 | 100 |  |  | 5127 | $count < 0 and ($count = @vals or return undef); | 
| 6573 |  |  |  |  |  |  | } else { | 
| 6574 | 14639 |  |  |  |  | 22786 | $count = 1; | 
| 6575 | 14639 |  |  |  |  | 33082 | @vals = ( $$valPtr ); | 
| 6576 |  |  |  |  |  |  | } | 
| 6577 | 16542 | 100 |  |  |  | 41965 | if (@vals != $count) { | 
| 6578 | 913 | 100 |  |  |  | 2611 | my $str = @vals > $count ? 'Too many' : 'Not enough'; | 
| 6579 | 913 |  |  |  |  | 3637 | return "$str values specified ($count required)"; | 
| 6580 |  |  |  |  |  |  | } | 
| 6581 | 15629 |  |  |  |  | 40159 | for ($n=0; $n<$count; ++$n) { | 
| 6582 | 18574 |  |  |  |  | 32905 | $val = shift @vals; | 
| 6583 | 18574 | 100 | 100 |  |  | 67505 | if ($format =~ /^int/) { | 
|  |  | 100 | 100 |  |  |  |  | 
| 6584 |  |  |  |  |  |  | # make sure the value is integer | 
| 6585 | 17210 | 100 |  |  |  | 52105 | unless (IsInt($val)) { | 
| 6586 | 3009 | 100 |  |  |  | 7589 | if (IsHex($val)) { | 
| 6587 | 6 |  |  |  |  | 23 | $val = $$valPtr = hex($val); | 
| 6588 |  |  |  |  |  |  | } else { | 
| 6589 |  |  |  |  |  |  | # round single floating point values to the nearest integer | 
| 6590 | 3003 | 100 | 100 |  |  | 8538 | return 'Not an integer' unless IsFloat($val) and $count == 1; | 
| 6591 | 1266 | 100 |  |  |  | 5275 | $val = $$valPtr = int($val + ($val < 0 ? -0.5 : 0.5)); | 
| 6592 |  |  |  |  |  |  | } | 
| 6593 |  |  |  |  |  |  | } | 
| 6594 | 15473 | 50 |  |  |  | 48239 | my $rng = $intRange{$format} or return "Bad int format: $format"; | 
| 6595 | 15473 | 100 |  |  |  | 38764 | return "Value below $format minimum" if $val < $$rng[0]; | 
| 6596 |  |  |  |  |  |  | # (allow 0xfeedfeed code as value for 16-bit pointers) | 
| 6597 | 15172 | 100 | 66 |  |  | 52075 | return "Value above $format maximum" if $val > $$rng[1] and $val != 0xfeedfeed; | 
| 6598 |  |  |  |  |  |  | } elsif ($format =~ /^rational/ or $format eq 'float' or $format eq 'double') { | 
| 6599 |  |  |  |  |  |  | # make sure the value is a valid floating point number | 
| 6600 | 1343 | 100 |  |  |  | 4522 | unless (IsFloat($val)) { | 
| 6601 |  |  |  |  |  |  | # allow 'inf', 'undef' and fractional rational values | 
| 6602 | 263 | 100 |  |  |  | 1061 | if ($format =~ /^rational/) { | 
| 6603 | 227 | 100 | 66 |  |  | 1081 | next if $val eq 'inf' or $val eq 'undef'; | 
| 6604 | 226 | 100 |  |  |  | 827 | if ($val =~ m{^([-+]?\d+)/(\d+)$}) { | 
| 6605 | 70 | 50 | 66 |  |  | 412 | next unless $1 < 0 and $format =~ /u$/; | 
| 6606 | 0 |  |  |  |  | 0 | return 'Must be an unsigned rational'; | 
| 6607 |  |  |  |  |  |  | } | 
| 6608 |  |  |  |  |  |  | } | 
| 6609 | 192 |  |  |  |  | 774 | return 'Not a floating point number'; | 
| 6610 |  |  |  |  |  |  | } | 
| 6611 | 1080 | 50 | 66 |  |  | 6894 | if ($format =~ /^rational\d+u$/ and $val < 0) { | 
| 6612 | 0 |  |  |  |  | 0 | return 'Must be a positive number'; | 
| 6613 |  |  |  |  |  |  | } | 
| 6614 |  |  |  |  |  |  | } | 
| 6615 |  |  |  |  |  |  | } | 
| 6616 | 13395 |  |  |  |  | 35191 | return undef;   # success! | 
| 6617 |  |  |  |  |  |  | } | 
| 6618 |  |  |  |  |  |  |  | 
| 6619 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6620 |  |  |  |  |  |  | # check new value for binary data block | 
| 6621 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) tagInfo hash ref, 2) raw value ref | 
| 6622 |  |  |  |  |  |  | # Returns: error string or undef (and may modify value) on success | 
| 6623 |  |  |  |  |  |  | sub CheckBinaryData($$$) | 
| 6624 |  |  |  |  |  |  | { | 
| 6625 | 11758 |  |  | 11758 | 0 | 26031 | my ($self, $tagInfo, $valPtr) = @_; | 
| 6626 | 11758 |  |  |  |  | 27075 | my $format = $$tagInfo{Format}; | 
| 6627 | 11758 | 100 |  |  |  | 26521 | unless ($format) { | 
| 6628 | 4400 |  |  |  |  | 8701 | my $table = $$tagInfo{Table}; | 
| 6629 | 4400 | 100 | 66 |  |  | 17484 | if ($table and $$table{FORMAT}) { | 
| 6630 | 3099 |  |  |  |  | 7215 | $format = $$table{FORMAT}; | 
| 6631 |  |  |  |  |  |  | } else { | 
| 6632 |  |  |  |  |  |  | # use default 'int8u' unless specified | 
| 6633 | 1301 |  |  |  |  | 2845 | $format = 'int8u'; | 
| 6634 |  |  |  |  |  |  | } | 
| 6635 |  |  |  |  |  |  | } | 
| 6636 | 11758 |  |  |  |  | 16797 | my $count; | 
| 6637 | 11758 | 100 |  |  |  | 35477 | if ($format =~ /(.*)\[(.*)\]/) { | 
| 6638 | 1636 |  |  |  |  | 4544 | $format = $1; | 
| 6639 | 1636 |  |  |  |  | 3453 | $count = $2; | 
| 6640 |  |  |  |  |  |  | # can't evaluate $count now because we don't know $size yet | 
| 6641 | 1636 | 50 |  |  |  | 3797 | undef $count if $count =~ /\$size/; | 
| 6642 |  |  |  |  |  |  | } | 
| 6643 | 11758 |  |  |  |  | 29159 | return CheckValue($valPtr, $format, $count); | 
| 6644 |  |  |  |  |  |  | } | 
| 6645 |  |  |  |  |  |  |  | 
| 6646 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6647 |  |  |  |  |  |  | # Rename a file (with patch for Windows Unicode file names, and other problem) | 
| 6648 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) old name, 2) new name | 
| 6649 |  |  |  |  |  |  | # Returns: true on success | 
| 6650 |  |  |  |  |  |  | sub Rename($$$) | 
| 6651 |  |  |  |  |  |  | { | 
| 6652 | 3 |  |  | 3 | 0 | 13 | my ($self, $old, $new) = @_; | 
| 6653 | 3 |  |  |  |  | 12 | my ($result, $try, $winUni); | 
| 6654 |  |  |  |  |  |  |  | 
| 6655 | 3 | 50 |  |  |  | 20 | if ($self->EncodeFileName($old)) { | 
|  |  | 50 |  |  |  |  |  | 
| 6656 | 0 |  |  |  |  | 0 | $self->EncodeFileName($new, 1); | 
| 6657 | 0 |  |  |  |  | 0 | $winUni = 1; | 
| 6658 |  |  |  |  |  |  | } elsif ($self->EncodeFileName($new)) { | 
| 6659 | 0 |  |  |  |  | 0 | $old = $_[1]; | 
| 6660 | 0 |  |  |  |  | 0 | $self->EncodeFileName($old, 1); | 
| 6661 | 0 |  |  |  |  | 0 | $winUni = 1; | 
| 6662 |  |  |  |  |  |  | } | 
| 6663 | 3 |  |  |  |  | 9 | for (;;) { | 
| 6664 | 3 | 50 |  |  |  | 11 | if ($winUni) { | 
| 6665 | 0 |  |  |  |  | 0 | $result = eval { Win32API::File::MoveFileExW($old, $new, | 
|  | 0 |  |  |  |  | 0 |  | 
| 6666 |  |  |  |  |  |  | Win32API::File::MOVEFILE_REPLACE_EXISTING() | | 
| 6667 |  |  |  |  |  |  | Win32API::File::MOVEFILE_COPY_ALLOWED()) }; | 
| 6668 |  |  |  |  |  |  | } else { | 
| 6669 | 3 |  |  |  |  | 395 | $result = rename($old, $new); | 
| 6670 |  |  |  |  |  |  | } | 
| 6671 | 3 | 50 | 33 |  |  | 27 | last if $result or $^O ne 'MSWin32'; | 
| 6672 |  |  |  |  |  |  | # keep trying for up to 0.5 seconds | 
| 6673 |  |  |  |  |  |  | # (patch for Windows denial-of-service susceptibility) | 
| 6674 | 0 |  | 0 |  |  | 0 | $try = ($try || 1) + 1; | 
| 6675 | 0 | 0 |  |  |  | 0 | last if $try > 50; | 
| 6676 | 0 |  |  |  |  | 0 | select(undef,undef,undef,0.01); # sleep for 0.01 sec | 
| 6677 |  |  |  |  |  |  | } | 
| 6678 | 3 |  |  |  |  | 19 | return $result; | 
| 6679 |  |  |  |  |  |  | } | 
| 6680 |  |  |  |  |  |  |  | 
| 6681 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6682 |  |  |  |  |  |  | # Delete a file (with patch for Windows Unicode file names) | 
| 6683 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1-N) names of files to delete | 
| 6684 |  |  |  |  |  |  | # Returns: number of files deleted | 
| 6685 |  |  |  |  |  |  | sub Unlink($@) | 
| 6686 |  |  |  |  |  |  | { | 
| 6687 | 0 |  |  | 0 | 0 | 0 | my $self = shift; | 
| 6688 | 0 |  |  |  |  | 0 | my $result = 0; | 
| 6689 | 0 |  |  |  |  | 0 | while (@_) { | 
| 6690 | 0 |  |  |  |  | 0 | my $file = shift; | 
| 6691 | 0 | 0 |  |  |  | 0 | if ($self->EncodeFileName($file)) { | 
| 6692 | 0 | 0 |  |  |  | 0 | ++$result if eval { Win32API::File::DeleteFileW($file) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 6693 |  |  |  |  |  |  | } else { | 
| 6694 | 0 | 0 |  |  |  | 0 | ++$result if unlink $file; | 
| 6695 |  |  |  |  |  |  | } | 
| 6696 |  |  |  |  |  |  | } | 
| 6697 | 0 |  |  |  |  | 0 | return $result; | 
| 6698 |  |  |  |  |  |  | } | 
| 6699 |  |  |  |  |  |  |  | 
| 6700 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6701 |  |  |  |  |  |  | # Set file times (Unix seconds since the epoch) | 
| 6702 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) file name or ref, 2) access time, 3) modification time, | 
| 6703 |  |  |  |  |  |  | #         4) inode change or creation time (or undef for any time to avoid setting) | 
| 6704 |  |  |  |  |  |  | #         5) flag to suppress warning | 
| 6705 |  |  |  |  |  |  | # Returns: 1 on success, 0 on error | 
| 6706 |  |  |  |  |  |  | my $k32SetFileTime; | 
| 6707 |  |  |  |  |  |  | sub SetFileTime($$;$$$$) | 
| 6708 |  |  |  |  |  |  | { | 
| 6709 | 0 |  |  | 0 | 0 | 0 | my ($self, $file, $atime, $mtime, $ctime, $noWarn) = @_; | 
| 6710 | 0 |  |  |  |  | 0 | my $saveFile; | 
| 6711 | 0 |  |  |  |  | 0 | local *FH; | 
| 6712 |  |  |  |  |  |  |  | 
| 6713 |  |  |  |  |  |  | # open file by name if necessary | 
| 6714 | 0 | 0 |  |  |  | 0 | unless (ref $file) { | 
| 6715 |  |  |  |  |  |  | # (file will be automatically closed when *FH goes out of scope) | 
| 6716 | 0 | 0 |  |  |  | 0 | unless ($self->Open(\*FH, $file, '+<')) { | 
| 6717 | 0 |  |  |  |  | 0 | my $success; | 
| 6718 | 0 | 0 | 0 |  |  | 0 | if (defined $atime or defined $mtime) { | 
| 6719 | 0 |  |  |  |  | 0 | my ($a, $m, $c) = $self->GetFileTime($file); | 
| 6720 | 0 | 0 |  |  |  | 0 | $atime = $a unless defined $atime; | 
| 6721 | 0 | 0 |  |  |  | 0 | $mtime = $m unless defined $mtime; | 
| 6722 | 0 | 0 | 0 |  |  | 0 | $success = eval { utime($atime, $mtime, $file) } if defined $atime and defined $mtime; | 
|  | 0 |  |  |  |  | 0 |  | 
| 6723 |  |  |  |  |  |  | } | 
| 6724 | 0 | 0 |  |  |  | 0 | $self->Warn('Error opening file for update') unless $success; | 
| 6725 | 0 |  |  |  |  | 0 | return $success; | 
| 6726 |  |  |  |  |  |  | } | 
| 6727 | 0 |  |  |  |  | 0 | $saveFile = $file; | 
| 6728 | 0 |  |  |  |  | 0 | $file = \*FH; | 
| 6729 |  |  |  |  |  |  | } | 
| 6730 |  |  |  |  |  |  | # on Windows, try to work around incorrect file times when daylight saving time is in effect | 
| 6731 | 0 | 0 |  |  |  | 0 | if ($^O eq 'MSWin32') { | 
| 6732 | 0 | 0 |  |  |  | 0 | if (not eval { require Win32::API }) { | 
|  | 0 | 0 |  |  |  | 0 |  | 
| 6733 | 0 |  |  |  |  | 0 | $self->WarnOnce('Install Win32::API for proper handling of Windows file times'); | 
| 6734 | 0 |  |  |  |  | 0 | } elsif (not eval { require Win32API::File }) { | 
| 6735 | 0 |  |  |  |  | 0 | $self->WarnOnce('Install Win32API::File for proper handling of Windows file times'); | 
| 6736 |  |  |  |  |  |  | } else { | 
| 6737 |  |  |  |  |  |  | # get Win32 handle, needed for SetFileTime | 
| 6738 | 0 |  |  |  |  | 0 | my $win32Handle = eval { Win32API::File::GetOsFHandle($file) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 6739 | 0 | 0 |  |  |  | 0 | unless ($win32Handle) { | 
| 6740 | 0 |  |  |  |  | 0 | $self->Warn('Win32API::File::GetOsFHandle returned invalid handle'); | 
| 6741 | 0 |  |  |  |  | 0 | return 0; | 
| 6742 |  |  |  |  |  |  | } | 
| 6743 |  |  |  |  |  |  | # convert Unix seconds to FILETIME structs | 
| 6744 | 0 |  |  |  |  | 0 | my $time; | 
| 6745 | 0 |  |  |  |  | 0 | foreach $time ($atime, $mtime, $ctime) { | 
| 6746 |  |  |  |  |  |  | # set to NULL if not defined (i.e. do not change) | 
| 6747 | 0 | 0 |  |  |  | 0 | defined $time or $time = 0, next; | 
| 6748 |  |  |  |  |  |  | # convert to 100 ns intervals since 0:00 UTC Jan 1, 1601 | 
| 6749 |  |  |  |  |  |  | # (89 leap years between 1601 and 1970) | 
| 6750 | 0 |  |  |  |  | 0 | my $wt = ($time + (((1970-1601)*365+89)*24*3600)) * 1e7; | 
| 6751 | 0 |  |  |  |  | 0 | my $hi = int($wt / 4294967296); | 
| 6752 | 0 |  |  |  |  | 0 | $time = pack 'LL', int($wt - $hi * 4294967296), $hi; # pack FILETIME struct | 
| 6753 |  |  |  |  |  |  | } | 
| 6754 | 0 | 0 |  |  |  | 0 | unless ($k32SetFileTime) { | 
| 6755 | 0 | 0 |  |  |  | 0 | return 0 if defined $k32SetFileTime; | 
| 6756 | 0 |  |  |  |  | 0 | $k32SetFileTime = new Win32::API('KERNEL32', 'SetFileTime', 'NPPP', 'I'); | 
| 6757 | 0 | 0 |  |  |  | 0 | unless ($k32SetFileTime) { | 
| 6758 | 0 |  |  |  |  | 0 | $self->Warn('Error calling Win32::API::SetFileTime'); | 
| 6759 | 0 |  |  |  |  | 0 | $k32SetFileTime = 0; | 
| 6760 | 0 |  |  |  |  | 0 | return 0; | 
| 6761 |  |  |  |  |  |  | } | 
| 6762 |  |  |  |  |  |  | } | 
| 6763 | 0 | 0 |  |  |  | 0 | unless ($k32SetFileTime->Call($win32Handle, $ctime, $atime, $mtime)) { | 
| 6764 | 0 |  |  |  |  | 0 | $self->Warn('Win32::API::SetFileTime returned ' . Win32::GetLastError()); | 
| 6765 | 0 |  |  |  |  | 0 | return 0; | 
| 6766 |  |  |  |  |  |  | } | 
| 6767 | 0 |  |  |  |  | 0 | return 1; | 
| 6768 |  |  |  |  |  |  | } | 
| 6769 |  |  |  |  |  |  | } | 
| 6770 |  |  |  |  |  |  | # other OS (or Windows fallback) | 
| 6771 | 0 | 0 | 0 |  |  | 0 | if (defined $atime and defined $mtime) { | 
| 6772 | 0 |  |  |  |  | 0 | my $success; | 
| 6773 | 0 |  |  |  |  | 0 | local $SIG{'__WARN__'} = \&SetWarning; # (this may not be necessary) | 
| 6774 | 0 |  |  |  |  | 0 | for (;;) { | 
| 6775 | 0 |  |  |  |  | 0 | undef $evalWarning; | 
| 6776 |  |  |  |  |  |  | # (this may fail on the first try if futimes is not implemented) | 
| 6777 | 0 |  |  |  |  | 0 | $success = eval { utime($atime, $mtime, $file) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 6778 | 0 | 0 | 0 |  |  | 0 | last if $success or not defined $saveFile; | 
| 6779 | 0 |  |  |  |  | 0 | close $file; | 
| 6780 | 0 |  |  |  |  | 0 | $file = $saveFile; | 
| 6781 | 0 |  |  |  |  | 0 | undef $saveFile; | 
| 6782 |  |  |  |  |  |  | } | 
| 6783 | 0 | 0 |  |  |  | 0 | unless ($noWarn) { | 
| 6784 | 0 | 0 | 0 |  |  | 0 | if ($@ or $evalWarning) { | 
|  |  | 0 |  |  |  |  |  | 
| 6785 | 0 |  | 0 |  |  | 0 | $self->Warn(CleanWarning($@ || $evalWarning)); | 
| 6786 |  |  |  |  |  |  | } elsif (not $success) { | 
| 6787 | 0 |  |  |  |  | 0 | $self->Warn('Error setting file time'); | 
| 6788 |  |  |  |  |  |  | } | 
| 6789 |  |  |  |  |  |  | } | 
| 6790 | 0 |  |  |  |  | 0 | return $success; | 
| 6791 |  |  |  |  |  |  | } | 
| 6792 | 0 |  |  |  |  | 0 | return 1; # (nothing to do) | 
| 6793 |  |  |  |  |  |  | } | 
| 6794 |  |  |  |  |  |  |  | 
| 6795 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6796 |  |  |  |  |  |  | # Copy data block from RAF to output file in max 64kB chunks | 
| 6797 |  |  |  |  |  |  | # Inputs: 0) RAF ref, 1) outfile ref, 2) block size | 
| 6798 |  |  |  |  |  |  | # Returns: 1 on success, 0 on read error, undef on write error | 
| 6799 |  |  |  |  |  |  | sub CopyBlock($$$) | 
| 6800 |  |  |  |  |  |  | { | 
| 6801 | 69 |  |  | 69 | 0 | 225 | my ($raf, $outfile, $size) = @_; | 
| 6802 | 69 |  |  |  |  | 148 | my $buff; | 
| 6803 | 69 |  |  |  |  | 132 | for (;;) { | 
| 6804 | 122 | 100 |  |  |  | 364 | last unless $size > 0; | 
| 6805 | 53 | 50 |  |  |  | 186 | my $n = $size > 65536 ? 65536 : $size; | 
| 6806 | 53 | 50 |  |  |  | 199 | $raf->Read($buff, $n) == $n or return 0; | 
| 6807 | 53 | 50 |  |  |  | 949 | Write($outfile, $buff) or return undef; | 
| 6808 | 53 |  |  |  |  | 180 | $size -= $n; | 
| 6809 |  |  |  |  |  |  | } | 
| 6810 | 69 |  |  |  |  | 235 | return 1; | 
| 6811 |  |  |  |  |  |  | } | 
| 6812 |  |  |  |  |  |  |  | 
| 6813 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6814 |  |  |  |  |  |  | # Copy image data from one file to another | 
| 6815 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 6816 |  |  |  |  |  |  | #         1) reference to list of image data [ position, size, pad bytes ] | 
| 6817 |  |  |  |  |  |  | #         2) output file ref | 
| 6818 |  |  |  |  |  |  | # Returns: true on success | 
| 6819 |  |  |  |  |  |  | sub CopyImageData($$$) | 
| 6820 |  |  |  |  |  |  | { | 
| 6821 | 13 |  |  | 13 | 0 | 55 | my ($self, $imageDataBlocks, $outfile) = @_; | 
| 6822 | 13 |  |  |  |  | 47 | my $raf = $$self{RAF}; | 
| 6823 | 13 |  |  |  |  | 29 | my ($dataBlock, $err); | 
| 6824 | 13 |  |  |  |  | 38 | my $num = @$imageDataBlocks; | 
| 6825 | 13 | 50 |  |  |  | 133 | $self->VPrint(0, "  Copying $num image data blocks\n") if $num; | 
| 6826 | 13 |  |  |  |  | 47 | foreach $dataBlock (@$imageDataBlocks) { | 
| 6827 | 24 |  |  |  |  | 71 | my ($pos, $size, $pad) = @$dataBlock; | 
| 6828 | 24 | 50 |  |  |  | 96 | $raf->Seek($pos, 0) or $err = 'read', last; | 
| 6829 | 24 |  |  |  |  | 135 | my $result = CopyBlock($raf, $outfile, $size); | 
| 6830 | 24 | 0 |  |  |  | 73 | $result or $err = defined $result ? 'read' : 'writ'; | 
|  |  | 50 |  |  |  |  |  | 
| 6831 |  |  |  |  |  |  | # pad if necessary | 
| 6832 | 24 | 100 | 50 |  |  | 79 | Write($outfile, "\0" x $pad) or $err = 'writ' if $pad; | 
| 6833 | 24 | 50 |  |  |  | 82 | last if $err; | 
| 6834 |  |  |  |  |  |  | } | 
| 6835 | 13 | 50 |  |  |  | 65 | if ($err) { | 
| 6836 | 0 |  |  |  |  | 0 | $self->Error("Error ${err}ing image data"); | 
| 6837 | 0 |  |  |  |  | 0 | return 0; | 
| 6838 |  |  |  |  |  |  | } | 
| 6839 | 13 |  |  |  |  | 59 | return 1; | 
| 6840 |  |  |  |  |  |  | } | 
| 6841 |  |  |  |  |  |  |  | 
| 6842 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6843 |  |  |  |  |  |  | # Write to binary data block | 
| 6844 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref | 
| 6845 |  |  |  |  |  |  | # Returns: Binary data block or undefined on error | 
| 6846 |  |  |  |  |  |  | sub WriteBinaryData($$$) | 
| 6847 |  |  |  |  |  |  | { | 
| 6848 | 14789 |  |  | 14789 | 0 | 28501 | my ($self, $dirInfo, $tagTablePtr) = @_; | 
| 6849 | 14789 | 100 |  |  |  | 51682 | $self or return 1;    # allow dummy access to autoload this package | 
| 6850 |  |  |  |  |  |  |  | 
| 6851 |  |  |  |  |  |  | # get default format ('int8u' unless specified) | 
| 6852 | 450 | 50 |  |  |  | 1254 | my $dataPt = $$dirInfo{DataPt} or return undef; | 
| 6853 | 450 |  | 100 |  |  | 1685 | my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u'; | 
| 6854 | 450 |  |  |  |  | 1352 | my $increment = FormatSize($defaultFormat); | 
| 6855 | 450 | 50 |  |  |  | 1159 | unless ($increment) { | 
| 6856 | 0 |  |  |  |  | 0 | warn "Unknown format $defaultFormat\n"; | 
| 6857 | 0 |  |  |  |  | 0 | return undef; | 
| 6858 |  |  |  |  |  |  | } | 
| 6859 |  |  |  |  |  |  | # extract data members first if necessary | 
| 6860 | 450 |  |  |  |  | 739 | my @varOffsets; | 
| 6861 | 450 | 100 |  |  |  | 1330 | if ($$tagTablePtr{DATAMEMBER}) { | 
| 6862 | 192 |  |  |  |  | 485 | $$dirInfo{DataMember} = $$tagTablePtr{DATAMEMBER}; | 
| 6863 | 192 |  |  |  |  | 496 | $$dirInfo{VarFormatData} = \@varOffsets; | 
| 6864 | 192 |  |  |  |  | 878 | $self->ProcessBinaryData($dirInfo, $tagTablePtr); | 
| 6865 | 192 |  |  |  |  | 478 | delete $$dirInfo{DataMember}; | 
| 6866 | 192 |  |  |  |  | 410 | delete $$dirInfo{VarFormatData}; | 
| 6867 |  |  |  |  |  |  | } | 
| 6868 | 450 |  | 100 |  |  | 1575 | my $dirStart = $$dirInfo{DirStart} || 0; | 
| 6869 | 450 |  | 66 |  |  | 1549 | my $dirLen = $$dirInfo{DirLen} || length($$dataPt) - $dirStart; | 
| 6870 | 450 | 50 |  |  |  | 1732 | my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef; | 
| 6871 | 450 |  |  |  |  | 949 | my $dirName = $$dirInfo{DirName}; | 
| 6872 | 450 |  |  |  |  | 775 | my $varSize = 0; | 
| 6873 | 450 |  |  |  |  | 924 | my @varInfo = @varOffsets; | 
| 6874 | 450 |  |  |  |  | 708 | my $tagInfo; | 
| 6875 | 450 |  |  |  |  | 834 | $dataPt = \$newData; | 
| 6876 | 450 |  |  |  |  | 1421 | foreach $tagInfo (sort { $$a{TagID} <=> $$b{TagID} } $self->GetNewTagInfoList($tagTablePtr)) { | 
|  | 645 |  |  |  |  | 1124 |  | 
| 6877 | 227 |  |  |  |  | 493 | my $tagID = $$tagInfo{TagID}; | 
| 6878 |  |  |  |  |  |  | # evaluate conditional tags now if necessary | 
| 6879 | 227 | 100 | 100 |  |  | 1198 | if (ref $$tagTablePtr{$tagID} eq 'ARRAY' or $$tagInfo{Condition}) { | 
| 6880 | 22 |  |  |  |  | 85 | my $writeInfo = $self->GetTagInfo($tagTablePtr, $tagID); | 
| 6881 | 22 | 100 | 100 |  |  | 162 | next unless $writeInfo and $writeInfo eq $tagInfo; | 
| 6882 |  |  |  |  |  |  | } | 
| 6883 |  |  |  |  |  |  | # add offsets for variable-sized tags if necessary | 
| 6884 | 218 |  | 100 |  |  | 680 | while (@varInfo and $varInfo[0][0] < $tagID) { | 
| 6885 | 10 |  |  |  |  | 22 | $varSize = $varInfo[0][1];  # get accumulated variable size | 
| 6886 | 10 |  |  |  |  | 31 | shift @varInfo; | 
| 6887 |  |  |  |  |  |  | } | 
| 6888 | 218 |  |  |  |  | 406 | my $count = 1; | 
| 6889 | 218 |  |  |  |  | 437 | my $format = $$tagInfo{Format}; | 
| 6890 | 218 |  |  |  |  | 457 | my $entry = int($tagID) * $increment + $varSize; # relative offset of this entry | 
| 6891 | 218 | 100 |  |  |  | 510 | if ($format) { | 
| 6892 | 87 | 100 |  |  |  | 432 | if ($format =~ /(.*)\[(.*)\]/) { | 
|  |  | 100 |  |  |  |  |  | 
| 6893 | 36 |  |  |  |  | 112 | $format = $1; | 
| 6894 | 36 |  |  |  |  | 86 | $count = $2; | 
| 6895 | 36 |  |  |  |  | 66 | my $size = $dirLen; # used in eval | 
| 6896 |  |  |  |  |  |  | # evaluate count to allow count to be based on previous values | 
| 6897 |  |  |  |  |  |  | #### eval Format size ($size, $self) - NOTE: %val not supported for writing | 
| 6898 | 36 |  |  |  |  | 1685 | $count = eval $count; | 
| 6899 | 36 | 50 |  |  |  | 178 | $@ and warn($@), next; | 
| 6900 |  |  |  |  |  |  | } elsif ($format eq 'string') { | 
| 6901 |  |  |  |  |  |  | # string with no specified count runs to end of block | 
| 6902 | 1 | 50 |  |  |  | 5 | $count = ($dirLen > $entry) ? $dirLen - $entry : 0; | 
| 6903 |  |  |  |  |  |  | } | 
| 6904 |  |  |  |  |  |  | } else { | 
| 6905 | 131 |  |  |  |  | 217 | $format = $defaultFormat; | 
| 6906 |  |  |  |  |  |  | } | 
| 6907 |  |  |  |  |  |  | # read/write using variable format if changed in Hook | 
| 6908 | 218 | 100 | 66 |  |  | 642 | $format = $varInfo[0][2] if @varInfo and $varInfo[0][0] == $tagID; | 
| 6909 | 218 |  |  |  |  | 755 | my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen-$entry); | 
| 6910 | 218 | 100 |  |  |  | 558 | next unless defined $val; | 
| 6911 | 215 |  |  |  |  | 959 | my $nvHash = $self->GetNewValueHash($tagInfo, $$self{CUR_WRITE_GROUP}); | 
| 6912 | 215 | 100 |  |  |  | 728 | next unless $self->IsOverwriting($nvHash, $val) > 0; | 
| 6913 | 214 |  |  |  |  | 571 | my $newVal = $self->GetNewValue($nvHash); | 
| 6914 | 214 | 100 |  |  |  | 534 | next unless defined $newVal;    # can't delete from a binary table | 
| 6915 |  |  |  |  |  |  | # update DataMember with new value if necessary | 
| 6916 | 213 | 100 |  |  |  | 562 | $$self{$$tagInfo{DataMember}} = $newVal if $$tagInfo{DataMember}; | 
| 6917 |  |  |  |  |  |  | # only write masked bits if specified | 
| 6918 | 213 |  |  |  |  | 443 | my $mask = $$tagInfo{Mask}; | 
| 6919 | 213 | 100 |  |  |  | 461 | $newVal = (($newVal << $$tagInfo{BitShift}) & $mask) | ($val & ~$mask) if $mask; | 
| 6920 |  |  |  |  |  |  | # set the size | 
| 6921 | 213 | 50 | 33 |  |  | 584 | if ($$tagInfo{DataTag} and not $$tagInfo{IsOffset}) { | 
| 6922 | 0 | 0 |  |  |  | 0 | warn 'Internal error' unless $newVal == 0xfeedfeed; | 
| 6923 | 0 |  |  |  |  | 0 | my $data = $self->GetNewValue($$tagInfo{DataTag}); | 
| 6924 | 0 | 0 |  |  |  | 0 | $newVal = length($data) if defined $data; | 
| 6925 | 0 |  | 0 |  |  | 0 | my $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT} || 'int32u'; | 
| 6926 | 0 | 0 | 0 |  |  | 0 | if ($format =~ /^int16/ and $newVal > 0xffff) { | 
| 6927 | 0 |  |  |  |  | 0 | $self->Error("$$tagInfo{DataTag} is too large (64 kB max. for this file)"); | 
| 6928 |  |  |  |  |  |  | } | 
| 6929 |  |  |  |  |  |  | } | 
| 6930 | 213 |  |  |  |  | 575 | my $rtnVal = WriteValue($newVal, $format, $count, $dataPt, $entry); | 
| 6931 | 213 | 50 |  |  |  | 499 | if (defined $rtnVal) { | 
| 6932 | 213 |  |  |  |  | 1207 | $self->VerboseValue("- $dirName:$$tagInfo{Name}", $val); | 
| 6933 | 213 |  |  |  |  | 719 | $self->VerboseValue("+ $dirName:$$tagInfo{Name}", $newVal); | 
| 6934 | 213 |  |  |  |  | 585 | ++$$self{CHANGED}; | 
| 6935 |  |  |  |  |  |  | } | 
| 6936 |  |  |  |  |  |  | } | 
| 6937 |  |  |  |  |  |  | # add necessary fixups for any offsets | 
| 6938 | 450 | 50 | 66 |  |  | 1455 | if ($$tagTablePtr{IS_OFFSET} and $$dirInfo{Fixup}) { | 
| 6939 | 1 |  |  |  |  | 2 | $varSize = 0; | 
| 6940 | 1 |  |  |  |  | 3 | @varInfo = @varOffsets; | 
| 6941 | 1 |  |  |  |  | 2 | my $fixup = $$dirInfo{Fixup}; | 
| 6942 | 1 |  |  |  |  | 2 | my $tagID; | 
| 6943 | 1 |  |  |  |  | 2 | foreach $tagID (@{$$tagTablePtr{IS_OFFSET}}) { | 
|  | 1 |  |  |  |  | 4 |  | 
| 6944 | 1 | 50 |  |  |  | 4 | $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID) or next; | 
| 6945 | 1 |  | 33 |  |  | 6 | while (@varInfo and $varInfo[0][0] < $tagID) { | 
| 6946 | 0 |  |  |  |  | 0 | $varSize = $varInfo[0][1]; | 
| 6947 | 0 |  |  |  |  | 0 | shift @varInfo; | 
| 6948 |  |  |  |  |  |  | } | 
| 6949 | 1 |  |  |  |  | 2 | my $entry = $tagID * $increment + $varSize; # (no offset to dirStart for new dir data) | 
| 6950 | 1 | 50 |  |  |  | 19 | next unless $entry <= $dirLen - 4; | 
| 6951 |  |  |  |  |  |  | # (Ricoh has 16-bit preview image offsets, so can't just assume int32u) | 
| 6952 | 0 |  | 0 |  |  | 0 | my $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT} || 'int32u'; | 
| 6953 | 0 |  |  |  |  | 0 | my $offset = ReadValue($dataPt, $entry, $format, 1, $dirLen-$entry); | 
| 6954 |  |  |  |  |  |  | # ignore if offset is zero (eg. Ricoh DNG uses this to indicate no preview) | 
| 6955 | 0 | 0 |  |  |  | 0 | next unless $offset; | 
| 6956 | 0 |  |  |  |  | 0 | $fixup->AddFixup($entry, $$tagInfo{DataTag}, $format); | 
| 6957 |  |  |  |  |  |  | # handle the preview image now if this is a JPEG file | 
| 6958 |  |  |  |  |  |  | next unless $$self{FILE_TYPE} eq 'JPEG' and $$tagInfo{DataTag} and | 
| 6959 | 0 | 0 | 0 |  |  | 0 | $$tagInfo{DataTag} eq 'PreviewImage' and defined $$tagInfo{OffsetPair}; | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 6960 |  |  |  |  |  |  | # NOTE: here we assume there are no var-sized tags between the | 
| 6961 |  |  |  |  |  |  | # OffsetPair tags.  If this ever becomes possible we must recalculate | 
| 6962 |  |  |  |  |  |  | # $varSize for the OffsetPair tag here! | 
| 6963 | 0 |  |  |  |  | 0 | $entry = $$tagInfo{OffsetPair} * $increment + $varSize; | 
| 6964 | 0 |  |  |  |  | 0 | my $size = ReadValue($dataPt, $entry, $format, 1, $dirLen-$entry); | 
| 6965 | 0 |  |  |  |  | 0 | my $previewInfo = $$self{PREVIEW_INFO}; | 
| 6966 |  |  |  |  |  |  | $previewInfo or $previewInfo = $$self{PREVIEW_INFO} = { | 
| 6967 | 0 | 0 |  |  |  | 0 | Fixup => new Image::ExifTool::Fixup, | 
| 6968 |  |  |  |  |  |  | }; | 
| 6969 |  |  |  |  |  |  | # set flag indicating we are using short pointers | 
| 6970 | 0 | 0 |  |  |  | 0 | $$previewInfo{IsShort} = 1 unless $format eq 'int32u'; | 
| 6971 | 0 | 0 | 0 |  |  | 0 | $$previewInfo{Absolute} = 1 if $$tagInfo{IsOffset} and $$tagInfo{IsOffset} eq '3'; | 
| 6972 |  |  |  |  |  |  | # get the value of the Composite::PreviewImage tag | 
| 6973 | 0 |  |  |  |  | 0 | $$previewInfo{Data} = $self->GetNewValue(GetCompositeTagInfo('PreviewImage')); | 
| 6974 | 0 | 0 |  |  |  | 0 | unless (defined $$previewInfo{Data}) { | 
| 6975 | 0 | 0 | 0 |  |  | 0 | if ($offset >= 0 and $offset + $size <= $$dirInfo{DataLen}) { | 
| 6976 | 0 |  |  |  |  | 0 | $$previewInfo{Data} = substr(${$$dirInfo{DataPt}},$offset,$size); | 
|  | 0 |  |  |  |  | 0 |  | 
| 6977 |  |  |  |  |  |  | } else { | 
| 6978 | 0 |  |  |  |  | 0 | $$previewInfo{Data} = 'LOAD_PREVIEW'; # flag to load preview later | 
| 6979 |  |  |  |  |  |  | } | 
| 6980 |  |  |  |  |  |  | } | 
| 6981 |  |  |  |  |  |  | } | 
| 6982 |  |  |  |  |  |  | } | 
| 6983 |  |  |  |  |  |  | # write any necessary SubDirectories | 
| 6984 | 450 | 100 |  |  |  | 1278 | if ($$tagTablePtr{IS_SUBDIR}) { | 
| 6985 | 12 |  |  |  |  | 47 | $varSize = 0; | 
| 6986 | 12 |  |  |  |  | 49 | @varInfo = @varOffsets; | 
| 6987 | 12 |  |  |  |  | 27 | my $tagID; | 
| 6988 | 12 |  |  |  |  | 33 | foreach $tagID (@{$$tagTablePtr{IS_SUBDIR}}) { | 
|  | 12 |  |  |  |  | 47 |  | 
| 6989 | 13 |  |  |  |  | 61 | my $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID); | 
| 6990 | 13 | 100 |  |  |  | 77 | next unless defined $tagInfo; | 
| 6991 | 4 |  | 33 |  |  | 23 | while (@varInfo and $varInfo[0][0] < $tagID) { | 
| 6992 | 0 |  |  |  |  | 0 | $varSize = $varInfo[0][1]; | 
| 6993 | 0 |  |  |  |  | 0 | shift @varInfo; | 
| 6994 |  |  |  |  |  |  | } | 
| 6995 | 4 |  |  |  |  | 13 | my $entry = int($tagID) * $increment + $varSize; | 
| 6996 | 4 | 50 |  |  |  | 15 | last if $entry >= $dirLen; | 
| 6997 |  |  |  |  |  |  | # get value for Condition if necessary | 
| 6998 | 4 | 50 |  |  |  | 17 | unless ($tagInfo) { | 
| 6999 | 0 |  |  |  |  | 0 | my $more = $dirLen - $entry; | 
| 7000 | 0 | 0 |  |  |  | 0 | $more = 128 if $more > 128; | 
| 7001 | 0 |  |  |  |  | 0 | my $v = substr($newData, $entry, $more); | 
| 7002 | 0 |  |  |  |  | 0 | $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID, \$v); | 
| 7003 | 0 | 0 |  |  |  | 0 | next unless $tagInfo; | 
| 7004 |  |  |  |  |  |  | } | 
| 7005 | 4 | 50 |  |  |  | 17 | next unless $$tagInfo{SubDirectory}; # (just to be safe) | 
| 7006 | 4 |  |  |  |  | 22 | my %subdirInfo = ( DataPt => \$newData, DirStart => $entry ); | 
| 7007 | 4 |  |  |  |  | 17 | my $subTablePtr = GetTagTable($$tagInfo{SubDirectory}{TagTable}); | 
| 7008 | 4 |  |  |  |  | 37 | my $dat = $self->WriteDirectory(\%subdirInfo, $subTablePtr); | 
| 7009 | 4 | 50 | 33 |  |  | 43 | substr($newData, $entry) = $dat if defined $dat and length $dat; | 
| 7010 |  |  |  |  |  |  | } | 
| 7011 |  |  |  |  |  |  | } | 
| 7012 | 450 |  |  |  |  | 1595 | return $newData; | 
| 7013 |  |  |  |  |  |  | } | 
| 7014 |  |  |  |  |  |  |  | 
| 7015 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 7016 |  |  |  |  |  |  | # Write TIFF as a directory | 
| 7017 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref | 
| 7018 |  |  |  |  |  |  | # Returns: New directory data or undefined on error | 
| 7019 |  |  |  |  |  |  | sub WriteTIFF($$$) | 
| 7020 |  |  |  |  |  |  | { | 
| 7021 | 111 |  |  | 111 | 0 | 441 | my ($self, $dirInfo, $tagTablePtr) = @_; | 
| 7022 | 111 | 50 |  |  |  | 448 | $self or return 1;    # allow dummy access | 
| 7023 | 111 |  |  |  |  | 363 | my $buff = ''; | 
| 7024 | 111 |  |  |  |  | 411 | $$dirInfo{OutFile} = \$buff; | 
| 7025 | 111 | 50 |  |  |  | 678 | return $buff if $self->ProcessTIFF($dirInfo, $tagTablePtr) > 0; | 
| 7026 | 0 |  |  |  |  |  | return undef; | 
| 7027 |  |  |  |  |  |  | } | 
| 7028 |  |  |  |  |  |  |  | 
| 7029 |  |  |  |  |  |  | 1; # end | 
| 7030 |  |  |  |  |  |  |  | 
| 7031 |  |  |  |  |  |  | __END__ |