| 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 | 59 |  |  | 59 |  | 458 | use strict; | 
|  | 59 |  |  |  |  | 129 |  | 
|  | 59 |  |  |  |  | 2690 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 59 |  |  | 59 |  | 175316 | use Image::ExifTool::TagLookup qw(FindTagInfo TagExists); | 
|  | 59 |  |  |  |  | 10335 |  | 
|  | 59 |  |  |  |  | 23128 |  | 
| 18 | 59 |  |  | 59 |  | 42970 | use Image::ExifTool::Fixup; | 
|  | 59 |  |  |  |  | 446 |  | 
|  | 59 |  |  |  |  | 138582 |  | 
| 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 JXL 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 | 5679 |  |  | 5679 | 1 | 47876 | local $_; | 
| 308 | 5679 |  |  |  |  | 21460 | my ($self, $tag, $value, %options) = @_; | 
| 309 | 5679 |  |  |  |  | 10343 | my ($err, $tagInfo, $family); | 
| 310 | 5679 |  |  |  |  | 15371 | my $verbose = $$self{OPTIONS}{Verbose}; | 
| 311 | 5679 |  |  |  |  | 11644 | my $out = $$self{OPTIONS}{TextOut}; | 
| 312 | 5679 |  | 100 |  |  | 19913 | my $protected = $options{Protected} || 0; | 
| 313 | 5679 |  |  |  |  | 9878 | my $listOnly = $options{ListOnly}; | 
| 314 | 5679 |  |  |  |  | 9637 | my $setTags = $options{SetTags}; | 
| 315 | 5679 |  |  |  |  | 9216 | my $noFlat = $options{NoFlat}; | 
| 316 | 5679 |  |  |  |  | 8821 | my $numSet = 0; | 
| 317 |  |  |  |  |  |  |  | 
| 318 | 5679 | 100 |  |  |  | 12304 | unless (defined $tag) { | 
| 319 | 40 |  |  |  |  | 949 | delete $$self{NEW_VALUE}; | 
| 320 | 40 |  |  |  |  | 123 | $$self{SAVE_COUNT} = 0; | 
| 321 | 40 |  |  |  |  | 182 | $$self{DEL_GROUP} = { }; | 
| 322 | 40 |  |  |  |  | 177 | return 1; | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  | # allow value to be scalar or list reference | 
| 325 | 5639 | 100 |  |  |  | 13416 | if (ref $value) { | 
| 326 | 218 | 100 |  |  |  | 1563 | 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 |  |  |  | 365 | if (@$value > 1) { | 
| 330 |  |  |  |  |  |  | # set all list-type tags first | 
| 331 | 51 |  |  |  |  | 139 | my $replace = $options{Replace}; | 
| 332 | 51 |  |  |  |  | 107 | my $noJoin; | 
| 333 | 51 |  |  |  |  | 166 | foreach (@$value) { | 
| 334 | 153 | 100 |  |  |  | 401 | $noJoin = 1 if ref $_; | 
| 335 | 153 |  |  |  |  | 913 | my ($n, $e) = SetNewValue($self, $tag, $_, %options, ListOnly => 1); | 
| 336 | 153 | 100 |  |  |  | 456 | $err = $e if $e; | 
| 337 | 153 |  |  |  |  | 286 | $numSet += $n; | 
| 338 | 153 |  |  |  |  | 480 | delete $options{Replace}; # don't replace earlier values in list | 
| 339 |  |  |  |  |  |  | } | 
| 340 | 51 | 100 |  |  |  | 290 | return $numSet if $noJoin;  # don't join if list contains objects | 
| 341 |  |  |  |  |  |  | # and now set only non-list tags | 
| 342 | 50 |  |  |  |  | 280 | $value = join $$self{OPTIONS}{ListSep}, @$value; | 
| 343 | 50 |  |  |  |  | 153 | $options{Replace} = $replace; | 
| 344 | 50 |  |  |  |  | 159 | $listOnly = $options{ListOnly} = 0; | 
| 345 |  |  |  |  |  |  | } else { | 
| 346 | 27 |  |  |  |  | 104 | $value = $$value[0]; | 
| 347 | 27 | 50 |  |  |  | 130 | $value = $$value if ref $value eq 'SCALAR'; # (handle single scalar ref in a list) | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  | } elsif (ref $value eq 'SCALAR') { | 
| 350 | 127 |  |  |  |  | 469 | $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 | 5638 | 100 | 100 |  |  | 40354 | $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 | 5638 | 100 |  |  |  | 20607 | ($options{Group}, $tag) = ($1, $2) if $tag =~ /(.*):(.+)/; | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | # allow trailing '#' for ValueConv value | 
| 361 | 5638 | 100 |  |  |  | 15684 | $options{Type} = 'ValueConv' if $tag =~ s/#$//; | 
| 362 | 5638 |  | 66 |  |  | 25322 | my $convType = $options{Type} || ($$self{OPTIONS}{PrintConv} ? 'PrintConv' : 'ValueConv'); | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | # filter value if necessary | 
| 365 | 5638 | 100 | 50 |  |  | 26636 | $self->Filter($$self{OPTIONS}{FilterW}, \$value) or return 0 if $convType eq 'PrintConv'; | 
| 366 |  |  |  |  |  |  |  | 
| 367 | 5638 |  |  |  |  | 10539 | my (@wantGroup, $family2); | 
| 368 | 5638 |  |  |  |  | 11036 | my $wantGroup = $options{Group}; | 
| 369 | 5638 | 100 |  |  |  | 12300 | if ($wantGroup) { | 
| 370 | 2414 |  |  |  |  | 7801 | foreach (split /:/, $wantGroup) { | 
| 371 | 2440 | 50 | 33 |  |  | 17387 | next unless length($_) and /^(\d+)?(.*)/; # separate family number and group name | 
| 372 | 2440 |  |  |  |  | 8329 | my ($f, $g) = ($1, $2); | 
| 373 | 2440 |  |  |  |  | 5088 | my $lcg = lc $g; | 
| 374 |  |  |  |  |  |  | # save group/family unless '*' or 'all' | 
| 375 | 2440 | 100 | 66 |  |  | 12828 | push @wantGroup, [ $f, $lcg ] unless $lcg eq '*' or $lcg eq 'all'; | 
| 376 | 2440 | 100 |  |  |  | 9504 | if ($g =~ s/^ID-//i) {          # family 7 is a tag ID | 
|  |  | 100 |  |  |  |  |  | 
| 377 | 1 | 50 | 33 |  |  | 7 | return 0 if defined $f and $f ne 7; | 
| 378 | 1 |  |  |  |  | 4 | $wantGroup[-1] = [ 7, $g ]; # group name with 'ID-' removed and case preserved | 
| 379 |  |  |  |  |  |  | } elsif (defined $f) { | 
| 380 | 30 | 50 |  |  |  | 122 | $f > 2 and return 0;        # only allow family 0, 1 or 2 | 
| 381 | 30 | 100 |  |  |  | 105 | $family2 = 1 if $f == 2;    # set flag indicating family 2 was used | 
| 382 |  |  |  |  |  |  | } else { | 
| 383 | 2409 | 100 |  |  |  | 8434 | $family2 = 1 if $family2groups{$lcg}; | 
| 384 |  |  |  |  |  |  | } | 
| 385 |  |  |  |  |  |  | } | 
| 386 | 2414 | 100 |  |  |  | 7281 | undef $wantGroup unless @wantGroup; | 
| 387 |  |  |  |  |  |  | } | 
| 388 |  |  |  |  |  |  |  | 
| 389 | 5638 |  |  |  |  | 13950 | $tag =~ s/ .*//;    # convert from tag key to tag name if necessary | 
| 390 | 5638 | 100 |  |  |  | 14745 | $tag = '*' if lc($tag) eq 'all';    # use '*' instead of 'all' | 
| 391 |  |  |  |  |  |  | # | 
| 392 |  |  |  |  |  |  | # handle group delete | 
| 393 |  |  |  |  |  |  | # | 
| 394 | 5638 |  | 100 |  |  | 17364 | while ($tag eq '*' and not defined $value and not $family2 and @wantGroup < 2) { | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 395 |  |  |  |  |  |  | # set groups to delete | 
| 396 | 47 |  |  |  |  | 126 | my (@del, $grp); | 
| 397 | 47 |  | 66 |  |  | 255 | my $remove = ($options{Replace} and $options{Replace} > 1); | 
| 398 | 47 | 100 |  |  |  | 173 | if ($wantGroup) { | 
| 399 | 34 | 50 |  |  |  | 2336 | @del = grep /^$wantGroup$/i, @delGroups unless $wantGroup =~ /^XM[LP]-\*$/i; | 
| 400 |  |  |  |  |  |  | # remove associated groups when excluding from mass delete | 
| 401 | 34 | 100 | 100 |  |  | 300 | if (@del and $remove) { | 
| 402 |  |  |  |  |  |  | # remove associated groups in other family | 
| 403 | 4 | 100 |  |  |  | 34 | push @del, @{$excludeGroups{$del[0]}} if $excludeGroups{$del[0]}; | 
|  | 2 |  |  |  |  | 14 |  | 
| 404 |  |  |  |  |  |  | # remove upstream groups according to JPEG map | 
| 405 | 4 |  |  |  |  | 11 | my $dirName = $del[0]; | 
| 406 | 4 |  |  |  |  | 10 | my @dirNames; | 
| 407 | 4 |  |  |  |  | 9 | for (;;) { | 
| 408 | 10 |  |  |  |  | 26 | my $parent = $jpegMap{$dirName}; | 
| 409 | 10 | 50 |  |  |  | 30 | if (ref $parent) { | 
| 410 | 0 |  |  |  |  | 0 | push @dirNames, @$parent; | 
| 411 | 0 |  |  |  |  | 0 | $parent = pop @dirNames; | 
| 412 |  |  |  |  |  |  | } | 
| 413 | 10 | 100 | 66 |  |  | 51 | $dirName = $parent || shift @dirNames or last; | 
| 414 | 6 |  |  |  |  | 16 | 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 |  |  |  | 218 | 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 |  |  |  |  | 1579 | push @del, (grep !/^$protectedGroups$/, @delGroups), '*'; | 
| 423 |  |  |  |  |  |  | } | 
| 424 | 47 | 50 |  |  |  | 220 | if (@del) { | 
|  |  | 0 |  |  |  |  |  | 
| 425 | 47 |  |  |  |  | 98 | ++$numSet; | 
| 426 | 47 |  |  |  |  | 99 | my @donegrps; | 
| 427 | 47 |  |  |  |  | 204 | my $delGroup = $$self{DEL_GROUP}; | 
| 428 | 47 |  |  |  |  | 144 | foreach $grp (@del) { | 
| 429 | 804 | 100 |  |  |  | 1268 | if ($remove) { | 
| 430 | 23 |  |  |  |  | 43 | my $didExcl; | 
| 431 | 23 | 100 |  |  |  | 76 | if ($grp =~ /^(XM[LP])(-.*)?$/) { | 
| 432 | 4 |  |  |  |  | 13 | my $x = $1; | 
| 433 | 4 | 100 | 33 |  |  | 39 | if ($grp eq $x) { | 
|  |  | 50 |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | # exclude all related family 1 groups too | 
| 435 | 1 |  |  |  |  | 20 | foreach (keys %$delGroup) { | 
| 436 | 58 | 100 |  |  |  | 167 | next unless /^(-?)$x-/; | 
| 437 | 1 | 50 |  |  |  | 8 | push @donegrps, $_ unless $1; | 
| 438 | 1 |  |  |  |  | 4 | 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 |  |  |  | 16 | if ($$delGroup{$x}) { | 
| 443 | 2 |  |  |  |  | 7 | push @donegrps, $x; | 
| 444 | 2 |  |  |  |  | 7 | delete $$delGroup{$x}; | 
| 445 |  |  |  |  |  |  | } | 
| 446 |  |  |  |  |  |  | # flag XMP/XML family 1 group for exclusion with leading '-' | 
| 447 | 3 |  |  |  |  | 12 | $$delGroup{"-$grp"} = 1; | 
| 448 | 3 |  |  |  |  | 9 | $didExcl = 1; | 
| 449 |  |  |  |  |  |  | } | 
| 450 |  |  |  |  |  |  | } | 
| 451 | 23 | 100 |  |  |  | 60 | if (exists $$delGroup{$grp}) { | 
| 452 | 15 |  |  |  |  | 26 | delete $$delGroup{$grp}; | 
| 453 |  |  |  |  |  |  | } else { | 
| 454 | 8 | 100 |  |  |  | 25 | next unless $didExcl; | 
| 455 |  |  |  |  |  |  | } | 
| 456 |  |  |  |  |  |  | } else { | 
| 457 | 781 |  |  |  |  | 1913 | $$delGroup{$grp} = 1; | 
| 458 |  |  |  |  |  |  | # add extra groups to delete if necessary | 
| 459 | 781 | 100 |  |  |  | 1547 | if ($delMore{$grp}) { | 
| 460 | 49 |  |  |  |  | 123 | $$delGroup{$_} = 1, push @donegrps, $_ foreach @{$delMore{$grp}}; | 
|  | 49 |  |  |  |  | 284 |  | 
| 461 |  |  |  |  |  |  | } | 
| 462 |  |  |  |  |  |  | # remove all of this group from previous new values | 
| 463 | 781 |  |  |  |  | 1386 | $self->RemoveNewValuesForGroup($grp); | 
| 464 |  |  |  |  |  |  | } | 
| 465 | 799 |  |  |  |  | 1359 | push @donegrps, $grp; | 
| 466 |  |  |  |  |  |  | } | 
| 467 | 47 | 100 | 66 |  |  | 302 | 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 |  |  |  |  | 12 | 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 |  |  |  | 177 | return ($numSet, $err) if wantarray; | 
| 479 | 47 | 50 |  |  |  | 184 | $err and warn "$err\n"; | 
| 480 | 47 |  |  |  |  | 322 | return $numSet; | 
| 481 |  |  |  |  |  |  | } | 
| 482 |  |  |  |  |  |  |  | 
| 483 |  |  |  |  |  |  | # initialize write/create flags | 
| 484 | 5591 |  |  |  |  | 8455 | my $createOnly; | 
| 485 | 5591 |  |  |  |  | 9934 | my $editOnly = $options{EditOnly}; | 
| 486 | 5591 |  |  |  |  | 8944 | my $editGroup = $options{EditGroup}; | 
| 487 | 5591 |  |  |  |  | 12422 | my $writeMode = $$self{OPTIONS}{WriteMode}; | 
| 488 | 5591 | 100 |  |  |  | 12486 | if ($writeMode ne 'wcg') { | 
| 489 | 27 | 100 |  |  |  | 139 | $createOnly = 1 if $writeMode !~ /w/i;  # don't write existing tags | 
| 490 | 27 | 100 |  |  |  | 143 | if ($writeMode !~ /c/i) { | 
|  |  | 100 |  |  |  |  |  | 
| 491 | 2 | 50 |  |  |  | 25 | return 0 if $createOnly;    # nothing to do unless writing existing tags | 
| 492 | 2 |  |  |  |  | 6 | $editOnly = 1;              # don't create new tags | 
| 493 |  |  |  |  |  |  | } elsif ($writeMode !~ /g/i) { | 
| 494 | 8 |  |  |  |  | 19 | $editGroup = 1;             # don't create new groups | 
| 495 |  |  |  |  |  |  | } | 
| 496 |  |  |  |  |  |  | } | 
| 497 | 5591 |  |  |  |  | 10369 | my ($ifdName, $mieGroup, $movGroup, $fg); | 
| 498 |  |  |  |  |  |  | # set family 1 group names | 
| 499 | 5591 |  |  |  |  | 10986 | foreach $fg (@wantGroup) { | 
| 500 | 2297 | 100 | 100 |  |  | 6639 | next if defined $$fg[0] and $$fg[0] != 1; | 
| 501 | 2278 |  |  |  |  | 4667 | $_ = $$fg[1]; | 
| 502 |  |  |  |  |  |  | # set $ifdName if this group is a valid IFD or SubIFD name | 
| 503 | 2278 |  |  |  |  | 3271 | my $grpName; | 
| 504 | 2278 | 100 | 100 |  |  | 19792 | if (/^IFD(\d+)$/i) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 505 | 131 |  |  |  |  | 523 | $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 | 274 |  |  |  |  | 846 | $grpName = $exifDirs{$_}; | 
| 512 | 274 | 50 | 33 |  |  | 1252 | $ifdName = $grpName unless $ifdName and $allFam0{$_}; | 
| 513 |  |  |  |  |  |  | } elsif ($allFam0{$_}) { | 
| 514 | 293 |  |  |  |  | 647 | $grpName = $allFam0{$_}; | 
| 515 |  |  |  |  |  |  | } elsif (/^Track(\d+)$/i) { | 
| 516 | 1 |  |  |  |  | 6 | $grpName = $movGroup = "Track$1";  # QuickTime track | 
| 517 |  |  |  |  |  |  | } elsif (/^MIE(\d*-?)(\w+)$/i) { | 
| 518 | 2 |  |  |  |  | 21 | $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 | 502 |  |  |  |  | 1898 | my $table = GetTagTable('Image::ExifTool::XMP::Main'); | 
| 522 | 502 |  |  |  |  | 1310 | my $writeProc = $$table{WRITE_PROC}; | 
| 523 | 502 | 50 |  |  |  | 1263 | if ($writeProc) { | 
| 524 | 59 |  |  | 59 |  | 632 | no strict 'refs'; | 
|  | 59 |  |  |  |  | 350 |  | 
|  | 59 |  |  |  |  | 90457 |  | 
| 525 | 502 |  |  |  |  | 1841 | &$writeProc(); | 
| 526 |  |  |  |  |  |  | } | 
| 527 |  |  |  |  |  |  | } | 
| 528 |  |  |  |  |  |  | # fix case for known groups | 
| 529 | 2278 | 100 | 66 |  |  | 12539 | $wantGroup =~ s/$grpName/$grpName/i if $grpName and $grpName ne $_; | 
| 530 |  |  |  |  |  |  | } | 
| 531 |  |  |  |  |  |  | # | 
| 532 |  |  |  |  |  |  | # get list of tags we want to set | 
| 533 |  |  |  |  |  |  | # | 
| 534 | 5591 |  |  |  |  | 10253 | my $origTag = $tag; | 
| 535 | 5591 |  |  |  |  | 18687 | my @matchingTags = FindTagInfo($tag); | 
| 536 | 5591 |  |  |  |  | 15701 | until (@matchingTags) { | 
| 537 | 1416 |  |  |  |  | 2543 | my $langCode; | 
| 538 |  |  |  |  |  |  | # allow language suffix of form "-en_CA" or "-" on tag name | 
| 539 | 1416 | 100 | 100 |  |  | 8079 | 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 |  |  |  |  | 186 | $tag = $1; | 
| 543 |  |  |  |  |  |  | # normalize case of language codes | 
| 544 | 51 |  |  |  |  | 138 | $langCode = lc($2); | 
| 545 | 51 | 100 |  |  |  | 235 | $langCode .= (length($3) == 3 ? uc($3) : lc($3)) if $3; | 
|  |  | 100 |  |  |  |  |  | 
| 546 | 51 |  |  |  |  | 187 | my @newMatches = FindTagInfo($tag); | 
| 547 | 51 |  |  |  |  | 184 | foreach $tagInfo (@newMatches) { | 
| 548 |  |  |  |  |  |  | # only allow language codes in tables which support them | 
| 549 | 238 | 50 |  |  |  | 672 | next unless $$tagInfo{Table}; | 
| 550 | 238 | 100 |  |  |  | 645 | my $langInfoProc = $$tagInfo{Table}{LANG_INFO} or next; | 
| 551 | 186 |  |  |  |  | 615 | my $langInfo = &$langInfoProc($tagInfo, $langCode); | 
| 552 | 186 | 100 |  |  |  | 598 | push @matchingTags, $langInfo if $langInfo; | 
| 553 |  |  |  |  |  |  | } | 
| 554 | 51 | 100 |  |  |  | 187 | last if @matchingTags; | 
| 555 |  |  |  |  |  |  | } elsif (not $options{NoShortcut}) { | 
| 556 |  |  |  |  |  |  | # look for a shortcut or alias | 
| 557 | 1365 |  |  |  |  | 10607 | require Image::ExifTool::Shortcuts; | 
| 558 | 1365 |  |  |  |  | 31311 | my ($match) = grep /^\Q$tag\E$/i, keys %Image::ExifTool::Shortcuts::Main; | 
| 559 | 1365 |  |  |  |  | 4111 | undef $err; | 
| 560 | 1365 | 100 |  |  |  | 3480 | if ($match) { | 
| 561 | 1 |  |  |  |  | 6 | $options{NoShortcut} = $options{Sanitized} = 1; | 
| 562 | 1 |  |  |  |  | 2 | foreach $tag (@{$Image::ExifTool::Shortcuts::Main{$match}}) { | 
|  | 1 |  |  |  |  | 4 |  | 
| 563 | 3 |  |  |  |  | 49 | my ($n, $e) = $self->SetNewValue($tag, $value, %options); | 
| 564 | 3 |  |  |  |  | 12 | $numSet += $n; | 
| 565 | 3 | 50 |  |  |  | 22 | $e and $err = $e; | 
| 566 |  |  |  |  |  |  | } | 
| 567 | 1 | 50 |  |  |  | 5 | undef $err if $numSet;  # no error if any set successfully | 
| 568 | 1 | 50 |  |  |  | 4 | return ($numSet, $err) if wantarray; | 
| 569 | 1 | 50 |  |  |  | 4 | $err and warn "$err\n"; | 
| 570 | 1 |  |  |  |  | 9 | return $numSet; | 
| 571 |  |  |  |  |  |  | } | 
| 572 |  |  |  |  |  |  | } | 
| 573 | 1366 | 50 |  |  |  | 3174 | unless ($listOnly) { | 
| 574 | 1366 | 100 |  |  |  | 4462 | if (not TagExists($tag)) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 575 | 49 | 50 |  |  |  | 287 | if ($tag =~ /^[-\w*?]+$/) { | 
| 576 | 49 | 100 |  |  |  | 134 | my $pre = $wantGroup ? $wantGroup . ':' : ''; | 
| 577 | 49 |  |  |  |  | 155 | $err = "Tag '$pre${origTag}' is not defined"; | 
| 578 | 49 | 100 |  |  |  | 161 | $err .= ' or has a bad language code' if $origTag =~ /-/; | 
| 579 | 49 | 50 | 66 |  |  | 179 | 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 |  |  |  |  | 1512 | $err = "Sorry, $wantGroup:$origTag doesn't exist or isn't writable"; | 
| 590 |  |  |  |  |  |  | } else { | 
| 591 | 810 |  |  |  |  | 2294 | $err = "Sorry, $origTag is not writable"; | 
| 592 |  |  |  |  |  |  | } | 
| 593 | 1366 | 50 |  |  |  | 3523 | $verbose > 2 and print $out "$err\n"; | 
| 594 |  |  |  |  |  |  | } | 
| 595 |  |  |  |  |  |  | # all done | 
| 596 | 1366 | 50 |  |  |  | 8274 | 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 | 4224 |  |  |  |  | 8038 | my $foundMatch = 0; | 
| 602 |  |  |  |  |  |  | # | 
| 603 |  |  |  |  |  |  | # determine the groups for all tags found, and the tag with | 
| 604 |  |  |  |  |  |  | # the highest priority group | 
| 605 |  |  |  |  |  |  | # | 
| 606 | 4224 |  |  |  |  | 12134 | my (@tagInfoList, @writeAlsoList, %writeGroup, %preferred, %tagPriority); | 
| 607 | 4224 |  |  |  |  | 0 | my (%avoid, $wasProtected, $noCreate, %highestPriority, %highestQT); | 
| 608 |  |  |  |  |  |  |  | 
| 609 | 4224 |  |  |  |  | 7843 | TAG: foreach $tagInfo (@matchingTags) { | 
| 610 | 69913 |  |  |  |  | 331092 | $tag = $$tagInfo{Name};     # get tag name for warnings | 
| 611 | 69913 |  |  |  |  | 118020 | 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 | 69913 | 100 |  |  |  | 197591 | $highestPriority{$lcTag} = -999 unless defined $highestPriority{$lcTag}; | 
| 614 | 69913 |  |  |  |  | 104087 | my ($priority, $writeGroup); | 
| 615 | 69913 | 100 |  |  |  | 217395 | my $prfTag = defined $$tagInfo{Preferred} ? $$tagInfo{Preferred} : $$tagInfo{Table}{PREFERRED}; | 
| 616 | 69913 | 100 |  |  |  | 128936 | if ($wantGroup) { | 
| 617 |  |  |  |  |  |  | # a WriteGroup of All is special | 
| 618 | 49702 |  | 100 |  |  | 102768 | my $wgAll = ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All'); | 
| 619 | 49702 |  |  |  |  | 129397 | my @grp = $self->GetGroup($tagInfo); | 
| 620 | 49702 |  |  |  |  | 77676 | my $hiPri = 1000; | 
| 621 | 49702 |  |  |  |  | 79972 | foreach $fg (@wantGroup) { | 
| 622 | 49740 |  |  |  |  | 93506 | my ($fam, $lcWant) = @$fg; | 
| 623 | 49740 | 100 |  |  |  | 100161 | $lcWant = $translateWantGroup{$lcWant} if $translateWantGroup{$lcWant}; | 
| 624 |  |  |  |  |  |  | # only set tag in specified group | 
| 625 |  |  |  |  |  |  | # bump priority of preferred tag | 
| 626 | 49740 | 100 |  |  |  | 89929 | $hiPri += $prfTag if $prfTag; | 
| 627 | 49740 | 100 | 66 |  |  | 91087 | if (not defined $fam) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 628 | 49498 | 100 |  |  |  | 101764 | 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 | 2182 | 100 | 100 |  |  | 5315 | $writeGroup = $grp[0] if $wgAll and not $writeGroup; | 
| 632 | 2182 |  |  |  |  | 4219 | next; | 
| 633 |  |  |  |  |  |  | } | 
| 634 | 47316 | 100 |  |  |  | 97116 | next if $lcWant eq lc $grp[2]; | 
| 635 |  |  |  |  |  |  | } elsif ($fam == 7) { | 
| 636 | 2 | 100 |  |  |  | 24 | next if IsSameID($$tagInfo{TagID}, $lcWant); | 
| 637 |  |  |  |  |  |  | } elsif ($fam != 1 and not $$tagInfo{AllowGroup}) { | 
| 638 | 132 | 100 |  |  |  | 351 | next if $lcWant eq lc $grp[$fam]; | 
| 639 | 110 | 100 | 100 |  |  | 350 | if ($wgAll and not $fam and $allFam0{$lcWant}) { | 
|  |  |  | 100 |  |  |  |  | 
| 640 | 5 | 100 |  |  |  | 23 | $writeGroup or $writeGroup = $allFam0{$lcWant}; | 
| 641 | 5 |  |  |  |  | 18 | next; | 
| 642 |  |  |  |  |  |  | } | 
| 643 | 105 |  |  |  |  | 271 | next TAG;   # wrong group | 
| 644 |  |  |  |  |  |  | } | 
| 645 |  |  |  |  |  |  | # handle family 1 groups specially | 
| 646 | 36848 | 100 | 66 |  |  | 213811 | if ($grp[0] eq 'EXIF' or $grp[0] eq 'SonyIDC' or $wgAll) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 647 | 1581 | 100 | 100 |  |  | 5460 | unless ($ifdName and $lcWant eq lc $ifdName) { | 
| 648 | 1141 | 100 | 100 |  |  | 4938 | next TAG unless $wgAll and not $fam and $allFam0{$lcWant}; | 
|  |  |  | 100 |  |  |  |  | 
| 649 | 7 | 100 |  |  |  | 29 | $writeGroup = $allFam0{$lcWant} unless $writeGroup; | 
| 650 | 7 |  |  |  |  | 15 | next; | 
| 651 |  |  |  |  |  |  | } | 
| 652 | 440 | 100 | 100 |  |  | 1183 | next TAG if $wgAll and $allFam0{$lcWant} and $fam; | 
|  |  |  | 100 |  |  |  |  | 
| 653 |  |  |  |  |  |  | # can't yet write PreviewIFD tags (except for image) | 
| 654 | 438 | 50 |  |  |  | 1154 | $lcWant eq 'PreviewIFD' and ++$foundMatch, next TAG; | 
| 655 | 438 |  |  |  |  | 984 | $writeGroup = $ifdName; # write to the specified IFD | 
| 656 |  |  |  |  |  |  | } elsif ($grp[0] eq 'QuickTime') { | 
| 657 | 1558 | 100 |  |  |  | 3703 | if ($grp[1] eq 'Track#') { | 
| 658 | 16 | 100 | 66 |  |  | 127 | next TAG unless $movGroup and $lcWant eq lc($movGroup); | 
| 659 | 1 |  |  |  |  | 3 | $writeGroup = $movGroup; | 
| 660 |  |  |  |  |  |  | } else { | 
| 661 | 1542 |  |  |  |  | 4142 | my $grp = $$tagInfo{Table}{WRITE_GROUP}; | 
| 662 | 1542 | 100 | 100 |  |  | 7288 | next TAG unless $grp and $lcWant eq lc $grp; | 
| 663 | 28 |  |  |  |  | 71 | $writeGroup = $grp; | 
| 664 |  |  |  |  |  |  | } | 
| 665 |  |  |  |  |  |  | } elsif ($grp[0] eq 'MIE') { | 
| 666 | 746 | 100 | 66 |  |  | 3595 | next TAG unless $mieGroup and $lcWant eq lc($mieGroup); | 
| 667 | 2 |  |  |  |  | 9 | $writeGroup = $mieGroup; # write to specific MIE group | 
| 668 |  |  |  |  |  |  | # set specific write group with document number if specified | 
| 669 | 2 | 0 | 33 |  |  | 15 | 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 | 32962 | 100 |  |  |  | 105188 | next TAG unless $lcWant eq lc $grp[1]; | 
| 676 |  |  |  |  |  |  | } | 
| 677 |  |  |  |  |  |  | } | 
| 678 | 13750 | 100 | 66 |  |  | 70063 | $writeGroup or $writeGroup = ($$tagInfo{WriteGroup} || $$tagInfo{Table}{WRITE_GROUP} || $grp[0]); | 
| 679 | 13750 |  |  |  |  | 25492 | $priority = $hiPri; # highest priority since group was specified | 
| 680 |  |  |  |  |  |  | } | 
| 681 | 33961 |  |  |  |  | 48392 | ++$foundMatch; | 
| 682 |  |  |  |  |  |  | # must do a dummy call to the write proc to autoload write package | 
| 683 |  |  |  |  |  |  | # before checking Writable flag | 
| 684 | 33961 |  |  |  |  | 54004 | my $table = $$tagInfo{Table}; | 
| 685 | 33961 |  |  |  |  | 66623 | my $writeProc = $$table{WRITE_PROC}; | 
| 686 |  |  |  |  |  |  | # load source table if this was a user-defined table | 
| 687 | 33961 | 100 |  |  |  | 76441 | if ($$table{SRC_TABLE}) { | 
| 688 | 9 |  |  |  |  | 36 | my $src = GetTagTable($$table{SRC_TABLE}); | 
| 689 | 9 | 50 |  |  |  | 35 | $writeProc = $$src{WRITE_PROC} unless $writeProc; | 
| 690 |  |  |  |  |  |  | } | 
| 691 |  |  |  |  |  |  | { | 
| 692 | 59 |  |  | 59 |  | 565 | no strict 'refs'; | 
|  | 59 |  |  |  |  | 152 |  | 
|  | 59 |  |  |  |  | 761589 |  | 
|  | 33961 |  |  |  |  | 46296 |  | 
| 693 | 33961 | 100 | 66 |  |  | 110476 | next unless $writeProc and &$writeProc(); | 
| 694 |  |  |  |  |  |  | } | 
| 695 |  |  |  |  |  |  | # must still check writable flags in case of UserDefined tags | 
| 696 | 33961 |  |  |  |  | 74637 | my $writable = $$tagInfo{Writable}; | 
| 697 |  |  |  |  |  |  | next unless $writable or ($$table{WRITABLE} and | 
| 698 | 33961 | 50 | 66 |  |  | 146149 | not defined $writable and not $$tagInfo{SubDirectory}); | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 699 |  |  |  |  |  |  | # set specific write group (if we didn't already) | 
| 700 | 33960 | 100 | 66 |  |  | 87237 | 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 | 20287 |  | 100 |  |  | 64514 | $writeGroup = $$tagInfo{WriteGroup} || $$tagInfo{Table}{WRITE_GROUP}; | 
| 705 |  |  |  |  |  |  | # use group 0 name if no WriteGroup specified | 
| 706 | 20287 |  |  |  |  | 54710 | my $group0 = $self->GetGroup($tagInfo, 0); | 
| 707 | 20287 | 100 |  |  |  | 46961 | $writeGroup or $writeGroup = $group0; | 
| 708 |  |  |  |  |  |  | # get priority for this group | 
| 709 | 20287 | 100 |  |  |  | 38056 | unless ($priority) { | 
| 710 | 20210 | 100 | 100 |  |  | 50272 | if ($$tagInfo{Avoid} and $$tagInfo{WriteAlso}) { | 
| 711 | 22 |  |  |  |  | 50 | $priority = 0; | 
| 712 |  |  |  |  |  |  | } else { | 
| 713 | 20188 |  |  |  |  | 42978 | $priority = $$self{WRITE_PRIORITY}{lc($writeGroup)}; | 
| 714 | 20188 | 100 |  |  |  | 38015 | unless ($priority) { | 
| 715 | 3568 |  | 100 |  |  | 11422 | $priority = $$self{WRITE_PRIORITY}{lc($group0)} || 0; | 
| 716 |  |  |  |  |  |  | } | 
| 717 |  |  |  |  |  |  | } | 
| 718 |  |  |  |  |  |  | } | 
| 719 |  |  |  |  |  |  | # adjust priority based on Preferred level for this tag | 
| 720 | 20287 | 100 |  |  |  | 39343 | $priority += $prfTag if $prfTag; | 
| 721 |  |  |  |  |  |  | } | 
| 722 |  |  |  |  |  |  | # don't write tag if protected | 
| 723 | 33960 |  |  |  |  | 57962 | my $prot = $$tagInfo{Protected}; | 
| 724 | 33960 | 100 | 100 |  |  | 70500 | $prot = 1 if $noFlat and defined $$tagInfo{Flat}; | 
| 725 | 33960 | 100 |  |  |  | 59858 | if ($prot) { | 
| 726 | 2341 |  |  |  |  | 4791 | $prot &= ~$protected; | 
| 727 | 2341 | 100 |  |  |  | 5205 | if ($prot) { | 
| 728 | 1208 |  |  |  |  | 4590 | my %lkup = ( 1=>'unsafe', 2=>'protected', 3=>'unsafe and protected'); | 
| 729 | 1208 |  |  |  |  | 2500 | $wasProtected = $lkup{$prot}; | 
| 730 | 1208 | 100 |  |  |  | 2791 | if ($verbose > 1) { | 
| 731 | 1 |  |  |  |  | 7 | my $wgrp1 = $self->GetWriteGroup1($tagInfo, $writeGroup); | 
| 732 | 1 |  |  |  |  | 9 | print $out "Sorry, $wgrp1:$tag is $wasProtected for writing\n"; | 
| 733 |  |  |  |  |  |  | } | 
| 734 | 1208 |  |  |  |  | 3574 | next; | 
| 735 |  |  |  |  |  |  | } | 
| 736 |  |  |  |  |  |  | } | 
| 737 |  |  |  |  |  |  | # set priority for this tag | 
| 738 | 32752 |  |  |  |  | 120005 | $tagPriority{$tagInfo} = $priority; | 
| 739 |  |  |  |  |  |  | # keep track of highest priority QuickTime tag | 
| 740 |  |  |  |  |  |  | $highestQT{$lcTag} = $priority if $$table{GROUPS}{0} eq 'QuickTime' and | 
| 741 | 32752 | 100 | 100 |  |  | 100604 | (not defined $highestQT{$lcTag} or $highestQT{$lcTag} < $priority); | 
|  |  |  | 100 |  |  |  |  | 
| 742 | 32752 | 100 |  |  |  | 85806 | if ($priority > $highestPriority{$lcTag}) { | 
|  |  | 100 |  |  |  |  |  | 
| 743 | 10388 |  |  |  |  | 17130 | $highestPriority{$lcTag} = $priority; | 
| 744 | 10388 |  |  |  |  | 38746 | $preferred{$lcTag} = { $tagInfo => 1 }; | 
| 745 | 10388 | 100 |  |  |  | 32850 | $avoid{$lcTag} = $$tagInfo{Avoid} ? 1 : 0; | 
| 746 |  |  |  |  |  |  | } elsif ($priority == $highestPriority{$lcTag}) { | 
| 747 |  |  |  |  |  |  | # create all tags with highest priority | 
| 748 | 13490 |  |  |  |  | 31366 | $preferred{$lcTag}{$tagInfo} = 1; | 
| 749 | 13490 | 100 |  |  |  | 32210 | ++$avoid{$lcTag} if $$tagInfo{Avoid}; | 
| 750 |  |  |  |  |  |  | } | 
| 751 | 32752 | 100 |  |  |  | 62360 | if ($$tagInfo{WriteAlso}) { | 
| 752 |  |  |  |  |  |  | # store WriteAlso tags separately so we can set them first | 
| 753 | 111 |  |  |  |  | 411 | push @writeAlsoList, $tagInfo; | 
| 754 |  |  |  |  |  |  | } else { | 
| 755 | 32641 |  |  |  |  | 58092 | push @tagInfoList, $tagInfo; | 
| 756 |  |  |  |  |  |  | } | 
| 757 |  |  |  |  |  |  | # special case to allow override of XMP WriteGroup | 
| 758 | 32752 | 100 |  |  |  | 65104 | if ($writeGroup eq 'XMP') { | 
| 759 | 5490 |  | 33 |  |  | 19385 | my $wg = $$tagInfo{WriteGroup} || $$table{WRITE_GROUP}; | 
| 760 | 5490 | 50 |  |  |  | 12864 | $writeGroup = $wg if $wg; | 
| 761 |  |  |  |  |  |  | } | 
| 762 | 32752 |  |  |  |  | 106255 | $writeGroup{$tagInfo} = $writeGroup; | 
| 763 |  |  |  |  |  |  | } | 
| 764 |  |  |  |  |  |  | # sort tag info list in reverse order of priority (highest number last) | 
| 765 |  |  |  |  |  |  | # so we get the highest priority error message in the end | 
| 766 | 4224 |  |  |  |  | 14143 | @tagInfoList = sort { $tagPriority{$a} <=> $tagPriority{$b} } @tagInfoList; | 
|  | 54489 |  |  |  |  | 106498 |  | 
| 767 |  |  |  |  |  |  | # must write any tags which also write other tags first | 
| 768 | 4224 | 100 |  |  |  | 11800 | unshift @tagInfoList, @writeAlsoList if @writeAlsoList; | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | # check priorities for each set of tags we are writing | 
| 771 | 4224 |  |  |  |  | 7311 | my $lcTag; | 
| 772 | 4224 |  |  |  |  | 13938 | foreach $lcTag (keys %preferred) { | 
| 773 |  |  |  |  |  |  | # don't create tags with priority 0 if group priorities are set | 
| 774 | 9535 | 100 | 66 |  |  | 49025 | if ($preferred{$lcTag} and $highestPriority{$lcTag} == 0 and | 
|  |  |  | 66 |  |  |  |  | 
| 775 | 9 |  |  |  |  | 71 | %{$$self{WRITE_PRIORITY}}) | 
| 776 |  |  |  |  |  |  | { | 
| 777 | 9 |  |  |  |  | 36 | delete $preferred{$lcTag} | 
| 778 |  |  |  |  |  |  | } | 
| 779 |  |  |  |  |  |  | # avoid creating tags with 'Avoid' flag set if there are other alternatives | 
| 780 | 9535 | 50 | 66 |  |  | 26622 | if ($avoid{$lcTag} and $preferred{$lcTag}) { | 
| 781 | 1350 | 100 |  |  |  | 2611 | if ($avoid{$lcTag} < scalar(keys %{$preferred{$lcTag}})) { | 
|  | 1350 | 100 |  |  |  | 9526 |  | 
| 782 |  |  |  |  |  |  | # just remove the 'Avoid' tags since there are other preferred tags | 
| 783 | 1236 |  |  |  |  | 3015 | foreach $tagInfo (@tagInfoList) { | 
| 784 | 4561111 | 100 |  |  |  | 9466497 | next unless $lcTag eq lc $$tagInfo{Name}; | 
| 785 | 5803 | 100 |  |  |  | 18015 | delete $preferred{$lcTag}{$tagInfo} if $$tagInfo{Avoid}; | 
| 786 |  |  |  |  |  |  | } | 
| 787 |  |  |  |  |  |  | } elsif ($highestPriority{$lcTag} < 1000) { | 
| 788 |  |  |  |  |  |  | # look for another priority tag to create instead | 
| 789 | 29 |  |  |  |  | 82 | my $nextHighest = 0; | 
| 790 | 29 |  |  |  |  | 72 | my @nextBestTags; | 
| 791 | 29 |  |  |  |  | 86 | foreach $tagInfo (@tagInfoList) { | 
| 792 | 10962 | 100 |  |  |  | 22661 | next unless $lcTag eq lc $$tagInfo{Name}; | 
| 793 | 102 | 100 |  |  |  | 274 | my $priority = $tagPriority{$tagInfo} or next; | 
| 794 | 101 | 100 |  |  |  | 293 | next if $priority == $highestPriority{$lcTag}; | 
| 795 | 71 | 50 |  |  |  | 166 | next if $priority < $nextHighest; | 
| 796 | 71 |  |  |  |  | 122 | my $permanent = $$tagInfo{Permanent}; | 
| 797 | 71 | 50 |  |  |  | 240 | $permanent = $$tagInfo{Table}{PERMANENT} unless defined $permanent; | 
| 798 | 71 | 100 | 66 |  |  | 268 | next if $$tagInfo{Avoid} or $permanent; | 
| 799 | 67 | 100 |  |  |  | 245 | next if $writeGroup{$tagInfo} eq 'MakerNotes'; | 
| 800 | 23 | 100 |  |  |  | 94 | if ($nextHighest < $priority) { | 
| 801 | 18 |  |  |  |  | 41 | $nextHighest = $priority; | 
| 802 | 18 |  |  |  |  | 54 | undef @nextBestTags; | 
| 803 |  |  |  |  |  |  | } | 
| 804 | 23 |  |  |  |  | 58 | push @nextBestTags, $tagInfo; | 
| 805 |  |  |  |  |  |  | } | 
| 806 | 29 | 100 |  |  |  | 172 | if (@nextBestTags) { | 
| 807 |  |  |  |  |  |  | # change our preferred tags to the next best tags | 
| 808 | 13 |  |  |  |  | 43 | delete $preferred{$lcTag}; | 
| 809 | 13 |  |  |  |  | 46 | foreach $tagInfo (@nextBestTags) { | 
| 810 | 14 |  |  |  |  | 92 | $preferred{$lcTag}{$tagInfo} = 1; | 
| 811 |  |  |  |  |  |  | } | 
| 812 |  |  |  |  |  |  | } | 
| 813 |  |  |  |  |  |  | } | 
| 814 |  |  |  |  |  |  | } | 
| 815 |  |  |  |  |  |  | } | 
| 816 |  |  |  |  |  |  | # | 
| 817 |  |  |  |  |  |  | # generate new value hash for each tag | 
| 818 |  |  |  |  |  |  | # | 
| 819 | 4224 |  |  |  |  | 8708 | my ($prioritySet, $createGroups, %alsoWrote); | 
| 820 |  |  |  |  |  |  |  | 
| 821 | 4224 |  |  |  |  | 9143 | delete $$self{CHECK_WARN};  # reset CHECK_PROC warnings | 
| 822 |  |  |  |  |  |  |  | 
| 823 |  |  |  |  |  |  | # loop through all valid tags to find the one(s) to write | 
| 824 | 4224 |  |  |  |  | 7955 | foreach $tagInfo (@tagInfoList) { | 
| 825 | 32740 | 100 |  |  |  | 98508 | next if $alsoWrote{$tagInfo};   # don't rewrite tags we already wrote | 
| 826 |  |  |  |  |  |  | # only process List or non-List tags if specified | 
| 827 | 32731 | 100 | 100 |  |  | 80693 | next if defined $listOnly and ($listOnly xor $$tagInfo{List}); | 
|  |  |  | 100 |  |  |  |  | 
| 828 | 32510 |  |  |  |  | 43983 | my $noConv; | 
| 829 | 32510 |  |  |  |  | 95486 | my $writeGroup = $writeGroup{$tagInfo}; | 
| 830 | 32510 |  |  |  |  | 69436 | my $permanent = $$tagInfo{Permanent}; | 
| 831 | 32510 | 100 |  |  |  | 110307 | $permanent = $$tagInfo{Table}{PERMANENT} unless defined $permanent; | 
| 832 | 32510 | 100 | 100 |  |  | 93982 | $writeGroup eq 'MakerNotes' and $permanent = 1 unless defined $permanent; | 
| 833 | 32510 |  |  |  |  | 90933 | my $wgrp1 = $self->GetWriteGroup1($tagInfo, $writeGroup); | 
| 834 | 32510 |  |  |  |  | 77290 | $tag = $$tagInfo{Name};     # get tag name for warnings | 
| 835 | 32510 |  |  |  |  | 64222 | my $lcTag = lc $tag; | 
| 836 | 32510 |  | 100 |  |  | 97072 | my $pref = $preferred{$lcTag} || { }; | 
| 837 |  |  |  |  |  |  | # don't write Avoid-ed tags with side effect unless preferred | 
| 838 | 32510 | 100 | 100 |  |  | 120420 | next if not $$pref{$tagInfo} and $$tagInfo{Avoid} and $$tagInfo{WriteAlso}; | 
|  |  |  | 100 |  |  |  |  | 
| 839 | 32488 |  |  |  |  | 56167 | my $shift = $options{Shift}; | 
| 840 | 32488 |  |  |  |  | 50040 | my $addValue = $options{AddValue}; | 
| 841 | 32488 | 100 |  |  |  | 69499 | if (defined $shift) { | 
| 842 |  |  |  |  |  |  | # (can't currently shift list-type tags) | 
| 843 | 164 |  |  |  |  | 287 | my $shiftable; | 
| 844 | 164 | 50 |  |  |  | 435 | if ($$tagInfo{List}) { | 
| 845 | 0 |  |  |  |  | 0 | $shiftable = '';    # can add/delete but not shift | 
| 846 |  |  |  |  |  |  | } else { | 
| 847 | 164 |  |  |  |  | 325 | $shiftable = $$tagInfo{Shift}; | 
| 848 | 164 | 100 |  |  |  | 423 | unless ($shift) { | 
| 849 |  |  |  |  |  |  | # set shift according to AddValue/DelValue | 
| 850 | 24 | 50 |  |  |  | 81 | $shift = 1 if $addValue; | 
| 851 |  |  |  |  |  |  | # can shift a date/time with -=, but this is | 
| 852 |  |  |  |  |  |  | # a conditional delete operation for other tags | 
| 853 | 24 | 0 | 33 |  |  | 86 | $shift = -1 if $options{DelValue} and defined $shiftable and $shiftable eq 'Time'; | 
|  |  |  | 33 |  |  |  |  | 
| 854 |  |  |  |  |  |  | } | 
| 855 | 164 | 50 | 33 |  |  | 976 | if ($shift and (not defined $value or not length $value)) { | 
|  |  |  | 33 |  |  |  |  | 
| 856 |  |  |  |  |  |  | # (now allow -= to be used for shiftable tag - v8.05) | 
| 857 |  |  |  |  |  |  | #$err = "No value for time shift of $wgrp1:$tag"; | 
| 858 |  |  |  |  |  |  | #$verbose > 2 and print $out "$err\n"; | 
| 859 |  |  |  |  |  |  | #next; | 
| 860 | 0 |  |  |  |  | 0 | undef $shift; | 
| 861 |  |  |  |  |  |  | } | 
| 862 |  |  |  |  |  |  | } | 
| 863 |  |  |  |  |  |  | # can't shift List-type tag | 
| 864 | 164 | 0 | 66 |  |  | 544 | if ((defined $shiftable and not $shiftable) and | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 865 |  |  |  |  |  |  | # and don't try to conditionally delete if Shift is "0" | 
| 866 |  |  |  |  |  |  | ($shift or ($shiftable eq '0' and $options{DelValue}))) | 
| 867 |  |  |  |  |  |  | { | 
| 868 | 0 |  |  |  |  | 0 | $err = "$wgrp1:$tag is not shiftable"; | 
| 869 | 0 | 0 |  |  |  | 0 | $verbose and print $out "$err\n"; | 
| 870 | 0 |  |  |  |  | 0 | next; | 
| 871 |  |  |  |  |  |  | } | 
| 872 |  |  |  |  |  |  | } | 
| 873 | 32488 |  |  |  |  | 52107 | my $val = $value; | 
| 874 | 32488 | 100 | 33 |  |  | 85286 | if (defined $val) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 875 |  |  |  |  |  |  | # check to make sure this is a List or Shift tag if adding | 
| 876 | 21666 | 100 | 100 |  |  | 50590 | if ($addValue and not ($shift or $$tagInfo{List})) { | 
|  |  |  | 100 |  |  |  |  | 
| 877 | 9 | 50 |  |  |  | 37 | if ($addValue eq '2') { | 
| 878 | 0 |  |  |  |  | 0 | undef $addValue;    # quietly reset this option | 
| 879 |  |  |  |  |  |  | } else { | 
| 880 | 9 |  |  |  |  | 39 | $err = "Can't add $wgrp1:$tag (not a List type)"; | 
| 881 | 9 | 50 |  |  |  | 31 | $verbose > 2 and print $out "$err\n"; | 
| 882 | 9 |  |  |  |  | 30 | next; | 
| 883 |  |  |  |  |  |  | } | 
| 884 |  |  |  |  |  |  | } | 
| 885 | 21657 | 100 | 66 |  |  | 101552 | if ($shift) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 886 | 164 | 100 | 66 |  |  | 695 | if ($$tagInfo{Shift} and $$tagInfo{Shift} eq 'Time') { | 
|  |  | 100 |  |  |  |  |  | 
| 887 |  |  |  |  |  |  | # add '+' or '-' prefix to indicate shift direction | 
| 888 | 47 | 100 |  |  |  | 139 | $val = ($shift > 0 ? '+' : '-') . $val; | 
| 889 |  |  |  |  |  |  | # check the shift for validity | 
| 890 | 47 |  |  |  |  | 2353 | require 'Image/ExifTool/Shift.pl'; | 
| 891 | 47 |  |  |  |  | 183 | my $err2 = CheckShift($$tagInfo{Shift}, $val); | 
| 892 | 47 | 50 |  |  |  | 143 | if ($err2) { | 
| 893 | 0 |  |  |  |  | 0 | $err = "$err2 for $wgrp1:$tag"; | 
| 894 | 0 | 0 |  |  |  | 0 | $verbose > 2 and print $out "$err\n"; | 
| 895 | 0 |  |  |  |  | 0 | next; | 
| 896 |  |  |  |  |  |  | } | 
| 897 |  |  |  |  |  |  | } elsif (IsFloat($val)) { | 
| 898 | 113 |  |  |  |  | 310 | $val *= $shift; | 
| 899 |  |  |  |  |  |  | } else { | 
| 900 | 4 |  |  |  |  | 20 | $err = "Shift value for $wgrp1:$tag is not a number"; | 
| 901 | 4 | 50 |  |  |  | 15 | $verbose > 2 and print $out "$err\n"; | 
| 902 | 4 |  |  |  |  | 17 | next; | 
| 903 |  |  |  |  |  |  | } | 
| 904 | 160 |  |  |  |  | 328 | $noConv = 1;    # no conversions if shifting tag | 
| 905 |  |  |  |  |  |  | } elsif (not length $val and $options{DelValue}) { | 
| 906 | 35 |  |  |  |  | 79 | $noConv = 1;    # no conversions for deleting empty value | 
| 907 |  |  |  |  |  |  | } elsif (ref $val eq 'HASH' and not $$tagInfo{Struct}) { | 
| 908 | 2 |  |  |  |  | 10 | $err = "Can't write a structure to $wgrp1:$tag"; | 
| 909 | 2 | 50 |  |  |  | 9 | $verbose > 2 and print $out "$err\n"; | 
| 910 | 2 |  |  |  |  | 8 | next; | 
| 911 |  |  |  |  |  |  | } | 
| 912 |  |  |  |  |  |  | } elsif ($permanent) { | 
| 913 | 6784 | 100 |  |  |  | 14513 | return 0 if $options{IgnorePermanent}; | 
| 914 |  |  |  |  |  |  | # can't delete permanent tags, so set them to DelValue or empty string instead | 
| 915 | 6780 | 100 |  |  |  | 13492 | if (defined $$tagInfo{DelValue}) { | 
| 916 | 33 |  |  |  |  | 64 | $val = $$tagInfo{DelValue}; | 
| 917 | 33 |  |  |  |  | 70 | $noConv = 1;    # DelValue is the raw value, so no conversion necessary | 
| 918 |  |  |  |  |  |  | } else { | 
| 919 | 6747 |  |  |  |  | 10206 | $val = ''; | 
| 920 |  |  |  |  |  |  | } | 
| 921 |  |  |  |  |  |  | } elsif ($addValue or $options{DelValue}) { | 
| 922 | 0 |  |  |  |  | 0 | $err = "No value to add or delete in $wgrp1:$tag"; | 
| 923 | 0 | 0 |  |  |  | 0 | $verbose > 2 and print $out "$err\n"; | 
| 924 | 0 |  |  |  |  | 0 | next; | 
| 925 |  |  |  |  |  |  | } else { | 
| 926 | 4038 | 100 |  |  |  | 9740 | if ($$tagInfo{DelCheck}) { | 
| 927 |  |  |  |  |  |  | #### eval DelCheck ($self, $tagInfo, $wantGroup) | 
| 928 | 6 |  |  |  |  | 679 | my $err2 = eval $$tagInfo{DelCheck}; | 
| 929 | 6 | 50 |  |  |  | 46 | $@ and warn($@), $err2 = 'Error evaluating DelCheck'; | 
| 930 | 6 | 50 |  |  |  | 24 | if (defined $err2) { | 
| 931 |  |  |  |  |  |  | # (allow other tags to be set using DelCheck as a hook) | 
| 932 | 6 | 100 |  |  |  | 111 | $err2 or goto WriteAlso; # GOTO! | 
| 933 | 3 | 50 |  |  |  | 21 | $err2 .= ' for' unless $err2 =~ /delete$/; | 
| 934 | 3 |  |  |  |  | 29 | $err = "$err2 $wgrp1:$tag"; | 
| 935 | 3 | 50 |  |  |  | 16 | $verbose > 2 and print $out "$err\n"; | 
| 936 | 3 |  |  |  |  | 13 | next; | 
| 937 |  |  |  |  |  |  | } | 
| 938 |  |  |  |  |  |  | } | 
| 939 |  |  |  |  |  |  | # set group delete flag if this tag represents an entire group | 
| 940 | 4032 | 100 | 66 |  |  | 10378 | if ($$tagInfo{DelGroup} and not $options{DelValue}) { | 
| 941 | 3 |  |  |  |  | 18 | my @del = ( $tag ); | 
| 942 | 3 |  |  |  |  | 10 | $$self{DEL_GROUP}{$tag} = 1; | 
| 943 |  |  |  |  |  |  | # delete extra groups if necessary | 
| 944 | 3 | 50 |  |  |  | 12 | if ($delMore{$tag}) { | 
| 945 | 0 |  |  |  |  | 0 | $$self{DEL_GROUP}{$_} = 1, push(@del,$_) foreach @{$delMore{$tag}}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 946 |  |  |  |  |  |  | } | 
| 947 |  |  |  |  |  |  | # remove all of this group from previous new values | 
| 948 | 3 |  |  |  |  | 15 | $self->RemoveNewValuesForGroup($tag); | 
| 949 | 3 | 50 |  |  |  | 9 | $verbose and print $out "  Deleting tags in: @del\n"; | 
| 950 | 3 |  |  |  |  | 6 | ++$numSet; | 
| 951 | 3 |  |  |  |  | 12 | next; | 
| 952 |  |  |  |  |  |  | } | 
| 953 | 4029 |  |  |  |  | 6510 | $noConv = 1;    # value is not defined, so don't do conversion | 
| 954 |  |  |  |  |  |  | } | 
| 955 |  |  |  |  |  |  | # apply inverse PrintConv and ValueConv conversions | 
| 956 |  |  |  |  |  |  | # save ValueConv setting for use in ConvInv() | 
| 957 | 32460 | 100 |  |  |  | 62154 | unless ($noConv) { | 
| 958 |  |  |  |  |  |  | # set default conversion type used by ConvInv() and CHECK_PROC routines | 
| 959 | 28203 |  |  |  |  | 60585 | $$self{ConvType} = $convType; | 
| 960 | 28203 |  |  |  |  | 39764 | my $e; | 
| 961 | 28203 |  |  |  |  | 85055 | ($val,$e) = $self->ConvInv($val,$tagInfo,$tag,$wgrp1,$$self{ConvType},$wantGroup); | 
| 962 | 28203 | 100 |  |  |  | 68528 | if (defined $e) { | 
| 963 |  |  |  |  |  |  | # empty error string causes error to be ignored without setting the value | 
| 964 | 8476 | 100 |  |  |  | 20414 | $e or goto WriteAlso; # GOTO! | 
| 965 | 8459 |  |  |  |  | 16274 | $err = $e; | 
| 966 |  |  |  |  |  |  | } | 
| 967 |  |  |  |  |  |  | } | 
| 968 | 32443 | 100 | 100 |  |  | 92393 | if (not defined $val and defined $value) { | 
| 969 |  |  |  |  |  |  | # if value conversion failed, we must still add a NEW_VALUE | 
| 970 |  |  |  |  |  |  | # entry for this tag it it was a DelValue | 
| 971 | 2805 | 50 |  |  |  | 10944 | next unless $options{DelValue}; | 
| 972 | 0 |  |  |  |  | 0 | $val = 'xxx never delete xxx'; | 
| 973 |  |  |  |  |  |  | } | 
| 974 | 29638 | 100 |  |  |  | 85823 | $$self{NEW_VALUE} or $$self{NEW_VALUE} = { }; | 
| 975 | 29638 | 100 |  |  |  | 68288 | if ($options{Replace}) { | 
| 976 |  |  |  |  |  |  | # delete the previous new value | 
| 977 | 14215 |  |  |  |  | 64479 | $self->GetNewValueHash($tagInfo, $writeGroup, 'delete', $options{ProtectSaved}); | 
| 978 |  |  |  |  |  |  | # also delete related tag previous new values | 
| 979 | 14215 | 100 |  |  |  | 40211 | if ($$tagInfo{WriteAlso}) { | 
| 980 | 25 |  |  |  |  | 132 | $$self{INDENT2} = '+'; | 
| 981 | 25 |  |  |  |  | 116 | my ($wgrp, $wtag); | 
| 982 | 25 | 100 | 66 |  |  | 185 | if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) { | 
|  |  |  | 66 |  |  |  |  | 
| 983 | 6 |  |  |  |  | 20 | $wgrp = $writeGroup . ':'; | 
| 984 |  |  |  |  |  |  | } else { | 
| 985 | 19 |  |  |  |  | 51 | $wgrp = ''; | 
| 986 |  |  |  |  |  |  | } | 
| 987 | 25 |  |  |  |  | 58 | foreach $wtag (sort keys %{$$tagInfo{WriteAlso}}) { | 
|  | 25 |  |  |  |  | 203 |  | 
| 988 | 91 |  |  |  |  | 448 | my ($n,$e) = $self->SetNewValue($wgrp . $wtag, undef, Replace=>2); | 
| 989 | 91 |  |  |  |  | 262 | $numSet += $n; | 
| 990 |  |  |  |  |  |  | } | 
| 991 | 25 |  |  |  |  | 125 | $$self{INDENT2} = ''; | 
| 992 |  |  |  |  |  |  | } | 
| 993 | 14215 | 100 |  |  |  | 35355 | $options{Replace} == 2 and ++$numSet, next; | 
| 994 |  |  |  |  |  |  | } | 
| 995 |  |  |  |  |  |  |  | 
| 996 | 29364 | 100 | 33 |  |  | 72561 | if (defined $val) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 997 |  |  |  |  |  |  | # we are editing this tag, so create a NEW_VALUE hash entry | 
| 998 |  |  |  |  |  |  | my $nvHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create', | 
| 999 | 19885 |  | 66 |  |  | 81389 | $options{ProtectSaved}, ($options{DelValue} and not $shift)); | 
| 1000 |  |  |  |  |  |  | # ignore new values protected with ProtectSaved | 
| 1001 | 19885 | 50 |  |  |  | 51181 | $nvHash or ++$numSet, next; # (increment $numSet to avoid warning) | 
| 1002 | 19885 | 100 | 100 |  |  | 51867 | $$nvHash{NoReplace} = 1 if $$tagInfo{List} and not $options{Replace}; | 
| 1003 | 19885 |  |  |  |  | 41108 | $$nvHash{WantGroup} = $wantGroup; | 
| 1004 | 19885 | 100 |  |  |  | 37579 | $$nvHash{EditOnly} = 1 if $editOnly; | 
| 1005 |  |  |  |  |  |  | # save maker note information if writing maker notes | 
| 1006 | 19885 | 100 |  |  |  | 44640 | if ($$tagInfo{MakerNotes}) { | 
| 1007 | 22 |  |  |  |  | 123 | $$nvHash{MAKER_NOTE_FIXUP} = $$self{MAKER_NOTE_FIXUP}; | 
| 1008 |  |  |  |  |  |  | } | 
| 1009 | 19885 | 100 | 100 |  |  | 100328 | if ($createOnly) {  # create only (never edit) | 
|  |  | 100 | 100 |  |  |  |  | 
| 1010 |  |  |  |  |  |  | # empty item in DelValue list to never edit existing value | 
| 1011 | 49 |  |  |  |  | 145 | $$nvHash{DelValue} = [ '' ]; | 
| 1012 | 49 |  |  |  |  | 118 | $$nvHash{CreateOnly} = 1; | 
| 1013 |  |  |  |  |  |  | } elsif ($options{DelValue} or $addValue or $shift) { | 
| 1014 |  |  |  |  |  |  | # flag any AddValue or DelValue by creating the DelValue list | 
| 1015 | 227 | 100 |  |  |  | 846 | $$nvHash{DelValue} or $$nvHash{DelValue} = [ ]; | 
| 1016 | 227 | 100 |  |  |  | 614 | if ($shift) { | 
|  |  | 100 |  |  |  |  |  | 
| 1017 |  |  |  |  |  |  | # add shift value to list | 
| 1018 | 160 |  |  |  |  | 475 | $$nvHash{Shift} = $val; | 
| 1019 |  |  |  |  |  |  | } elsif ($options{DelValue}) { | 
| 1020 |  |  |  |  |  |  | # don't create if we are replacing a specific value | 
| 1021 | 54 | 100 | 100 |  |  | 220 | $$nvHash{IsCreating} = 0 unless $val eq '' or $$tagInfo{List}; | 
| 1022 |  |  |  |  |  |  | # add delete value to list | 
| 1023 | 54 | 100 |  |  |  | 87 | push @{$$nvHash{DelValue}}, ref $val eq 'ARRAY' ? @$val : $val; | 
|  | 54 |  |  |  |  | 231 |  | 
| 1024 | 54 | 50 |  |  |  | 389 | if ($verbose > 1) { | 
| 1025 | 0 | 0 |  |  |  | 0 | my $verb = $permanent ? 'Replacing' : 'Deleting'; | 
| 1026 | 0 | 0 |  |  |  | 0 | my $fromList = $$tagInfo{List} ? ' from list' : ''; | 
| 1027 | 0 | 0 |  |  |  | 0 | my @vals = (ref $val eq 'ARRAY' ? @$val : $val); | 
| 1028 | 0 |  |  |  |  | 0 | foreach (@vals) { | 
| 1029 | 0 | 0 |  |  |  | 0 | if (ref $_ eq 'HASH') { | 
| 1030 | 0 |  |  |  |  | 0 | require 'Image/ExifTool/XMPStruct.pl'; | 
| 1031 | 0 |  |  |  |  | 0 | $_ = Image::ExifTool::XMP::SerializeStruct($_); | 
| 1032 |  |  |  |  |  |  | } | 
| 1033 | 0 |  |  |  |  | 0 | print $out "$$self{INDENT2}$verb $wgrp1:$tag$fromList if value is '${_}'\n"; | 
| 1034 |  |  |  |  |  |  | } | 
| 1035 |  |  |  |  |  |  | } | 
| 1036 |  |  |  |  |  |  | } | 
| 1037 |  |  |  |  |  |  | } | 
| 1038 |  |  |  |  |  |  | # set priority flag to add only the high priority info | 
| 1039 |  |  |  |  |  |  | # (will only create the priority tag if it doesn't exist, | 
| 1040 |  |  |  |  |  |  | #  others get changed only if they already exist) | 
| 1041 | 19885 | 100 |  |  |  | 57911 | my $prf = defined $$tagInfo{Preferred} ? $$tagInfo{Preferred} : $$tagInfo{Table}{PREFERRED}; | 
| 1042 |  |  |  |  |  |  | # hack to prefer only a single tag in the QuickTime group | 
| 1043 | 19885 | 100 |  |  |  | 56350 | if ($$tagInfo{Table}{GROUPS}{0} eq 'QuickTime') { | 
| 1044 | 660 | 100 |  |  |  | 2873 | $prf = 0 if $tagPriority{$tagInfo} < $highestQT{$lcTag}; | 
| 1045 |  |  |  |  |  |  | } | 
| 1046 | 19885 | 100 | 100 |  |  | 67335 | if ($$pref{$tagInfo} or $prf) { | 
| 1047 | 9209 | 100 | 100 |  |  | 47006 | if ($permanent or $shift) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 1048 |  |  |  |  |  |  | # don't create permanent or Shift-ed tag but define IsCreating | 
| 1049 |  |  |  |  |  |  | # so we know that it is the preferred tag | 
| 1050 | 5447 |  |  |  |  | 11803 | $$nvHash{IsCreating} = 0; | 
| 1051 |  |  |  |  |  |  | } elsif (($$tagInfo{List} and not $options{DelValue}) or | 
| 1052 |  |  |  |  |  |  | not ($$nvHash{DelValue} and @{$$nvHash{DelValue}}) or | 
| 1053 |  |  |  |  |  |  | # also create tag if any DelValue value is empty ('') | 
| 1054 | 58 |  |  |  |  | 436 | grep(/^$/,@{$$nvHash{DelValue}})) | 
| 1055 |  |  |  |  |  |  | { | 
| 1056 | 3748 | 100 |  |  |  | 11846 | $$nvHash{IsCreating} = $editOnly ? 0 : ($editGroup ? 2 : 1); | 
|  |  | 100 |  |  |  |  |  | 
| 1057 |  |  |  |  |  |  | # add to hash of groups where this tag is being created | 
| 1058 | 3748 | 100 | 100 |  |  | 13505 | $createGroups or $createGroups = $options{CreateGroups} || { }; | 
| 1059 | 3748 |  |  |  |  | 14463 | $$createGroups{$self->GetGroup($tagInfo, 0)} = 1; | 
| 1060 | 3748 |  |  |  |  | 11014 | $$nvHash{CreateGroups} = $createGroups; | 
| 1061 |  |  |  |  |  |  | } | 
| 1062 |  |  |  |  |  |  | } | 
| 1063 | 19885 | 100 |  |  |  | 52399 | if ($$nvHash{IsCreating}) { | 
|  |  | 100 |  |  |  |  |  | 
| 1064 | 3738 | 100 |  |  |  | 5962 | if (%{$$self{DEL_GROUP}}) { | 
|  | 3738 |  |  |  |  | 11131 |  | 
| 1065 | 227 |  |  |  |  | 441 | my ($grp, @grps); | 
| 1066 | 227 |  |  |  |  | 334 | foreach $grp (keys %{$$self{DEL_GROUP}}) { | 
|  | 227 |  |  |  |  | 3309 |  | 
| 1067 | 12589 | 100 |  |  |  | 23864 | next if $$self{DEL_GROUP}{$grp} == 2; | 
| 1068 |  |  |  |  |  |  | # set flag indicating tags were written after this group was deleted | 
| 1069 | 354 |  |  |  |  | 485 | $$self{DEL_GROUP}{$grp} = 2; | 
| 1070 | 354 |  |  |  |  | 568 | push @grps, $grp; | 
| 1071 |  |  |  |  |  |  | } | 
| 1072 | 227 | 100 | 66 |  |  | 1183 | if ($verbose > 1 and @grps) { | 
| 1073 | 1 |  |  |  |  | 5 | @grps = sort @grps; | 
| 1074 | 1 |  |  |  |  | 10 | print $out "  Writing new tags after deleting groups: @grps\n"; | 
| 1075 |  |  |  |  |  |  | } | 
| 1076 |  |  |  |  |  |  | } | 
| 1077 |  |  |  |  |  |  | } elsif ($createOnly) { | 
| 1078 | 22 | 100 |  |  |  | 95 | $noCreate = $permanent ? 'permanent' : ($$tagInfo{Avoid} ? 'avoided' : ''); | 
|  |  | 100 |  |  |  |  |  | 
| 1079 | 22 | 50 |  |  |  | 67 | $noCreate or $noCreate = $shift ? 'shifting' : 'not preferred'; | 
|  |  | 100 |  |  |  |  |  | 
| 1080 | 22 | 50 |  |  |  | 63 | $verbose > 2 and print $out "Not creating $wgrp1:$tag ($noCreate)\n"; | 
| 1081 | 22 |  |  |  |  | 73 | next;   # nothing to do (not creating and not editing) | 
| 1082 |  |  |  |  |  |  | } | 
| 1083 | 19863 | 100 | 100 |  |  | 69554 | if ($shift or not $options{DelValue}) { | 
| 1084 | 19809 | 100 |  |  |  | 63415 | $$nvHash{Value} or $$nvHash{Value} = [ ]; | 
| 1085 | 19809 | 100 | 33 |  |  | 47199 | if (not $$tagInfo{List}) { | 
|  |  | 50 |  |  |  |  |  | 
| 1086 |  |  |  |  |  |  | # not a List tag -- overwrite existing value | 
| 1087 | 19293 |  |  |  |  | 46862 | $$nvHash{Value}[0] = $val; | 
| 1088 | 0 |  |  |  |  | 0 | } elsif (defined $$nvHash{AddBefore} and @{$$nvHash{Value}} >= $$nvHash{AddBefore}) { | 
| 1089 |  |  |  |  |  |  | # values from a later argument have been added (ie. Replace=0) | 
| 1090 |  |  |  |  |  |  | # to this list, so the new values should come before these | 
| 1091 | 0 | 0 |  |  |  | 0 | splice @{$$nvHash{Value}}, -$$nvHash{AddBefore}, 0, ref $val eq 'ARRAY' ? @$val : $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1092 |  |  |  |  |  |  | } else { | 
| 1093 |  |  |  |  |  |  | # add at end of existing list | 
| 1094 | 516 | 100 |  |  |  | 918 | push @{$$nvHash{Value}}, ref $val eq 'ARRAY' ? @$val : $val; | 
|  | 516 |  |  |  |  | 2264 |  | 
| 1095 |  |  |  |  |  |  | } | 
| 1096 | 19809 | 100 |  |  |  | 50106 | if ($verbose > 1) { | 
| 1097 | 22 |  |  |  |  | 39 | my $ifExists; | 
| 1098 | 22 | 50 |  |  |  | 55 | if ($$tagInfo{IsComposite}) { | 
| 1099 |  |  |  |  |  |  | # (composite tags don't technically exist in the file) | 
| 1100 | 0 | 0 |  |  |  | 0 | if ($$tagInfo{WriteAlso}) { | 
| 1101 | 0 |  |  |  |  | 0 | $ifExists = ' (+' . join(',+',sort keys %{$$tagInfo{WriteAlso}}) . '):'; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1102 |  |  |  |  |  |  | } else { | 
| 1103 | 0 |  |  |  |  | 0 | $ifExists = ''; | 
| 1104 |  |  |  |  |  |  | } | 
| 1105 |  |  |  |  |  |  | } else { | 
| 1106 |  |  |  |  |  |  | $ifExists = $$nvHash{IsCreating} ? ( $createOnly ? | 
| 1107 |  |  |  |  |  |  | ($$nvHash{IsCreating} == 2 ? | 
| 1108 |  |  |  |  |  |  | " if $writeGroup exists and tag doesn't" : | 
| 1109 |  |  |  |  |  |  | " if tag doesn't exist") : | 
| 1110 |  |  |  |  |  |  | ($$nvHash{IsCreating} == 2 ? " if $writeGroup exists" : '')) : | 
| 1111 | 22 | 0 | 33 |  |  | 104 | (($$nvHash{DelValue} and @{$$nvHash{DelValue}}) ? | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 1112 |  |  |  |  |  |  | ' if tag was deleted' : ' if tag exists'); | 
| 1113 |  |  |  |  |  |  | } | 
| 1114 | 22 | 50 |  |  |  | 69 | my $verb = ($shift ? 'Shifting' : ($addValue ? 'Adding' : 'Writing')); | 
|  |  | 50 |  |  |  |  |  | 
| 1115 | 22 |  |  |  |  | 114 | print $out "$$self{INDENT2}$verb $wgrp1:$tag$ifExists\n"; | 
| 1116 |  |  |  |  |  |  | } | 
| 1117 |  |  |  |  |  |  | } | 
| 1118 |  |  |  |  |  |  | } elsif ($permanent) { | 
| 1119 | 5556 |  |  |  |  | 12908 | $err = "Can't delete Permanent tag $wgrp1:$tag"; | 
| 1120 | 5556 | 50 |  |  |  | 11699 | $verbose > 1 and print $out "$err\n"; | 
| 1121 | 5556 |  |  |  |  | 17996 | next; | 
| 1122 |  |  |  |  |  |  | } elsif ($addValue or $options{DelValue}) { | 
| 1123 | 0 | 0 |  |  |  | 0 | $verbose > 1 and print $out "Adding/Deleting nothing does nothing\n"; | 
| 1124 | 0 |  |  |  |  | 0 | next; | 
| 1125 |  |  |  |  |  |  | } else { | 
| 1126 |  |  |  |  |  |  | # create empty new value hash entry to delete this tag | 
| 1127 | 3923 |  |  |  |  | 11965 | $self->GetNewValueHash($tagInfo, $writeGroup, 'delete'); | 
| 1128 | 3923 |  |  |  |  | 7837 | my $nvHash = $self->GetNewValueHash($tagInfo, $writeGroup, 'create'); | 
| 1129 | 3923 |  |  |  |  | 9663 | $$nvHash{WantGroup} = $wantGroup; | 
| 1130 | 3923 | 50 |  |  |  | 9289 | $verbose > 1 and print $out "$$self{INDENT2}Deleting $wgrp1:$tag\n"; | 
| 1131 |  |  |  |  |  |  | } | 
| 1132 | 23786 | 100 |  |  |  | 46671 | $$setTags{$tagInfo} = 1 if $setTags; | 
| 1133 | 23786 | 100 |  |  |  | 59431 | $prioritySet = 1 if $$pref{$tagInfo}; | 
| 1134 | 23806 |  |  |  |  | 36310 | WriteAlso: | 
| 1135 |  |  |  |  |  |  | ++$numSet; | 
| 1136 |  |  |  |  |  |  | # also write related tags | 
| 1137 | 23806 |  |  |  |  | 42905 | my $writeAlso = $$tagInfo{WriteAlso}; | 
| 1138 | 23806 | 100 |  |  |  | 77997 | if ($writeAlso) { | 
| 1139 | 76 |  |  |  |  | 342 | $$self{INDENT2} = '+';  # indicate related tag with a leading "+" | 
| 1140 | 76 |  |  |  |  | 228 | my ($wgrp, $wtag, $n); | 
| 1141 | 76 | 100 | 66 |  |  | 668 | if ($$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All' and $writeGroup) { | 
|  |  |  | 66 |  |  |  |  | 
| 1142 | 46 |  |  |  |  | 148 | $wgrp = $writeGroup . ':'; | 
| 1143 |  |  |  |  |  |  | } else { | 
| 1144 | 30 |  |  |  |  | 88 | $wgrp = ''; | 
| 1145 |  |  |  |  |  |  | } | 
| 1146 | 76 |  |  |  |  | 472 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 1147 | 76 |  |  |  |  | 583 | foreach $wtag (sort keys %$writeAlso) { | 
| 1148 |  |  |  |  |  |  | my %opts = ( | 
| 1149 |  |  |  |  |  |  | Type => 'ValueConv', | 
| 1150 |  |  |  |  |  |  | Protected   => $protected | 0x02, | 
| 1151 |  |  |  |  |  |  | AddValue    => $addValue, | 
| 1152 |  |  |  |  |  |  | DelValue    => $options{DelValue}, | 
| 1153 |  |  |  |  |  |  | Shift       => $options{Shift}, | 
| 1154 |  |  |  |  |  |  | Replace     => $options{Replace},   # handle lists properly | 
| 1155 | 243 |  |  |  |  | 2046 | CreateGroups=> $createGroups, | 
| 1156 |  |  |  |  |  |  | SetTags     => \%alsoWrote,         # remember tags already written | 
| 1157 |  |  |  |  |  |  | ); | 
| 1158 | 243 |  |  |  |  | 577 | undef $evalWarning; | 
| 1159 |  |  |  |  |  |  | #### eval WriteAlso ($val,%opts) | 
| 1160 | 243 |  |  |  |  | 19215 | my $v = eval $$writeAlso{$wtag}; | 
| 1161 |  |  |  |  |  |  | # we wanted to do the eval in case there are side effect, but we | 
| 1162 |  |  |  |  |  |  | # don't want to write a value for a tag that is being deleted: | 
| 1163 | 243 | 100 |  |  |  | 1286 | undef $v unless defined $val; | 
| 1164 | 243 | 50 |  |  |  | 767 | $@ and $evalWarning = $@; | 
| 1165 | 243 | 50 |  |  |  | 680 | unless ($evalWarning) { | 
| 1166 | 243 |  |  |  |  | 1950 | ($n,$evalWarning) = $self->SetNewValue($wgrp . $wtag, $v, %opts); | 
| 1167 | 243 |  |  |  |  | 768 | $numSet += $n; | 
| 1168 |  |  |  |  |  |  | # count this as being set if any related tag is set | 
| 1169 | 243 | 100 | 100 |  |  | 1490 | $prioritySet = 1 if $n and $$pref{$tagInfo}; | 
| 1170 |  |  |  |  |  |  | } | 
| 1171 | 243 | 100 | 66 |  |  | 1102 | if ($evalWarning and (not $err or $verbose > 2)) { | 
|  |  |  | 66 |  |  |  |  | 
| 1172 | 9 |  |  |  |  | 39 | my $str = CleanWarning(); | 
| 1173 | 9 | 50 |  |  |  | 31 | if ($str) { | 
| 1174 | 9 | 50 |  |  |  | 63 | $str .= " for $wtag" unless $str =~ / for [-\w:]+$/; | 
| 1175 | 9 |  |  |  |  | 34 | $str .= " in $wgrp1:$tag (WriteAlso)"; | 
| 1176 | 9 | 50 |  |  |  | 28 | $err or $err = $str; | 
| 1177 | 9 | 50 |  |  |  | 50 | print $out "$str\n" if $verbose > 2; | 
| 1178 |  |  |  |  |  |  | } | 
| 1179 |  |  |  |  |  |  | } | 
| 1180 |  |  |  |  |  |  | } | 
| 1181 | 76 |  |  |  |  | 741 | $$self{INDENT2} = ''; | 
| 1182 |  |  |  |  |  |  | } | 
| 1183 |  |  |  |  |  |  | } | 
| 1184 |  |  |  |  |  |  | # print warning if we couldn't set our priority tag | 
| 1185 | 4220 | 100 | 100 |  |  | 25445 | if (defined $err and not $prioritySet) { | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 1186 | 85 | 50 | 33 |  |  | 577 | warn "$err\n" if $err and not wantarray; | 
| 1187 |  |  |  |  |  |  | } elsif (not $numSet) { | 
| 1188 | 619 | 100 |  |  |  | 2391 | my $pre = $wantGroup ? $wantGroup . ':' : ''; | 
| 1189 | 619 | 100 |  |  |  | 1884 | if ($wasProtected) { | 
|  |  | 100 |  |  |  |  |  | 
| 1190 | 372 |  |  |  |  | 779 | $verbose = 0;   # we already printed this verbose message | 
| 1191 | 372 | 100 | 100 |  |  | 2172 | unless ($options{Replace} and $options{Replace} == 2) { | 
| 1192 | 360 |  |  |  |  | 1434 | $err = "Sorry, $pre$tag is $wasProtected for writing"; | 
| 1193 |  |  |  |  |  |  | } | 
| 1194 |  |  |  |  |  |  | } elsif (not $listOnly) { | 
| 1195 | 240 | 50 | 33 |  |  | 1717 | if ($origTag =~ /[?*]/) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 1196 | 0 | 0 |  |  |  | 0 | if ($noCreate) { | 
|  |  | 0 |  |  |  |  |  | 
| 1197 | 0 |  |  |  |  | 0 | $err = "No tags matching 'pre${origTag}' will be created"; | 
| 1198 | 0 |  |  |  |  | 0 | $verbose = 0;   # (already printed) | 
| 1199 |  |  |  |  |  |  | } elsif ($foundMatch) { | 
| 1200 | 0 |  |  |  |  | 0 | $err = "Sorry, no writable tags matching '$pre${origTag}'"; | 
| 1201 |  |  |  |  |  |  | } else { | 
| 1202 | 0 |  |  |  |  | 0 | $err = "No matching tags for '$pre${origTag}'"; | 
| 1203 |  |  |  |  |  |  | } | 
| 1204 |  |  |  |  |  |  | } elsif ($noCreate) { | 
| 1205 | 0 |  |  |  |  | 0 | $err = "Not creating $pre$tag"; | 
| 1206 | 0 |  |  |  |  | 0 | $verbose = 0;   # (already printed) | 
| 1207 |  |  |  |  |  |  | } elsif ($foundMatch) { | 
| 1208 | 0 |  |  |  |  | 0 | $err = "Sorry, $pre$tag is not writable"; | 
| 1209 |  |  |  |  |  |  | } elsif ($wantGroup and @matchingTags) { | 
| 1210 | 240 |  |  |  |  | 748 | $err = "Sorry, $pre$tag doesn't exist or isn't writable"; | 
| 1211 |  |  |  |  |  |  | } else { | 
| 1212 | 0 |  |  |  |  | 0 | $err = "Tag '$pre${tag}' is not defined"; | 
| 1213 |  |  |  |  |  |  | } | 
| 1214 |  |  |  |  |  |  | } | 
| 1215 | 619 | 100 |  |  |  | 1657 | if ($err) { | 
| 1216 | 600 | 50 |  |  |  | 1653 | $verbose > 2 and print $out "$err\n"; | 
| 1217 | 600 | 50 |  |  |  | 1520 | warn "$err\n" unless wantarray; | 
| 1218 |  |  |  |  |  |  | } | 
| 1219 |  |  |  |  |  |  | } elsif ($$self{CHECK_WARN}) { | 
| 1220 | 0 |  |  |  |  | 0 | $err = $$self{CHECK_WARN}; | 
| 1221 | 0 | 0 |  |  |  | 0 | $verbose > 2 and print $out "$err\n"; | 
| 1222 |  |  |  |  |  |  | } elsif ($err and not $verbose) { | 
| 1223 | 437 |  |  |  |  | 992 | undef $err; | 
| 1224 |  |  |  |  |  |  | } | 
| 1225 | 4220 | 100 |  |  |  | 42842 | return ($numSet, $err) if wantarray; | 
| 1226 | 423 |  |  |  |  | 32279 | return $numSet; | 
| 1227 |  |  |  |  |  |  | } | 
| 1228 |  |  |  |  |  |  |  | 
| 1229 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1230 |  |  |  |  |  |  | # set new values from information in specified file | 
| 1231 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) source file name or reference, etc | 
| 1232 |  |  |  |  |  |  | #         2-N) List of tags to set (or all if none specified), or reference(s) to | 
| 1233 |  |  |  |  |  |  | #         hash for options to pass to SetNewValue.  The Replace option defaults | 
| 1234 |  |  |  |  |  |  | #         to 1 for SetNewValuesFromFile -- set this to 0 to allow multiple tags | 
| 1235 |  |  |  |  |  |  | #         to be copied to a list | 
| 1236 |  |  |  |  |  |  | # Returns: Hash of information set successfully (includes Warning or Error messages) | 
| 1237 |  |  |  |  |  |  | # Notes: Tag names may contain a group prefix, a leading '-' to exclude from copy, | 
| 1238 |  |  |  |  |  |  | #        and/or a trailing '#' to copy the ValueConv value.  The tag name '*' may | 
| 1239 |  |  |  |  |  |  | #        be used to represent all tags in a group.  An optional destination tag | 
| 1240 |  |  |  |  |  |  | #        may be specified with '>DSTTAG' ('DSTTAG | 
| 1241 |  |  |  |  |  |  | #        case the source tag may also be an expression involving tag names). | 
| 1242 |  |  |  |  |  |  | sub SetNewValuesFromFile($$;@) | 
| 1243 |  |  |  |  |  |  | { | 
| 1244 | 59 |  |  | 59 | 1 | 1245 | local $_; | 
| 1245 | 59 |  |  |  |  | 293 | my ($self, $srcFile, @setTags) = @_; | 
| 1246 | 59 |  |  |  |  | 398 | my ($key, $tag, @exclude, @reqTags); | 
| 1247 |  |  |  |  |  |  |  | 
| 1248 |  |  |  |  |  |  | # get initial SetNewValuesFromFile options | 
| 1249 | 59 |  |  |  |  | 330 | my %opts = ( Replace => 1 );    # replace existing list items by default | 
| 1250 | 59 |  |  |  |  | 354 | while (ref $setTags[0] eq 'HASH') { | 
| 1251 | 1 |  |  |  |  | 3 | $_ = shift @setTags; | 
| 1252 | 1 |  |  |  |  | 6 | foreach $key (keys %$_) { | 
| 1253 | 1 |  |  |  |  | 7 | $opts{$key} = $$_{$key}; | 
| 1254 |  |  |  |  |  |  | } | 
| 1255 |  |  |  |  |  |  | } | 
| 1256 |  |  |  |  |  |  | # expand shortcuts | 
| 1257 | 59 | 100 |  |  |  | 433 | @setTags and ExpandShortcuts(\@setTags); | 
| 1258 | 59 |  |  |  |  | 458 | my $srcExifTool = new Image::ExifTool; | 
| 1259 |  |  |  |  |  |  | # set flag to indicate we are being called from inside SetNewValuesFromFile() | 
| 1260 | 59 |  |  |  |  | 388 | $$srcExifTool{TAGS_FROM_FILE} = 1; | 
| 1261 |  |  |  |  |  |  | # synchronize and increment the file sequence number | 
| 1262 | 59 |  |  |  |  | 295 | $$srcExifTool{FILE_SEQUENCE} = $$self{FILE_SEQUENCE}++; | 
| 1263 |  |  |  |  |  |  | # set options for our extraction tool | 
| 1264 | 59 |  |  |  |  | 173 | my $options = $$self{OPTIONS}; | 
| 1265 |  |  |  |  |  |  | # copy both structured and flattened tags by default (but flattened tags are "unsafe") | 
| 1266 | 59 | 50 |  |  |  | 309 | my $structOpt = defined $$options{Struct} ? $$options{Struct} : 2; | 
| 1267 |  |  |  |  |  |  | # copy structures only if no tags specified (since flattened tags are "unsafe") | 
| 1268 | 59 | 100 | 66 |  |  | 526 | $structOpt = 1 if $structOpt eq '2' and not @setTags; | 
| 1269 |  |  |  |  |  |  | # +------------------------------------------+ | 
| 1270 |  |  |  |  |  |  | # ! DON'T FORGET!!  Must consider each new   ! | 
| 1271 |  |  |  |  |  |  | # ! option to decide how it is handled here. ! | 
| 1272 |  |  |  |  |  |  | # +------------------------------------------+ | 
| 1273 |  |  |  |  |  |  | $srcExifTool->Options( | 
| 1274 |  |  |  |  |  |  | Binary          => 1, | 
| 1275 |  |  |  |  |  |  | Charset         => $$options{Charset}, | 
| 1276 |  |  |  |  |  |  | CharsetEXIF     => $$options{CharsetEXIF}, | 
| 1277 |  |  |  |  |  |  | CharsetFileName => $$options{CharsetFileName}, | 
| 1278 |  |  |  |  |  |  | CharsetID3      => $$options{CharsetID3}, | 
| 1279 |  |  |  |  |  |  | CharsetIPTC     => $$options{CharsetIPTC}, | 
| 1280 |  |  |  |  |  |  | CharsetPhotoshop=> $$options{CharsetPhotoshop}, | 
| 1281 |  |  |  |  |  |  | Composite       => $$options{Composite}, | 
| 1282 |  |  |  |  |  |  | CoordFormat     => $$options{CoordFormat} || '%d %d %.8f', # copy coordinates at high resolution unless otherwise specified | 
| 1283 |  |  |  |  |  |  | DateFormat      => $$options{DateFormat}, | 
| 1284 |  |  |  |  |  |  | Duplicates      => 1, | 
| 1285 |  |  |  |  |  |  | Escape          => $$options{Escape}, | 
| 1286 |  |  |  |  |  |  | # Exclude (set below) | 
| 1287 |  |  |  |  |  |  | ExtendedXMP     => $$options{ExtendedXMP}, | 
| 1288 |  |  |  |  |  |  | ExtractEmbedded => $$options{ExtractEmbedded}, | 
| 1289 |  |  |  |  |  |  | FastScan        => $$options{FastScan}, | 
| 1290 |  |  |  |  |  |  | Filter          => $$options{Filter}, | 
| 1291 |  |  |  |  |  |  | FixBase         => $$options{FixBase}, | 
| 1292 |  |  |  |  |  |  | GlobalTimeShift => $$options{GlobalTimeShift}, | 
| 1293 |  |  |  |  |  |  | HexTagIDs       => $$options{HexTagIDs}, | 
| 1294 |  |  |  |  |  |  | IgnoreMinorErrors=>$$options{IgnoreMinorErrors}, | 
| 1295 |  |  |  |  |  |  | IgnoreTags      => $$options{IgnoreTags}, | 
| 1296 |  |  |  |  |  |  | Lang            => $$options{Lang}, | 
| 1297 |  |  |  |  |  |  | LargeFileSupport=> $$options{LargeFileSupport}, | 
| 1298 |  |  |  |  |  |  | List            => 1, | 
| 1299 |  |  |  |  |  |  | ListItem        => $$options{ListItem}, | 
| 1300 |  |  |  |  |  |  | ListSep         => $$options{ListSep}, | 
| 1301 |  |  |  |  |  |  | MakerNotes      => $$options{FastScan} && $$options{FastScan} > 1 ? undef : 1, | 
| 1302 |  |  |  |  |  |  | MDItemTags      => $$options{MDItemTags}, | 
| 1303 |  |  |  |  |  |  | MissingTagValue => $$options{MissingTagValue}, | 
| 1304 |  |  |  |  |  |  | NoPDFList       => $$options{NoPDFList}, | 
| 1305 |  |  |  |  |  |  | Password        => $$options{Password}, | 
| 1306 |  |  |  |  |  |  | PrintConv       => $$options{PrintConv}, | 
| 1307 |  |  |  |  |  |  | QuickTimeUTC    => $$options{QuickTimeUTC}, | 
| 1308 |  |  |  |  |  |  | RequestAll      => $$options{RequestAll} || 1, # (is this still necessary now that RequestTags are being set?) | 
| 1309 |  |  |  |  |  |  | RequestTags     => $$options{RequestTags}, | 
| 1310 |  |  |  |  |  |  | SaveFormat      => $$options{SaveFormat}, | 
| 1311 |  |  |  |  |  |  | SavePath        => $$options{SavePath}, | 
| 1312 |  |  |  |  |  |  | ScanForXMP      => $$options{ScanForXMP}, | 
| 1313 |  |  |  |  |  |  | StrictDate      => defined $$options{StrictDate} ? $$options{StrictDate} : 1, | 
| 1314 |  |  |  |  |  |  | Struct          => $structOpt, | 
| 1315 |  |  |  |  |  |  | SystemTags      => $$options{SystemTags}, | 
| 1316 |  |  |  |  |  |  | TimeZone        => $$options{TimeZone}, | 
| 1317 |  |  |  |  |  |  | Unknown         => $$options{Unknown}, | 
| 1318 |  |  |  |  |  |  | UserParam       => $$options{UserParam}, | 
| 1319 |  |  |  |  |  |  | Validate        => $$options{Validate}, | 
| 1320 |  |  |  |  |  |  | XAttrTags       => $$options{XAttrTags}, | 
| 1321 |  |  |  |  |  |  | XMPAutoConv     => $$options{XMPAutoConv}, | 
| 1322 | 59 | 50 | 50 |  |  | 2590 | ); | 
|  |  | 50 | 33 |  |  |  |  | 
|  |  |  | 50 |  |  |  |  | 
| 1323 | 59 |  |  |  |  | 342 | $$srcExifTool{GLOBAL_TIME_OFFSET} = $$self{GLOBAL_TIME_OFFSET}; | 
| 1324 | 59 |  |  |  |  | 276 | $$srcExifTool{ALT_EXIFTOOL} = $$self{ALT_EXIFTOOL}; | 
| 1325 | 59 |  |  |  |  | 211 | foreach $tag (@setTags) { | 
| 1326 | 69 | 100 |  |  |  | 253 | next if ref $tag; | 
| 1327 | 68 | 100 |  |  |  | 296 | if ($tag =~ /^-(.*)/) { | 
| 1328 |  |  |  |  |  |  | # avoid extracting tags that are excluded | 
| 1329 | 7 |  |  |  |  | 33 | push @exclude, $1; | 
| 1330 | 7 |  |  |  |  | 23 | next; | 
| 1331 |  |  |  |  |  |  | } | 
| 1332 |  |  |  |  |  |  | # add specified tags to list of requested tags | 
| 1333 | 61 |  |  |  |  | 145 | $_ = $tag; | 
| 1334 | 61 | 100 |  |  |  | 682 | if (/(.+?)\s*(>|<)\s*(.+)/) { | 
| 1335 | 30 | 100 |  |  |  | 148 | if ($2 eq '>') { | 
| 1336 | 10 |  |  |  |  | 34 | $_ = $1; | 
| 1337 |  |  |  |  |  |  | } else { | 
| 1338 | 20 |  |  |  |  | 61 | $_ = $3; | 
| 1339 | 20 | 100 |  |  |  | 128 | /\$/ and push(@reqTags, /\$\{?(?:[-\w]+:)*([-\w?*]+)/g), next; | 
| 1340 |  |  |  |  |  |  | } | 
| 1341 |  |  |  |  |  |  | } | 
| 1342 | 54 | 50 |  |  |  | 559 | push @reqTags, $2 if /(^|:)([-\w?*]+)#?$/; | 
| 1343 |  |  |  |  |  |  | } | 
| 1344 | 59 | 100 |  |  |  | 303 | if (@exclude) { | 
| 1345 | 6 |  |  |  |  | 39 | ExpandShortcuts(\@exclude, 1); | 
| 1346 | 6 |  |  |  |  | 64 | $srcExifTool->Options(Exclude => \@exclude); | 
| 1347 |  |  |  |  |  |  | } | 
| 1348 | 59 | 100 |  |  |  | 443 | $srcExifTool->Options(RequestTags => \@reqTags) if @reqTags; | 
| 1349 | 59 |  |  |  |  | 274 | my $printConv = $$options{PrintConv}; | 
| 1350 | 59 | 50 |  |  |  | 330 | if ($opts{Type}) { | 
| 1351 |  |  |  |  |  |  | # save source type separately because it may be different than dst Type | 
| 1352 | 0 |  |  |  |  | 0 | $opts{SrcType} = $opts{Type}; | 
| 1353 |  |  |  |  |  |  | # override PrintConv option with initial Type if given | 
| 1354 | 0 | 0 |  |  |  | 0 | $printConv = ($opts{Type} eq 'PrintConv' ? 1 : 0); | 
| 1355 | 0 |  |  |  |  | 0 | $srcExifTool->Options(PrintConv => $printConv); | 
| 1356 |  |  |  |  |  |  | } | 
| 1357 | 59 | 100 |  |  |  | 265 | my $srcType = $printConv ? 'PrintConv' : 'ValueConv'; | 
| 1358 |  |  |  |  |  |  |  | 
| 1359 |  |  |  |  |  |  | # get all tags from source file (including MakerNotes block) | 
| 1360 | 59 |  |  |  |  | 307 | my $info = $srcExifTool->ImageInfo($srcFile); | 
| 1361 | 59 | 50 | 33 |  |  | 442 | return $info if $$info{Error} and $$info{Error} eq 'Error opening file'; | 
| 1362 | 59 |  |  |  |  | 258 | delete $$srcExifTool{VALUE}{Error}; # delete so we can check this later | 
| 1363 |  |  |  |  |  |  |  | 
| 1364 |  |  |  |  |  |  | # sort tags in reverse order so we get priority tag last | 
| 1365 | 59 |  |  |  |  | 5909 | my @tags = reverse sort keys %$info; | 
| 1366 |  |  |  |  |  |  | # | 
| 1367 |  |  |  |  |  |  | # simply transfer all tags from source image if no tags specified | 
| 1368 |  |  |  |  |  |  | # | 
| 1369 | 59 | 100 |  |  |  | 1138 | unless (@setTags) { | 
| 1370 |  |  |  |  |  |  | # transfer maker note information to this object | 
| 1371 | 15 |  |  |  |  | 86 | $$self{MAKER_NOTE_FIXUP} = $$srcExifTool{MAKER_NOTE_FIXUP}; | 
| 1372 | 15 |  |  |  |  | 77 | $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER}; | 
| 1373 | 15 |  |  |  |  | 60 | foreach $tag (@tags) { | 
| 1374 |  |  |  |  |  |  | # don't try to set errors or warnings | 
| 1375 | 2649 | 100 |  |  |  | 11019 | next if $tag =~ /^(Error|Warning)\b/; | 
| 1376 |  |  |  |  |  |  | # get appropriate value type if necessary | 
| 1377 | 2645 | 50 | 33 |  |  | 7632 | if ($opts{SrcType} and $opts{SrcType} ne $srcType) { | 
| 1378 | 0 |  |  |  |  | 0 | $$info{$tag} = $srcExifTool->GetValue($tag, $opts{SrcType}); | 
| 1379 |  |  |  |  |  |  | } | 
| 1380 |  |  |  |  |  |  | # set value for this tag | 
| 1381 | 2645 |  |  |  |  | 12886 | my ($n, $e) = $self->SetNewValue($tag, $$info{$tag}, %opts); | 
| 1382 |  |  |  |  |  |  | # delete this tag if we couldn't set it | 
| 1383 | 2645 | 100 |  |  |  | 10360 | $n or delete $$info{$tag}; | 
| 1384 |  |  |  |  |  |  | } | 
| 1385 | 15 |  |  |  |  | 1430 | return $info; | 
| 1386 |  |  |  |  |  |  | } | 
| 1387 |  |  |  |  |  |  | # | 
| 1388 |  |  |  |  |  |  | # transfer specified tags in the proper order | 
| 1389 |  |  |  |  |  |  | # | 
| 1390 |  |  |  |  |  |  | # 1) loop through input list of tags to set, and build @setList | 
| 1391 | 44 |  |  |  |  | 195 | my (@setList, $set, %setMatches, $t, %altFiles); | 
| 1392 | 44 |  |  |  |  | 186 | foreach $t (@setTags) { | 
| 1393 | 69 | 100 |  |  |  | 299 | if (ref $t eq 'HASH') { | 
| 1394 |  |  |  |  |  |  | # update current options | 
| 1395 | 1 |  |  |  |  | 5 | foreach $key (keys %$t) { | 
| 1396 | 1 |  |  |  |  | 4 | $opts{$key} = $$t{$key}; | 
| 1397 |  |  |  |  |  |  | } | 
| 1398 | 1 |  |  |  |  | 5 | next; | 
| 1399 |  |  |  |  |  |  | } | 
| 1400 |  |  |  |  |  |  | # make a copy of the current options for this setTag | 
| 1401 |  |  |  |  |  |  | # (also use this hash to store expression and wildcard flags, EXPR and WILD) | 
| 1402 | 68 |  |  |  |  | 314 | my $opts = { %opts }; | 
| 1403 | 68 |  |  |  |  | 274 | $tag = lc $t;   # change tag/group names to all lower case | 
| 1404 | 68 |  |  |  |  | 205 | my (@fg, $grp, $dst, $dstGrp, $dstTag, $isExclude); | 
| 1405 |  |  |  |  |  |  | # handle redirection to another tag | 
| 1406 | 68 | 100 |  |  |  | 902 | if ($tag =~ /(.+?)\s*(>|<)\s*(.+)/) { | 
| 1407 | 30 |  |  |  |  | 92 | $dstGrp = ''; | 
| 1408 | 30 |  |  |  |  | 61 | my $opt; | 
| 1409 | 30 | 100 |  |  |  | 133 | if ($2 eq '>') { | 
| 1410 | 10 |  |  |  |  | 48 | ($tag, $dstTag) = ($1, $3); | 
| 1411 |  |  |  |  |  |  | # flag add and delete (eg. '+<' and '-<') redirections | 
| 1412 | 10 | 50 | 33 |  |  | 118 | $opt = $1 if $tag =~ s/\s*([-+])$// or $dstTag =~ s/^([-+])\s*//; | 
| 1413 |  |  |  |  |  |  | } else { | 
| 1414 | 20 |  |  |  |  | 90 | ($tag, $dstTag) = ($3, $1); | 
| 1415 | 20 | 50 |  |  |  | 112 | $opt = $1 if $dstTag =~ s/\s*([-+])$//; | 
| 1416 |  |  |  |  |  |  | # handle expressions | 
| 1417 | 20 | 100 |  |  |  | 93 | if ($tag =~ /\$/) { | 
| 1418 | 7 |  |  |  |  | 22 | $tag = $t;  # restore original case | 
| 1419 |  |  |  |  |  |  | # recover leading whitespace (except for initial single space) | 
| 1420 | 7 |  |  |  |  | 62 | $tag =~ s/(.+?)\s*(>|<) ?//; | 
| 1421 | 7 |  |  |  |  | 68 | $$opts{EXPR} = 1; # flag this expression | 
| 1422 |  |  |  |  |  |  | } else { | 
| 1423 |  |  |  |  |  |  | # (not sure why this is here because sign should be before '<') | 
| 1424 |  |  |  |  |  |  | # (--> allows "<+" or "<-", which is an undocumented feature) | 
| 1425 | 13 | 50 |  |  |  | 60 | $opt = $1 if $tag =~ s/^([-+])\s*//; | 
| 1426 |  |  |  |  |  |  | } | 
| 1427 |  |  |  |  |  |  | } | 
| 1428 | 30 | 100 |  |  |  | 152 | $$opts{Replace} = 0 if $dstTag =~ s/^\+//; | 
| 1429 |  |  |  |  |  |  | # validate tag name(s) | 
| 1430 | 30 | 50 | 66 |  |  | 219 | unless ($$opts{EXPR} or ValidTagName($tag)) { | 
| 1431 | 0 |  |  |  |  | 0 | $self->Warn("Invalid tag name '${tag}'. Use '=' not '<' to assign a tag value"); | 
| 1432 | 0 |  |  |  |  | 0 | next; | 
| 1433 |  |  |  |  |  |  | } | 
| 1434 | 30 | 50 |  |  |  | 125 | ValidTagName($dstTag) or $self->Warn("Invalid tag name '${dstTag}'"), next; | 
| 1435 |  |  |  |  |  |  | # translate '+' and '-' to appropriate SetNewValue option | 
| 1436 | 30 | 50 |  |  |  | 122 | if ($opt) { | 
| 1437 | 0 |  |  |  |  | 0 | $$opts{{ '+' => 'AddValue', '-' => 'DelValue' }->{$opt}} = 1; | 
| 1438 | 0 |  |  |  |  | 0 | $$opts{Shift} = 0;  # shift if shiftable | 
| 1439 |  |  |  |  |  |  | } | 
| 1440 | 30 | 100 |  |  |  | 192 | ($dstGrp, $dstTag) = ($1, $2) if $dstTag =~ /(.*):(.+)/; | 
| 1441 |  |  |  |  |  |  | # ValueConv may be specified separately on the destination with '#' | 
| 1442 | 30 | 50 |  |  |  | 147 | $$opts{Type} = 'ValueConv' if $dstTag =~ s/#$//; | 
| 1443 |  |  |  |  |  |  | # replace tag name of 'all' with '*' | 
| 1444 | 30 | 100 |  |  |  | 124 | $dstTag = '*' if $dstTag eq 'all'; | 
| 1445 |  |  |  |  |  |  | } else { | 
| 1446 | 38 | 50 |  |  |  | 224 | $$opts{Replace} = 0 if $tag =~ s/^\+//; | 
| 1447 |  |  |  |  |  |  | } | 
| 1448 | 68 | 100 |  |  |  | 303 | unless ($$opts{EXPR}) { | 
| 1449 | 61 |  |  |  |  | 261 | $isExclude = ($tag =~ s/^-//); | 
| 1450 | 61 | 100 |  |  |  | 362 | if ($tag =~ /(.*):(.+)/) { | 
| 1451 | 34 |  |  |  |  | 162 | ($grp, $tag) = ($1, $2); | 
| 1452 | 34 |  |  |  |  | 204 | foreach (split /:/, $grp) { | 
| 1453 |  |  |  |  |  |  | # save family/groups in list (ignoring 'all' and '*') | 
| 1454 | 35 | 50 | 33 |  |  | 338 | next unless length($_) and /^(\d+)?(.*)/; | 
| 1455 | 35 |  |  |  |  | 135 | my ($f, $g) = ($1, $2); | 
| 1456 | 35 | 50 | 33 |  |  | 305 | $f = 7 if (not $f or $f eq '7') and $g =~ s/^ID-//i; | 
|  |  |  | 33 |  |  |  |  | 
| 1457 | 35 | 50 | 33 |  |  | 178 | if ($g =~ /^file\d+$/i and (not $f or $f eq '8')) { | 
|  |  |  | 66 |  |  |  |  | 
| 1458 | 3 |  |  |  |  | 8 | $f = 8; | 
| 1459 | 3 |  |  |  |  | 14 | my $g8 = ucfirst $g; | 
| 1460 | 3 | 50 |  |  |  | 18 | if ($$srcExifTool{ALT_EXIFTOOL}{$g8}) { | 
| 1461 | 3 |  |  |  |  | 9 | $$opts{GROUP8} = $g8; | 
| 1462 | 3 | 100 |  |  |  | 12 | $altFiles{$g8} or $altFiles{$g8} = [ ]; | 
| 1463 |  |  |  |  |  |  | # save list of requested tags for this alternate ExifTool object | 
| 1464 | 3 |  |  |  |  | 5 | push @{$altFiles{$g8}}, "$grp:$tag"; | 
|  | 3 |  |  |  |  | 14 |  | 
| 1465 |  |  |  |  |  |  | } | 
| 1466 |  |  |  |  |  |  | } | 
| 1467 | 35 | 100 | 100 |  |  | 367 | push @fg, [ $f, $g ] unless $g eq '*' or $g eq 'all'; | 
| 1468 |  |  |  |  |  |  | } | 
| 1469 |  |  |  |  |  |  | } | 
| 1470 |  |  |  |  |  |  | # allow ValueConv to be specified by a '#' on the tag name | 
| 1471 | 61 | 50 |  |  |  | 281 | if ($tag =~ s/#$//) { | 
| 1472 | 0 |  |  |  |  | 0 | $$opts{SrcType} = 'ValueConv'; | 
| 1473 | 0 | 0 |  |  |  | 0 | $$opts{Type} = 'ValueConv' unless $dstTag; | 
| 1474 |  |  |  |  |  |  | } | 
| 1475 |  |  |  |  |  |  | # replace 'all' with '*' in tag and group names | 
| 1476 | 61 | 100 |  |  |  | 283 | $tag = '*' if $tag eq 'all'; | 
| 1477 |  |  |  |  |  |  | # allow wildcards in tag names (handle differently from all tags: '*') | 
| 1478 | 61 | 100 | 100 |  |  | 486 | if ($tag =~ /[?*]/ and $tag ne '*') { | 
| 1479 | 3 |  |  |  |  | 13 | $$opts{WILD} = 1;   # set flag indicating wildcards were used in source tag | 
| 1480 | 3 |  |  |  |  | 16 | $tag =~ s/\*/[-\\w]*/g; | 
| 1481 | 3 |  |  |  |  | 15 | $tag =~ s/\?/[-\\w]/g; | 
| 1482 |  |  |  |  |  |  | } | 
| 1483 |  |  |  |  |  |  | } | 
| 1484 |  |  |  |  |  |  | # redirect, exclude or set this tag (Note: @fg is empty if we don't care about the group) | 
| 1485 | 68 | 100 |  |  |  | 356 | if ($dstTag) { | 
|  |  | 100 |  |  |  |  |  | 
| 1486 |  |  |  |  |  |  | # redirect this tag | 
| 1487 | 30 | 50 |  |  |  | 97 | $isExclude and return { Error => "Can't redirect excluded tag" }; | 
| 1488 |  |  |  |  |  |  | # set destination group the same as source if necessary | 
| 1489 |  |  |  |  |  |  | # (removed in 7.72 so '-*:* | 
| 1490 |  |  |  |  |  |  | # $dstGrp = $grp if $dstGrp eq '*' and $grp; | 
| 1491 |  |  |  |  |  |  | # write to specified destination group/tag | 
| 1492 | 30 |  |  |  |  | 107 | $dst = [ $dstGrp, $dstTag ]; | 
| 1493 |  |  |  |  |  |  | } elsif ($isExclude) { | 
| 1494 |  |  |  |  |  |  | # implicitly assume '*' if first entry is an exclusion | 
| 1495 | 7 | 100 |  |  |  | 64 | unshift @setList, [ [ ], '*', [ '', '*' ], $opts ] unless @setList; | 
| 1496 |  |  |  |  |  |  | # exclude this tag by leaving $dst undefined | 
| 1497 |  |  |  |  |  |  | } else { | 
| 1498 | 31 | 100 | 100 |  |  | 283 | $dst = [ $grp || '', $$opts{WILD} ? '*' : $tag ]; # use same group name for dest | 
| 1499 |  |  |  |  |  |  | } | 
| 1500 |  |  |  |  |  |  | # save in reverse order so we don't set tags before an exclude | 
| 1501 | 68 |  |  |  |  | 392 | unshift @setList, [ \@fg, $tag, $dst, $opts ]; | 
| 1502 |  |  |  |  |  |  | } | 
| 1503 |  |  |  |  |  |  | # 1b) copy requested tags for each alternate ExifTool object | 
| 1504 | 44 |  |  |  |  | 161 | my $g8; | 
| 1505 | 44 |  |  |  |  | 254 | foreach $g8 (sort keys %altFiles) { | 
| 1506 |  |  |  |  |  |  | # request specific alternate tags to load them from the alternate ExifTool object | 
| 1507 | 1 |  |  |  |  | 8 | my $altInfo = $srcExifTool->GetInfo($altFiles{$g8}); | 
| 1508 |  |  |  |  |  |  | # add to tags list after dummy entry to signify start of tags for this alt file | 
| 1509 | 1 | 50 |  |  |  | 13 | if (%$altInfo) { | 
| 1510 | 1 |  |  |  |  | 12 | push @tags, 'Warning DUMMY', reverse sort keys %$altInfo; | 
| 1511 | 1 |  |  |  |  | 12 | $$info{$_} = $$altInfo{$_} foreach keys %$altInfo; | 
| 1512 |  |  |  |  |  |  | } | 
| 1513 |  |  |  |  |  |  | } | 
| 1514 |  |  |  |  |  |  | # 2) initialize lists of matching tags for each setTag | 
| 1515 | 44 |  |  |  |  | 181 | foreach $set (@setList) { | 
| 1516 | 69 | 100 |  |  |  | 455 | $$set[2] and $setMatches{$set} = [ ]; | 
| 1517 |  |  |  |  |  |  | } | 
| 1518 |  |  |  |  |  |  | # 3) loop through all tags in source image and save tags matching each setTag | 
| 1519 | 44 |  |  |  |  | 201 | my (%rtnInfo, $isAlt); | 
| 1520 | 44 |  |  |  |  | 148 | foreach $tag (@tags) { | 
| 1521 |  |  |  |  |  |  | # don't try to set errors or warnings | 
| 1522 | 6487 | 100 |  |  |  | 16104 | if ($tag =~ /^(Error|Warning)( |$)/) { | 
| 1523 | 14 | 100 |  |  |  | 59 | if ($tag eq 'Warning DUMMY') { | 
| 1524 | 1 |  |  |  |  | 10 | $isAlt = 1; # start of the alt tags | 
| 1525 |  |  |  |  |  |  | } else { | 
| 1526 | 13 |  |  |  |  | 46 | $rtnInfo{$tag} = $$info{$tag}; | 
| 1527 |  |  |  |  |  |  | } | 
| 1528 | 14 |  |  |  |  | 33 | next; | 
| 1529 |  |  |  |  |  |  | } | 
| 1530 |  |  |  |  |  |  | # only set specified tags | 
| 1531 | 6473 |  |  |  |  | 13714 | my $lcTag = lc(GetTagName($tag)); | 
| 1532 | 6473 |  |  |  |  | 11228 | my (@grp, %grp); | 
| 1533 | 6473 |  |  |  |  | 10276 | SET:    foreach $set (@setList) { | 
| 1534 | 10205 |  |  |  |  | 15416 | my $opts = $$set[3]; | 
| 1535 | 10205 | 100 |  |  |  | 19151 | next if $$opts{EXPR};   # (expressions handled in step 4) | 
| 1536 | 9238 | 100 | 100 |  |  | 26005 | next if $$opts{GROUP8} xor $isAlt; | 
| 1537 |  |  |  |  |  |  | # check first for matching tag | 
| 1538 | 8668 | 100 | 100 |  |  | 25186 | unless ($$set[1] eq $lcTag or $$set[1] eq '*') { | 
| 1539 |  |  |  |  |  |  | # handle wildcards | 
| 1540 | 6048 | 100 | 100 |  |  | 17022 | next unless $$opts{WILD} and $lcTag =~ /^$$set[1]$/; | 
| 1541 |  |  |  |  |  |  | } | 
| 1542 |  |  |  |  |  |  | # then check for matching group | 
| 1543 | 2636 | 100 |  |  |  | 3634 | if (@{$$set[0]}) { | 
|  | 2636 |  |  |  |  | 5384 |  | 
| 1544 |  |  |  |  |  |  | # get lower case group names if not done already | 
| 1545 | 1472 | 100 |  |  |  | 3131 | unless (@grp) { | 
| 1546 | 1368 |  |  |  |  | 3652 | @grp = map(lc, $srcExifTool->GetGroup($tag)); | 
| 1547 | 1368 |  |  |  |  | 9600 | $grp{$_} = 1 foreach @grp; | 
| 1548 |  |  |  |  |  |  | } | 
| 1549 | 1472 |  |  |  |  | 2456 | foreach (@{$$set[0]}) { | 
|  | 1472 |  |  |  |  | 2873 |  | 
| 1550 | 1514 |  |  |  |  | 3242 | my ($f, $g) = @$_; | 
| 1551 | 1514 | 100 |  |  |  | 2782 | if (not defined $f) { | 
|  |  | 50 |  |  |  |  |  | 
| 1552 | 1510 | 100 |  |  |  | 5459 | next SET unless $grp{$g}; | 
| 1553 |  |  |  |  |  |  | } elsif ($f == 7) { | 
| 1554 | 0 | 0 |  |  |  | 0 | next SET unless IsSameID($srcExifTool->GetTagID($tag), $g); | 
| 1555 |  |  |  |  |  |  | } else { | 
| 1556 | 4 | 50 | 33 |  |  | 22 | next SET unless defined $grp[$f] and $g eq $grp[$f]; | 
| 1557 |  |  |  |  |  |  | } | 
| 1558 |  |  |  |  |  |  | } | 
| 1559 |  |  |  |  |  |  | } | 
| 1560 | 1622 | 100 |  |  |  | 3587 | last unless $$set[2];   # all done if we hit an exclude | 
| 1561 |  |  |  |  |  |  | # add to the list of tags matching this setTag | 
| 1562 | 1448 |  |  |  |  | 1899 | push @{$setMatches{$set}}, $tag; | 
|  | 1448 |  |  |  |  | 5490 |  | 
| 1563 |  |  |  |  |  |  | } | 
| 1564 |  |  |  |  |  |  | } | 
| 1565 |  |  |  |  |  |  | # 4) loop through each setTag in original order, setting new tag values | 
| 1566 | 44 |  |  |  |  | 261 | foreach $set (reverse @setList) { | 
| 1567 |  |  |  |  |  |  | # get options for SetNewValue | 
| 1568 | 69 |  |  |  |  | 257 | my $opts = $$set[3]; | 
| 1569 |  |  |  |  |  |  | # handle expressions | 
| 1570 | 69 | 100 |  |  |  | 321 | if ($$opts{EXPR}) { | 
| 1571 | 7 |  |  |  |  | 67 | my $val = $srcExifTool->InsertTagValues(\@tags, $$set[1], 'Error'); | 
| 1572 | 7 | 50 |  |  |  | 40 | if ($$srcExifTool{VALUE}{Error}) { | 
| 1573 |  |  |  |  |  |  | # pass on any error as a warning | 
| 1574 | 0 |  |  |  |  | 0 | $tag = NextFreeTagKey(\%rtnInfo, 'Warning'); | 
| 1575 | 0 |  |  |  |  | 0 | $rtnInfo{$tag} = $$srcExifTool{VALUE}{Error}; | 
| 1576 | 0 |  |  |  |  | 0 | delete $$srcExifTool{VALUE}{Error}; | 
| 1577 | 0 | 0 |  |  |  | 0 | next unless defined $val; | 
| 1578 |  |  |  |  |  |  | } | 
| 1579 | 7 |  |  |  |  | 16 | my ($dstGrp, $dstTag) = @{$$set[2]}; | 
|  | 7 |  |  |  |  | 32 |  | 
| 1580 | 7 | 50 | 33 |  |  | 83 | $$opts{Protected} = 1 unless $dstTag =~ /[?*]/ and $dstTag ne '*'; | 
| 1581 | 7 | 50 |  |  |  | 28 | $$opts{Group} = $dstGrp if $dstGrp; | 
| 1582 | 7 |  |  |  |  | 62 | my @rtnVals = $self->SetNewValue($dstTag, $val, %$opts); | 
| 1583 | 7 | 50 |  |  |  | 53 | $rtnInfo{$dstTag} = $val if $rtnVals[0]; # tag was set successfully | 
| 1584 | 7 |  |  |  |  | 30 | next; | 
| 1585 |  |  |  |  |  |  | } | 
| 1586 | 62 |  |  |  |  | 168 | foreach $tag (@{$setMatches{$set}}) { | 
|  | 62 |  |  |  |  | 278 |  | 
| 1587 | 1448 |  |  |  |  | 2573 | my ($val, $noWarn); | 
| 1588 | 1448 | 50 | 33 |  |  | 4468 | if ($$opts{SrcType} and $$opts{SrcType} ne $srcType) { | 
| 1589 | 0 |  |  |  |  | 0 | $val = $srcExifTool->GetValue($tag, $$opts{SrcType}); | 
| 1590 |  |  |  |  |  |  | } else { | 
| 1591 | 1448 |  |  |  |  | 5733 | $val = $$info{$tag}; | 
| 1592 |  |  |  |  |  |  | } | 
| 1593 | 1448 |  |  |  |  | 2565 | my ($dstGrp, $dstTag) = @{$$set[2]}; | 
|  | 1448 |  |  |  |  | 3722 |  | 
| 1594 | 1448 | 100 |  |  |  | 3014 | if ($dstGrp) { | 
| 1595 | 1366 |  |  |  |  | 4081 | my @dstGrp = split /:/, $dstGrp; | 
| 1596 |  |  |  |  |  |  | # destination group of '*' writes to same group as source tag | 
| 1597 |  |  |  |  |  |  | # (family 1 unless otherwise specified) | 
| 1598 | 1366 |  |  |  |  | 2960 | foreach (@dstGrp) { | 
| 1599 | 1368 | 100 |  |  |  | 6693 | next unless /^(\d*)(all|\*)$/i; | 
| 1600 | 1082 | 50 |  |  |  | 5648 | $_ = $1 . $srcExifTool->GetGroup($tag, length $1 ? $1 : 1); | 
| 1601 | 1082 |  |  |  |  | 2584 | $noWarn = 1;    # don't warn on wildcard destinations | 
| 1602 |  |  |  |  |  |  | } | 
| 1603 | 1366 |  |  |  |  | 4597 | $$opts{Group} = join ':', @dstGrp; | 
| 1604 |  |  |  |  |  |  | } else { | 
| 1605 | 82 |  |  |  |  | 174 | delete $$opts{Group}; | 
| 1606 |  |  |  |  |  |  | } | 
| 1607 |  |  |  |  |  |  | # transfer maker note information if setting this tag | 
| 1608 | 1448 | 100 |  |  |  | 4867 | if ($$srcExifTool{TAG_INFO}{$tag}{MakerNotes}) { | 
| 1609 | 7 |  |  |  |  | 75 | $$self{MAKER_NOTE_FIXUP} = $$srcExifTool{MAKER_NOTE_FIXUP}; | 
| 1610 | 7 |  |  |  |  | 37 | $$self{MAKER_NOTE_BYTE_ORDER} = $$srcExifTool{MAKER_NOTE_BYTE_ORDER}; | 
| 1611 |  |  |  |  |  |  | } | 
| 1612 | 1448 | 100 |  |  |  | 3424 | if ($dstTag eq '*') { | 
| 1613 | 1415 |  |  |  |  | 2282 | $dstTag = $tag; | 
| 1614 | 1415 |  |  |  |  | 2198 | $noWarn = 1; | 
| 1615 |  |  |  |  |  |  | } | 
| 1616 | 1448 | 100 | 100 |  |  | 4870 | if ($$set[1] eq '*' or $$set[3]{WILD}) { | 
| 1617 |  |  |  |  |  |  | # don't copy from protected binary tags when using wildcards | 
| 1618 |  |  |  |  |  |  | next if $$srcExifTool{TAG_INFO}{$tag}{Protected} and | 
| 1619 | 1411 | 100 | 100 |  |  | 4428 | $$srcExifTool{TAG_INFO}{$tag}{Binary}; | 
| 1620 |  |  |  |  |  |  | # don't copy to protected tags when using wildcards | 
| 1621 | 1385 |  |  |  |  | 2300 | delete $$opts{Protected}; | 
| 1622 |  |  |  |  |  |  | # don't copy flattened tags if copying structures too when copying all | 
| 1623 | 1385 | 50 |  |  |  | 3396 | $$opts{NoFlat} = $structOpt eq '2' ? 1 : 0; | 
| 1624 |  |  |  |  |  |  | } else { | 
| 1625 |  |  |  |  |  |  | # allow protected tags to be copied if specified explicitly | 
| 1626 | 37 | 50 |  |  |  | 221 | $$opts{Protected} = 1 unless $dstTag =~ /[?*]/; | 
| 1627 | 37 |  |  |  |  | 113 | delete $$opts{NoFlat}; | 
| 1628 |  |  |  |  |  |  | } | 
| 1629 |  |  |  |  |  |  | # set value(s) for this tag | 
| 1630 | 1422 |  |  |  |  | 5824 | my ($rtn, $wrn) = $self->SetNewValue($dstTag, $val, %$opts); | 
| 1631 |  |  |  |  |  |  | # this was added in version 9.14, and allowed actions like "-subject | 
| 1632 |  |  |  |  |  |  | # write values of multiple tags into a list, but it had the side effect of | 
| 1633 |  |  |  |  |  |  | # duplicating items if there were multiple list tags with the same name | 
| 1634 |  |  |  |  |  |  | # (eg. -use mwg "-creator | 
| 1635 |  |  |  |  |  |  | # $$opts{Replace} = 0;    # accumulate values from tags matching a single argument | 
| 1636 | 1422 | 50 | 66 |  |  | 5957 | if ($wrn and not $noWarn) { | 
| 1637 |  |  |  |  |  |  | # return this warning | 
| 1638 | 0 |  |  |  |  | 0 | $rtnInfo{NextFreeTagKey(\%rtnInfo, 'Warning')} = $wrn; | 
| 1639 | 0 |  |  |  |  | 0 | $noWarn = 1; | 
| 1640 |  |  |  |  |  |  | } | 
| 1641 | 1422 | 100 |  |  |  | 6305 | $rtnInfo{$tag} = $val if $rtn;  # tag was set successfully | 
| 1642 |  |  |  |  |  |  | } | 
| 1643 |  |  |  |  |  |  | } | 
| 1644 | 44 |  |  |  |  | 3139 | return \%rtnInfo;   # return information that we set | 
| 1645 |  |  |  |  |  |  | } | 
| 1646 |  |  |  |  |  |  |  | 
| 1647 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1648 |  |  |  |  |  |  | # Get new value(s) for tag | 
| 1649 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) tag name (or tagInfo or nvHash ref, not public) | 
| 1650 |  |  |  |  |  |  | #         2) optional pointer to return new value hash reference (not part of public API) | 
| 1651 |  |  |  |  |  |  | # Returns: List of new Raw values (list may be empty if tag is being deleted) | 
| 1652 |  |  |  |  |  |  | # Notes: 1) Preferentially returns new value from Extra table if writable Extra tag exists | 
| 1653 |  |  |  |  |  |  | # 2) Must call AFTER IsOverwriting() returns 1 to get proper value for shifted times | 
| 1654 |  |  |  |  |  |  | # 3) Tag name is case sensitive and may be prefixed by family 0 or 1 group name | 
| 1655 |  |  |  |  |  |  | # 4) Value may have been modified by CHECK_PROC routine after ValueConv | 
| 1656 |  |  |  |  |  |  | sub GetNewValue($$;$) | 
| 1657 |  |  |  |  |  |  | { | 
| 1658 | 6735 |  |  | 6735 | 1 | 11948 | local $_; | 
| 1659 | 6735 |  |  |  |  | 10615 | my $self = shift; | 
| 1660 | 6735 |  |  |  |  | 11525 | my $tag = shift; | 
| 1661 | 6735 |  |  |  |  | 9684 | my $nvHash; | 
| 1662 | 6735 | 100 | 100 |  |  | 32499 | if ((ref $tag eq 'HASH' and $$tag{IsNVH}) or not defined $tag) { | 
|  |  |  | 100 |  |  |  |  | 
| 1663 | 4008 |  |  |  |  | 7053 | $nvHash = $tag; | 
| 1664 |  |  |  |  |  |  | } else { | 
| 1665 | 2727 |  |  |  |  | 4785 | my $newValueHashPt = shift; | 
| 1666 | 2727 | 100 |  |  |  | 6763 | if ($$self{NEW_VALUE}) { | 
| 1667 | 2610 |  |  |  |  | 4388 | my ($group, $tagInfo); | 
| 1668 | 2610 | 100 | 66 |  |  | 13942 | if (ref $tag) { | 
|  |  | 100 |  |  |  |  |  | 
| 1669 | 50 |  |  |  |  | 200 | $nvHash = $self->GetNewValueHash($tag); | 
| 1670 |  |  |  |  |  |  | } elsif (defined($tagInfo = $Image::ExifTool::Extra{$tag}) and | 
| 1671 |  |  |  |  |  |  | $$tagInfo{Writable}) | 
| 1672 |  |  |  |  |  |  | { | 
| 1673 | 1477 |  |  |  |  | 3777 | $nvHash = $self->GetNewValueHash($tagInfo); | 
| 1674 |  |  |  |  |  |  | } else { | 
| 1675 |  |  |  |  |  |  | # separate group from tag name | 
| 1676 | 1083 |  |  |  |  | 1998 | my @groups; | 
| 1677 | 1083 | 100 |  |  |  | 3667 | @groups = split ':', $1 if $tag =~ s/(.*)://; | 
| 1678 | 1083 |  |  |  |  | 3735 | my @tagInfoList = FindTagInfo($tag); | 
| 1679 |  |  |  |  |  |  | # decide which tag we want | 
| 1680 | 1083 |  |  |  |  | 2464 | GNV_TagInfo:    foreach $tagInfo (@tagInfoList) { | 
| 1681 | 1087 | 100 |  |  |  | 2927 | my $nvh = $self->GetNewValueHash($tagInfo) or next; | 
| 1682 |  |  |  |  |  |  | # select tag in specified group(s) if necessary | 
| 1683 | 4 |  |  |  |  | 16 | foreach (@groups) { | 
| 1684 | 2 | 50 |  |  |  | 7 | next if $_ eq $$nvh{WriteGroup}; | 
| 1685 | 2 |  |  |  |  | 8 | my @grps = $self->GetGroup($tagInfo); | 
| 1686 | 2 | 50 |  |  |  | 7 | if ($grps[0] eq $$nvh{WriteGroup}) { | 
| 1687 |  |  |  |  |  |  | # check family 1 group only if WriteGroup is not specific | 
| 1688 | 0 | 0 |  |  |  | 0 | next if $_ eq $grps[1]; | 
| 1689 |  |  |  |  |  |  | } else { | 
| 1690 |  |  |  |  |  |  | # otherwise check family 0 group | 
| 1691 | 2 | 50 |  |  |  | 8 | next if $_ eq $grps[0]; | 
| 1692 |  |  |  |  |  |  | } | 
| 1693 |  |  |  |  |  |  | # also check family 7 | 
| 1694 | 0 | 0 | 0 |  |  | 0 | next if /^ID-(.*)/i and IsSameID($$tagInfo{TagID}, $1); | 
| 1695 |  |  |  |  |  |  | # step to next entry in list | 
| 1696 | 0 | 0 |  |  |  | 0 | $nvh = $$nvh{Next} or next GNV_TagInfo; | 
| 1697 |  |  |  |  |  |  | } | 
| 1698 | 4 |  |  |  |  | 9 | $nvHash = $nvh; | 
| 1699 |  |  |  |  |  |  | # give priority to the one we are creating | 
| 1700 | 4 | 100 |  |  |  | 17 | last if defined $$nvHash{IsCreating}; | 
| 1701 |  |  |  |  |  |  | } | 
| 1702 |  |  |  |  |  |  | } | 
| 1703 |  |  |  |  |  |  | } | 
| 1704 |  |  |  |  |  |  | # return new value hash if requested | 
| 1705 | 2727 | 100 |  |  |  | 7920 | $newValueHashPt and $$newValueHashPt = $nvHash; | 
| 1706 |  |  |  |  |  |  | } | 
| 1707 | 6735 | 100 | 100 |  |  | 24222 | unless ($nvHash and $$nvHash{Value}) { | 
| 1708 | 4462 | 100 |  |  |  | 16865 | return () if wantarray;  # return empty list | 
| 1709 | 2675 |  |  |  |  | 7741 | return undef; | 
| 1710 |  |  |  |  |  |  | } | 
| 1711 | 2273 |  |  |  |  | 4423 | my $vals = $$nvHash{Value}; | 
| 1712 |  |  |  |  |  |  | # do inverse raw conversion if necessary | 
| 1713 |  |  |  |  |  |  | # - must also check after doing a Shift | 
| 1714 | 2273 | 100 | 100 |  |  | 9291 | if ($$nvHash{TagInfo}{RawConvInv} or $$nvHash{Shift}) { | 
| 1715 | 60 |  |  |  |  | 321 | my @copyVals = @$vals;  # modify a copy of the values | 
| 1716 | 60 |  |  |  |  | 142 | $vals = \@copyVals; | 
| 1717 | 60 |  |  |  |  | 175 | my $tagInfo = $$nvHash{TagInfo}; | 
| 1718 | 60 |  |  |  |  | 145 | my $conv = $$tagInfo{RawConvInv}; | 
| 1719 | 60 |  |  |  |  | 164 | my $table = $$tagInfo{Table}; | 
| 1720 | 60 |  |  |  |  | 142 | my ($val, $checkProc); | 
| 1721 | 60 | 100 | 66 |  |  | 293 | $checkProc = $$table{CHECK_PROC} if $$nvHash{Shift} and $table; | 
| 1722 | 60 |  |  |  |  | 354 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 1723 | 60 |  |  |  |  | 184 | undef $evalWarning; | 
| 1724 | 60 |  |  |  |  | 182 | foreach $val (@$vals) { | 
| 1725 |  |  |  |  |  |  | # must check value now if it was shifted | 
| 1726 | 60 | 100 |  |  |  | 187 | if ($checkProc) { | 
| 1727 | 26 |  |  |  |  | 153 | my $err = &$checkProc($self, $tagInfo, \$val); | 
| 1728 | 26 | 50 | 33 |  |  | 118 | if ($err or not defined $val) { | 
| 1729 | 0 | 0 |  |  |  | 0 | $err or $err = 'Error generating raw value'; | 
| 1730 | 0 |  |  |  |  | 0 | $self->WarnOnce("$err for $$tagInfo{Name}"); | 
| 1731 | 0 |  |  |  |  | 0 | @$vals = (); | 
| 1732 | 0 |  |  |  |  | 0 | last; | 
| 1733 |  |  |  |  |  |  | } | 
| 1734 | 26 | 50 |  |  |  | 169 | next unless $conv; | 
| 1735 |  |  |  |  |  |  | } else { | 
| 1736 | 34 | 50 |  |  |  | 119 | last unless $conv; | 
| 1737 |  |  |  |  |  |  | } | 
| 1738 |  |  |  |  |  |  | # do inverse raw conversion | 
| 1739 | 34 | 100 |  |  |  | 171 | if (ref($conv) eq 'CODE') { | 
| 1740 | 2 |  |  |  |  | 13 | $val = &$conv($val, $self); | 
| 1741 |  |  |  |  |  |  | } else { | 
| 1742 |  |  |  |  |  |  | #### eval RawConvInv ($self, $val, $tagInfo) | 
| 1743 | 32 |  |  |  |  | 3555 | $val = eval $conv; | 
| 1744 | 32 | 50 |  |  |  | 219 | $@ and $evalWarning = $@; | 
| 1745 |  |  |  |  |  |  | } | 
| 1746 | 34 | 50 |  |  |  | 240 | if ($evalWarning) { | 
| 1747 |  |  |  |  |  |  | # an empty warning ("\n") ignores tag with no error | 
| 1748 | 0 | 0 |  |  |  | 0 | if ($evalWarning ne "\n") { | 
| 1749 | 0 |  |  |  |  | 0 | my $err = CleanWarning() . " in $$tagInfo{Name} (RawConvInv)"; | 
| 1750 | 0 |  |  |  |  | 0 | $self->WarnOnce($err); | 
| 1751 |  |  |  |  |  |  | } | 
| 1752 | 0 |  |  |  |  | 0 | @$vals = (); | 
| 1753 | 0 |  |  |  |  | 0 | last; | 
| 1754 |  |  |  |  |  |  | } | 
| 1755 |  |  |  |  |  |  | } | 
| 1756 |  |  |  |  |  |  | } | 
| 1757 |  |  |  |  |  |  | # return our value(s) | 
| 1758 | 2273 | 100 |  |  |  | 9824 | return @$vals if wantarray; | 
| 1759 | 1159 |  |  |  |  | 5496 | return $$vals[0]; | 
| 1760 |  |  |  |  |  |  | } | 
| 1761 |  |  |  |  |  |  |  | 
| 1762 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1763 |  |  |  |  |  |  | # Return the total number of new values set | 
| 1764 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 1765 |  |  |  |  |  |  | # Returns: Scalar context) Number of new values that have been set (incl pseudo) | 
| 1766 |  |  |  |  |  |  | #          List context) Number of new values (incl pseudo), number of "pseudo" values | 
| 1767 |  |  |  |  |  |  | # ("pseudo" values are those which don't require rewriting the file to change) | 
| 1768 |  |  |  |  |  |  | sub CountNewValues($) | 
| 1769 |  |  |  |  |  |  | { | 
| 1770 | 236 |  |  | 236 | 1 | 663 | my $self = shift; | 
| 1771 | 236 |  |  |  |  | 810 | my $newVal = $$self{NEW_VALUE}; | 
| 1772 | 236 |  |  |  |  | 856 | my ($num, $pseudo) = (0, 0); | 
| 1773 | 236 | 100 |  |  |  | 1039 | if ($newVal) { | 
| 1774 | 217 |  |  |  |  | 1086 | $num = scalar keys %$newVal; | 
| 1775 | 217 |  |  |  |  | 469 | my $nv; | 
| 1776 | 217 |  |  |  |  | 2836 | foreach $nv (values %$newVal) { | 
| 1777 | 19139 |  |  |  |  | 44553 | my $tagInfo = $$nv{TagInfo}; | 
| 1778 |  |  |  |  |  |  | # don't count tags that don't write anything | 
| 1779 | 19139 | 100 |  |  |  | 42478 | $$tagInfo{WriteNothing} and --$num, next; | 
| 1780 |  |  |  |  |  |  | # count the number of pseudo tags included | 
| 1781 | 19124 | 100 |  |  |  | 43733 | $$tagInfo{WritePseudo} and ++$pseudo; | 
| 1782 |  |  |  |  |  |  | } | 
| 1783 |  |  |  |  |  |  | } | 
| 1784 | 236 |  |  |  |  | 931 | $num += scalar keys %{$$self{DEL_GROUP}}; | 
|  | 236 |  |  |  |  | 1193 |  | 
| 1785 | 236 | 50 |  |  |  | 1021 | return $num unless wantarray; | 
| 1786 | 236 |  |  |  |  | 970 | return ($num, $pseudo); | 
| 1787 |  |  |  |  |  |  | } | 
| 1788 |  |  |  |  |  |  |  | 
| 1789 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1790 |  |  |  |  |  |  | # Save new values for subsequent restore | 
| 1791 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 1792 |  |  |  |  |  |  | # Returns: Number of times new values have been saved | 
| 1793 |  |  |  |  |  |  | # Notes: increments SAVE_COUNT flag each time routine is called | 
| 1794 |  |  |  |  |  |  | sub SaveNewValues($) | 
| 1795 |  |  |  |  |  |  | { | 
| 1796 | 1 |  |  | 1 | 1 | 12 | my $self = shift; | 
| 1797 | 1 |  |  |  |  | 4 | my $newValues = $$self{NEW_VALUE}; | 
| 1798 | 1 |  |  |  |  | 4 | my $saveCount = ++$$self{SAVE_COUNT}; | 
| 1799 | 1 |  |  |  |  | 3 | my $key; | 
| 1800 | 1 |  |  |  |  | 58 | foreach $key (keys %$newValues) { | 
| 1801 | 113 |  |  |  |  | 164 | my $nvHash = $$newValues{$key}; | 
| 1802 | 113 |  |  |  |  | 197 | while ($nvHash) { | 
| 1803 |  |  |  |  |  |  | # set Save count if not done already | 
| 1804 | 115 | 50 |  |  |  | 238 | $$nvHash{Save} or $$nvHash{Save} = $saveCount; | 
| 1805 | 115 |  |  |  |  | 225 | $nvHash = $$nvHash{Next}; | 
| 1806 |  |  |  |  |  |  | } | 
| 1807 |  |  |  |  |  |  | } | 
| 1808 |  |  |  |  |  |  | # initialize hash for saving overwritten new values | 
| 1809 | 1 |  |  |  |  | 9 | $$self{SAVE_NEW_VALUE} = { }; | 
| 1810 |  |  |  |  |  |  | # make a copy of the delete group hash | 
| 1811 | 1 |  |  |  |  | 3 | my %delGrp = %{$$self{DEL_GROUP}}; | 
|  | 1 |  |  |  |  | 6 |  | 
| 1812 | 1 |  |  |  |  | 4 | $$self{SAVE_DEL_GROUP} = \%delGrp; | 
| 1813 | 1 |  |  |  |  | 5 | return $saveCount; | 
| 1814 |  |  |  |  |  |  | } | 
| 1815 |  |  |  |  |  |  |  | 
| 1816 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1817 |  |  |  |  |  |  | # Restore new values to last saved state | 
| 1818 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 1819 |  |  |  |  |  |  | # Notes: Restores saved new values, but currently doesn't restore them in the | 
| 1820 |  |  |  |  |  |  | # original order, so there may be some minor side-effects when restoring tags | 
| 1821 |  |  |  |  |  |  | # with overlapping groups. eg) XMP:Identifier, XMP-dc:Identifier | 
| 1822 |  |  |  |  |  |  | # Also, this doesn't do the right thing for list-type tags which accumulate | 
| 1823 |  |  |  |  |  |  | # values across a save point | 
| 1824 |  |  |  |  |  |  | sub RestoreNewValues($) | 
| 1825 |  |  |  |  |  |  | { | 
| 1826 | 1 |  |  | 1 | 1 | 12 | my $self = shift; | 
| 1827 | 1 |  |  |  |  | 3 | my $newValues = $$self{NEW_VALUE}; | 
| 1828 | 1 |  |  |  |  | 8 | my $savedValues = $$self{SAVE_NEW_VALUE}; | 
| 1829 | 1 |  |  |  |  | 3 | my $key; | 
| 1830 |  |  |  |  |  |  | # 1) remove any new values which don't have the Save flag set | 
| 1831 | 1 | 50 |  |  |  | 6 | if ($newValues) { | 
| 1832 | 1 |  |  |  |  | 209 | my @keys = keys %$newValues; | 
| 1833 | 1 |  |  |  |  | 7 | foreach $key (@keys) { | 
| 1834 | 574 |  |  |  |  | 728 | my $lastHash; | 
| 1835 | 574 |  |  |  |  | 981 | my $nvHash = $$newValues{$key}; | 
| 1836 | 574 |  |  |  |  | 933 | while ($nvHash) { | 
| 1837 | 576 | 100 |  |  |  | 1063 | if ($$nvHash{Save}) { | 
| 1838 | 25 |  |  |  |  | 37 | $lastHash = $nvHash; | 
| 1839 |  |  |  |  |  |  | } else { | 
| 1840 |  |  |  |  |  |  | # remove this entry from the list | 
| 1841 | 551 | 50 |  |  |  | 1030 | if ($lastHash) { | 
|  |  | 100 |  |  |  |  |  | 
| 1842 | 0 |  |  |  |  | 0 | $$lastHash{Next} = $$nvHash{Next}; | 
| 1843 |  |  |  |  |  |  | } elsif ($$nvHash{Next}) { | 
| 1844 | 2 |  |  |  |  | 8 | $$newValues{$key} = $$nvHash{Next}; | 
| 1845 |  |  |  |  |  |  | } else { | 
| 1846 | 549 |  |  |  |  | 794 | delete $$newValues{$key}; | 
| 1847 |  |  |  |  |  |  | } | 
| 1848 |  |  |  |  |  |  | } | 
| 1849 | 576 |  |  |  |  | 2892 | $nvHash = $$nvHash{Next}; | 
| 1850 |  |  |  |  |  |  | } | 
| 1851 |  |  |  |  |  |  | } | 
| 1852 |  |  |  |  |  |  | } | 
| 1853 |  |  |  |  |  |  | # 2) restore saved new values | 
| 1854 | 1 | 50 |  |  |  | 7 | if ($savedValues) { | 
| 1855 | 1 | 50 |  |  |  | 4 | $newValues or $newValues = $$self{NEW_VALUE} = { }; | 
| 1856 | 1 |  |  |  |  | 153 | foreach $key (keys %$savedValues) { | 
| 1857 | 90 | 100 |  |  |  | 142 | if ($$newValues{$key}) { | 
| 1858 |  |  |  |  |  |  | # add saved values to end of list | 
| 1859 | 2 |  |  |  |  | 10 | my $nvHash = LastInList($$newValues{$key}); | 
| 1860 | 2 |  |  |  |  | 21 | $$nvHash{Next} = $$savedValues{$key}; | 
| 1861 |  |  |  |  |  |  | } else { | 
| 1862 | 88 |  |  |  |  | 177 | $$newValues{$key} = $$savedValues{$key}; | 
| 1863 |  |  |  |  |  |  | } | 
| 1864 |  |  |  |  |  |  | } | 
| 1865 | 1 |  |  |  |  | 20 | $$self{SAVE_NEW_VALUE} = { };  # reset saved new values | 
| 1866 |  |  |  |  |  |  | } | 
| 1867 |  |  |  |  |  |  | # 3) restore delete groups | 
| 1868 | 1 |  |  |  |  | 6 | my %delGrp = %{$$self{SAVE_DEL_GROUP}}; | 
|  | 1 |  |  |  |  | 9 |  | 
| 1869 | 1 |  |  |  |  | 24 | $$self{DEL_GROUP} = \%delGrp; | 
| 1870 |  |  |  |  |  |  | } | 
| 1871 |  |  |  |  |  |  |  | 
| 1872 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1873 |  |  |  |  |  |  | # Set alternate file for extracting information | 
| 1874 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) family 8 group name (of the form "File#" where # is any number) | 
| 1875 |  |  |  |  |  |  | #         2) alternate file name, or undef to reset | 
| 1876 |  |  |  |  |  |  | # Returns: 1 on success, or 0 on invalid group name | 
| 1877 |  |  |  |  |  |  | sub SetAlternateFile($$$) | 
| 1878 |  |  |  |  |  |  | { | 
| 1879 | 6 |  |  | 6 | 1 | 74 | my ($self, $g8, $file) = @_; | 
| 1880 | 6 |  |  |  |  | 33 | $g8 = ucfirst lc $g8; | 
| 1881 | 6 | 50 |  |  |  | 44 | return 0 unless $g8 =~ /^File\d+$/; | 
| 1882 |  |  |  |  |  |  | # keep the same file if already initialized (possibly has metadata extracted) | 
| 1883 | 6 | 50 | 33 |  |  | 55 | if (not defined $file) { | 
|  |  | 50 |  |  |  |  |  | 
| 1884 | 0 |  |  |  |  | 0 | delete $$self{ALT_EXIFTOOL}{$g8}; | 
| 1885 |  |  |  |  |  |  | } elsif (not ($$self{ALT_EXIFTOOL}{$g8} and $$self{ALT_EXIFTOOL}{$g8}{ALT_FILE} eq $file)) { | 
| 1886 | 6 |  |  |  |  | 60 | my $altExifTool = Image::ExifTool->new; | 
| 1887 | 6 |  |  |  |  | 16 | $$altExifTool{ALT_FILE} = $file; | 
| 1888 | 6 |  |  |  |  | 24 | $$self{ALT_EXIFTOOL}{$g8} = $altExifTool; | 
| 1889 |  |  |  |  |  |  | } | 
| 1890 | 6 |  |  |  |  | 20 | return 1; | 
| 1891 |  |  |  |  |  |  | } | 
| 1892 |  |  |  |  |  |  |  | 
| 1893 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1894 |  |  |  |  |  |  | # Set filesystem time from from FileModifyDate or FileCreateDate tag | 
| 1895 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) file name or file ref | 
| 1896 |  |  |  |  |  |  | #         2) time (-M or -C) of original file (used for shift; obtained from file if not given) | 
| 1897 |  |  |  |  |  |  | #         3) tag name to write (undef for 'FileModifyDate') | 
| 1898 |  |  |  |  |  |  | #         4) flag set if argument 2 has already been converted to Unix seconds | 
| 1899 |  |  |  |  |  |  | # Returns: 1=time changed OK, 0=nothing done, -1=error setting time | 
| 1900 |  |  |  |  |  |  | #          (increments CHANGED flag and sets corresponding WRITTEN tag) | 
| 1901 |  |  |  |  |  |  | sub SetFileModifyDate($$;$$$) | 
| 1902 |  |  |  |  |  |  | { | 
| 1903 | 0 |  |  | 0 | 1 | 0 | my ($self, $file, $originalTime, $tag, $isUnixTime) = @_; | 
| 1904 | 0 |  |  |  |  | 0 | my $nvHash; | 
| 1905 | 0 | 0 |  |  |  | 0 | $tag = 'FileModifyDate' unless defined $tag; | 
| 1906 | 0 |  |  |  |  | 0 | my $val = $self->GetNewValue($tag, \$nvHash); | 
| 1907 | 0 | 0 |  |  |  | 0 | return 0 unless defined $val; | 
| 1908 | 0 |  |  |  |  | 0 | my $isOverwriting = $self->IsOverwriting($nvHash); | 
| 1909 | 0 | 0 |  |  |  | 0 | return 0 unless $isOverwriting; | 
| 1910 |  |  |  |  |  |  | # can currently only set creation date on Windows systems | 
| 1911 |  |  |  |  |  |  | # (and Mac now too, but that is handled with the MacOS tags) | 
| 1912 | 0 | 0 | 0 |  |  | 0 | return 0 if $tag eq 'FileCreateDate' and $^O ne 'MSWin32'; | 
| 1913 | 0 | 0 |  |  |  | 0 | if ($isOverwriting < 0) {  # are we shifting time? | 
| 1914 |  |  |  |  |  |  | # use original time of this file if not specified | 
| 1915 | 0 | 0 |  |  |  | 0 | unless (defined $originalTime) { | 
| 1916 | 0 |  |  |  |  | 0 | my ($aTime, $mTime, $cTime) = $self->GetFileTime($file); | 
| 1917 | 0 | 0 |  |  |  | 0 | $originalTime = ($tag eq 'FileCreateDate') ? $cTime : $mTime; | 
| 1918 | 0 | 0 |  |  |  | 0 | return 0 unless defined $originalTime; | 
| 1919 | 0 |  |  |  |  | 0 | $isUnixTime = 1; | 
| 1920 |  |  |  |  |  |  | } | 
| 1921 | 0 | 0 |  |  |  | 0 | $originalTime = int($^T - $originalTime*(24*3600) + 0.5) unless $isUnixTime; | 
| 1922 | 0 | 0 |  |  |  | 0 | return 0 unless $self->IsOverwriting($nvHash, $originalTime); | 
| 1923 | 0 |  |  |  |  | 0 | $val = $$nvHash{Value}[0]; # get shifted value | 
| 1924 |  |  |  |  |  |  | } | 
| 1925 | 0 |  |  |  |  | 0 | my ($aTime, $mTime, $cTime); | 
| 1926 | 0 | 0 |  |  |  | 0 | if ($tag eq 'FileCreateDate') { | 
| 1927 | 0 | 0 |  |  |  | 0 | eval { require Win32::API } or $self->WarnOnce("Install Win32::API to set $tag"), return -1; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1928 | 0 | 0 |  |  |  | 0 | eval { require Win32API::File } or $self->WarnOnce("Install Win32API::File to set $tag"), return -1; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1929 | 0 |  |  |  |  | 0 | $cTime = $val; | 
| 1930 |  |  |  |  |  |  | } else { | 
| 1931 | 0 |  |  |  |  | 0 | $aTime = $mTime = $val; | 
| 1932 |  |  |  |  |  |  | } | 
| 1933 | 0 | 0 |  |  |  | 0 | $self->SetFileTime($file, $aTime, $mTime, $cTime, 1) or $self->Warn("Error setting $tag"), return -1; | 
| 1934 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 1935 | 0 |  |  |  |  | 0 | $$self{WRITTEN}{$tag} = $val;   # remember that we wrote this tag | 
| 1936 | 0 |  |  |  |  | 0 | $self->VerboseValue("+ $tag", $val); | 
| 1937 | 0 |  |  |  |  | 0 | return 1; | 
| 1938 |  |  |  |  |  |  | } | 
| 1939 |  |  |  |  |  |  |  | 
| 1940 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 1941 |  |  |  |  |  |  | # Change file name and/or directory from FileName and Directory tags | 
| 1942 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) current file name (including path) | 
| 1943 |  |  |  |  |  |  | #         2) new name (or undef to build from FileName and Directory tags) | 
| 1944 |  |  |  |  |  |  | #         3) option: 'HardLink'/'SymLink' to create hard/symbolic link instead of renaming | 
| 1945 |  |  |  |  |  |  | #                    'Test' to only print new file name | 
| 1946 |  |  |  |  |  |  | #         4) 0 to indicate that a file will no longer exist (used for 'Test' only) | 
| 1947 |  |  |  |  |  |  | # Returns: 1=name changed OK, 0=nothing changed, -1=error changing name | 
| 1948 |  |  |  |  |  |  | #          (and increments CHANGED flag if filename changed) | 
| 1949 |  |  |  |  |  |  | # Notes: Will not overwrite existing file.  Creates directories as necessary. | 
| 1950 |  |  |  |  |  |  | sub SetFileName($$;$$$) | 
| 1951 |  |  |  |  |  |  | { | 
| 1952 | 1 |  |  | 1 | 1 | 4 | my ($self, $file, $newName, $opt, $usedFlag) = @_; | 
| 1953 | 1 |  |  |  |  | 3 | my ($nvHash, $doName, $doDir); | 
| 1954 |  |  |  |  |  |  |  | 
| 1955 | 1 | 50 |  |  |  | 4 | $opt or $opt = ''; | 
| 1956 |  |  |  |  |  |  | # determine the new file name | 
| 1957 | 1 | 50 |  |  |  | 5 | unless (defined $newName) { | 
| 1958 | 1 | 50 |  |  |  | 4 | if ($opt) { | 
| 1959 | 0 | 0 | 0 |  |  | 0 | if ($opt eq 'HardLink' or $opt eq 'Link') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1960 | 0 |  |  |  |  | 0 | $newName = $self->GetNewValue('HardLink'); | 
| 1961 |  |  |  |  |  |  | } elsif ($opt eq 'SymLink') { | 
| 1962 | 0 |  |  |  |  | 0 | $newName = $self->GetNewValue('SymLink'); | 
| 1963 |  |  |  |  |  |  | } elsif ($opt eq 'Test') { | 
| 1964 | 0 |  |  |  |  | 0 | $newName = $self->GetNewValue('TestName'); | 
| 1965 |  |  |  |  |  |  | } | 
| 1966 | 0 | 0 |  |  |  | 0 | return 0 unless defined $newName; | 
| 1967 |  |  |  |  |  |  | } else { | 
| 1968 | 1 |  |  |  |  | 5 | my $filename = $self->GetNewValue('FileName', \$nvHash); | 
| 1969 | 1 | 50 | 33 |  |  | 23 | $doName = 1 if defined $filename and $self->IsOverwriting($nvHash, $file); | 
| 1970 | 1 |  |  |  |  | 10 | my $dir = $self->GetNewValue('Directory', \$nvHash); | 
| 1971 | 1 | 50 | 33 |  |  | 5 | $doDir = 1 if defined $dir and $self->IsOverwriting($nvHash, $file); | 
| 1972 | 1 | 50 | 33 |  |  | 5 | return 0 unless $doName or $doDir;  # nothing to do | 
| 1973 | 1 | 50 |  |  |  | 3 | if ($doName) { | 
| 1974 | 1 |  |  |  |  | 5 | $newName = GetNewFileName($file, $filename); | 
| 1975 | 1 | 50 |  |  |  | 5 | $newName = GetNewFileName($newName, $dir) if $doDir; | 
| 1976 |  |  |  |  |  |  | } else { | 
| 1977 | 0 |  |  |  |  | 0 | $newName = GetNewFileName($file, $dir); | 
| 1978 |  |  |  |  |  |  | } | 
| 1979 |  |  |  |  |  |  | } | 
| 1980 |  |  |  |  |  |  | } | 
| 1981 |  |  |  |  |  |  | # validate new file name in Windows | 
| 1982 | 1 | 50 |  |  |  | 5 | if ($^O eq 'MSWin32') { | 
| 1983 | 0 | 0 |  |  |  | 0 | if ($newName =~ /[\0-\x1f<>"|*]/) { | 
| 1984 | 0 |  |  |  |  | 0 | $self->Warn('New file name not allowed in Windows (contains reserved characters)'); | 
| 1985 | 0 |  |  |  |  | 0 | return -1; | 
| 1986 |  |  |  |  |  |  | } | 
| 1987 | 0 | 0 | 0 |  |  | 0 | if ($newName =~ /:/ and $newName !~ /^[A-Z]:[^:]*$/i) { | 
| 1988 | 0 |  |  |  |  | 0 | $self->Warn("New file name not allowed in Windows (contains ':')"); | 
| 1989 | 0 |  |  |  |  | 0 | return -1; | 
| 1990 |  |  |  |  |  |  | } | 
| 1991 | 0 | 0 | 0 |  |  | 0 | if ($newName =~ /\?/ and $newName !~ m{^[\\/]{2}\?[\\/][^?]*$}) { | 
| 1992 | 0 |  |  |  |  | 0 | $self->Warn("New file name not allowed in Windows (contains '?')"); | 
| 1993 | 0 |  |  |  |  | 0 | return -1; | 
| 1994 |  |  |  |  |  |  | } | 
| 1995 | 0 | 0 |  |  |  | 0 | if ($newName =~ m{(^|[\\/])(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\.[^.]*)?$}i) { | 
| 1996 | 0 |  |  |  |  | 0 | $self->Warn('New file name not allowed in Windows (reserved device name)'); | 
| 1997 | 0 |  |  |  |  | 0 | return -1; | 
| 1998 |  |  |  |  |  |  | } | 
| 1999 | 0 | 0 |  |  |  | 0 | if ($newName =~ /([. ])$/) { | 
| 2000 | 0 | 0 |  |  |  | 0 | $self->Warn("New file name not recommended for Windows (ends with '${1}')", 2) and return -1; | 
| 2001 |  |  |  |  |  |  | } | 
| 2002 | 0 | 0 | 0 |  |  | 0 | if (length $newName > 259 and $newName !~ /\?/) { | 
| 2003 | 0 | 0 |  |  |  | 0 | $self->Warn('New file name not recommended for Windows (exceeds 260 chars)', 2) and return -1; | 
| 2004 |  |  |  |  |  |  | } | 
| 2005 |  |  |  |  |  |  | } else { | 
| 2006 | 1 |  |  |  |  | 4 | $newName =~ tr/\0//d;   # make sure name doesn't contain nulls | 
| 2007 |  |  |  |  |  |  | } | 
| 2008 |  |  |  |  |  |  | # protect against empty file name | 
| 2009 | 1 | 50 |  |  |  | 6 | length $newName or $self->Warn('New file name is empty'), return -1; | 
| 2010 |  |  |  |  |  |  | # don't replace existing file | 
| 2011 | 1 | 0 | 0 |  |  | 6 | if ($self->Exists($newName) and (not defined $usedFlag or $usedFlag)) { | 
|  |  |  | 33 |  |  |  |  | 
| 2012 | 0 | 0 | 0 |  |  | 0 | if ($file ne $newName or $opt =~ /Link$/) { | 
| 2013 |  |  |  |  |  |  | # allow for case-insensitive filesystem | 
| 2014 | 0 | 0 | 0 |  |  | 0 | if ($opt =~ /Link$/ or not $self->IsSameFile($file, $newName)) { | 
| 2015 | 0 |  |  |  |  | 0 | $self->Warn("File '${newName}' already exists"); | 
| 2016 | 0 |  |  |  |  | 0 | return -1; | 
| 2017 |  |  |  |  |  |  | } | 
| 2018 |  |  |  |  |  |  | } else { | 
| 2019 | 0 |  |  |  |  | 0 | $self->Warn('File name is unchanged'); | 
| 2020 | 0 |  |  |  |  | 0 | return 0; | 
| 2021 |  |  |  |  |  |  | } | 
| 2022 |  |  |  |  |  |  | } | 
| 2023 | 1 | 50 |  |  |  | 7 | if ($opt eq 'Test') { | 
| 2024 | 0 |  |  |  |  | 0 | my $out = $$self{OPTIONS}{TextOut}; | 
| 2025 | 0 |  |  |  |  | 0 | print $out "'${file}' --> '${newName}'\n"; | 
| 2026 | 0 |  |  |  |  | 0 | return 1; | 
| 2027 |  |  |  |  |  |  | } | 
| 2028 |  |  |  |  |  |  | # create directory for new file if necessary | 
| 2029 | 1 |  |  |  |  | 2 | my $result; | 
| 2030 | 1 | 50 |  |  |  | 8 | if (($result = $self->CreateDirectory($newName)) != 0) { | 
| 2031 | 0 | 0 |  |  |  | 0 | if ($result < 0) { | 
| 2032 | 0 |  |  |  |  | 0 | $self->Warn("Error creating directory for '${newName}'"); | 
| 2033 | 0 |  |  |  |  | 0 | return -1; | 
| 2034 |  |  |  |  |  |  | } | 
| 2035 | 0 |  |  |  |  | 0 | $self->VPrint(0, "Created directory for '${newName}'\n"); | 
| 2036 |  |  |  |  |  |  | } | 
| 2037 | 1 | 50 | 33 |  |  | 11 | if ($opt eq 'HardLink' or $opt eq 'Link') { | 
|  |  | 50 |  |  |  |  |  | 
| 2038 | 0 | 0 |  |  |  | 0 | unless (link $file, $newName) { | 
| 2039 | 0 |  |  |  |  | 0 | $self->Warn("Error creating hard link '${newName}'"); | 
| 2040 | 0 |  |  |  |  | 0 | return -1; | 
| 2041 |  |  |  |  |  |  | } | 
| 2042 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 2043 | 0 |  |  |  |  | 0 | $self->VerboseValue('+ HardLink', $newName); | 
| 2044 | 0 |  |  |  |  | 0 | return 1; | 
| 2045 |  |  |  |  |  |  | } elsif ($opt eq 'SymLink') { | 
| 2046 | 0 | 0 |  |  |  | 0 | $^O eq 'MSWin32' and $self->Warn('SymLink not supported in Windows'), return -1; | 
| 2047 | 0 |  |  |  |  | 0 | $newName =~ s(^\./)();  # remove leading "./" from link name if it exists | 
| 2048 |  |  |  |  |  |  | # path to linked file must be relative to the $newName directory, but $file | 
| 2049 |  |  |  |  |  |  | # is relative to the current directory, so convert it to an absolute path | 
| 2050 |  |  |  |  |  |  | # if using a relative directory and $newName isn't in the current directory | 
| 2051 | 0 | 0 | 0 |  |  | 0 | if ($file !~ m(^/) and $newName =~ m(/)) { | 
| 2052 | 0 | 0 |  |  |  | 0 | unless (eval { require Cwd }) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 2053 | 0 |  |  |  |  | 0 | $self->Warn('Install Cwd to make symlinks to other directories'); | 
| 2054 | 0 |  |  |  |  | 0 | return -1; | 
| 2055 |  |  |  |  |  |  | } | 
| 2056 | 0 |  |  |  |  | 0 | $file = eval { Cwd::abs_path($file) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2057 | 0 | 0 |  |  |  | 0 | unless (defined $file) { | 
| 2058 | 0 |  |  |  |  | 0 | $self->Warn('Error in Cwd::abs_path when creating symlink'); | 
| 2059 | 0 |  |  |  |  | 0 | return -1; | 
| 2060 |  |  |  |  |  |  | } | 
| 2061 |  |  |  |  |  |  | } | 
| 2062 | 0 | 0 |  |  |  | 0 | unless (eval { symlink $file, $newName } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 2063 | 0 |  |  |  |  | 0 | $self->Warn("Error creating symbolic link '${newName}'"); | 
| 2064 | 0 |  |  |  |  | 0 | return -1; | 
| 2065 |  |  |  |  |  |  | } | 
| 2066 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 2067 | 0 |  |  |  |  | 0 | $self->VerboseValue('+ SymLink', $newName); | 
| 2068 | 0 |  |  |  |  | 0 | return 1; | 
| 2069 |  |  |  |  |  |  | } | 
| 2070 |  |  |  |  |  |  | # attempt to rename the file | 
| 2071 | 1 | 50 |  |  |  | 8 | unless ($self->Rename($file, $newName)) { | 
| 2072 | 0 |  |  |  |  | 0 | local (*EXIFTOOL_SFN_IN, *EXIFTOOL_SFN_OUT); | 
| 2073 |  |  |  |  |  |  | # renaming didn't work, so copy the file instead | 
| 2074 | 0 | 0 |  |  |  | 0 | unless ($self->Open(\*EXIFTOOL_SFN_IN, $file)) { | 
| 2075 | 0 |  |  |  |  | 0 | $self->Error("Error opening '${file}'"); | 
| 2076 | 0 |  |  |  |  | 0 | return -1; | 
| 2077 |  |  |  |  |  |  | } | 
| 2078 | 0 | 0 |  |  |  | 0 | unless ($self->Open(\*EXIFTOOL_SFN_OUT, $newName, '>')) { | 
| 2079 | 0 |  |  |  |  | 0 | close EXIFTOOL_SFN_IN; | 
| 2080 | 0 |  |  |  |  | 0 | $self->Error("Error creating '${newName}'"); | 
| 2081 | 0 |  |  |  |  | 0 | return -1; | 
| 2082 |  |  |  |  |  |  | } | 
| 2083 | 0 |  |  |  |  | 0 | binmode EXIFTOOL_SFN_IN; | 
| 2084 | 0 |  |  |  |  | 0 | binmode EXIFTOOL_SFN_OUT; | 
| 2085 | 0 |  |  |  |  | 0 | my ($buff, $err); | 
| 2086 | 0 |  |  |  |  | 0 | while (read EXIFTOOL_SFN_IN, $buff, 65536) { | 
| 2087 | 0 | 0 |  |  |  | 0 | print EXIFTOOL_SFN_OUT $buff or $err = 1; | 
| 2088 |  |  |  |  |  |  | } | 
| 2089 | 0 | 0 |  |  |  | 0 | close EXIFTOOL_SFN_OUT or $err = 1; | 
| 2090 | 0 |  |  |  |  | 0 | close EXIFTOOL_SFN_IN; | 
| 2091 | 0 | 0 |  |  |  | 0 | if ($err) { | 
| 2092 | 0 |  |  |  |  | 0 | $self->Unlink($newName);    # erase bad output file | 
| 2093 | 0 |  |  |  |  | 0 | $self->Error("Error writing '${newName}'"); | 
| 2094 | 0 |  |  |  |  | 0 | return -1; | 
| 2095 |  |  |  |  |  |  | } | 
| 2096 |  |  |  |  |  |  | # preserve modification time | 
| 2097 | 0 |  |  |  |  | 0 | my ($aTime, $mTime, $cTime) = $self->GetFileTime($file); | 
| 2098 | 0 |  |  |  |  | 0 | $self->SetFileTime($newName, $aTime, $mTime, $cTime); | 
| 2099 |  |  |  |  |  |  | # remove the original file | 
| 2100 | 0 | 0 |  |  |  | 0 | $self->Unlink($file) or $self->Warn('Error removing old file'); | 
| 2101 |  |  |  |  |  |  | } | 
| 2102 | 1 |  |  |  |  | 5 | $$self{NewName} = $newName; # remember new file name | 
| 2103 | 1 |  |  |  |  | 6 | ++$$self{CHANGED}; | 
| 2104 | 1 |  |  |  |  | 9 | $self->VerboseValue('+ FileName', $newName); | 
| 2105 | 1 |  |  |  |  | 5 | return 1; | 
| 2106 |  |  |  |  |  |  | } | 
| 2107 |  |  |  |  |  |  |  | 
| 2108 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2109 |  |  |  |  |  |  | # Set file permissions, group/user id and various MDItem tags from new tag values | 
| 2110 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) file name or glob (must be a name for MDItem tags) | 
| 2111 |  |  |  |  |  |  | # Returns: 1=something was set OK, 0=didn't try, -1=error (and warning set) | 
| 2112 |  |  |  |  |  |  | # Notes: There may be errors even if 1 is returned | 
| 2113 |  |  |  |  |  |  | sub SetSystemTags($$) | 
| 2114 |  |  |  |  |  |  | { | 
| 2115 | 223 |  |  | 223 | 0 | 874 | my ($self, $file) = @_; | 
| 2116 | 223 |  |  |  |  | 649 | my $result = 0; | 
| 2117 |  |  |  |  |  |  |  | 
| 2118 | 223 |  |  |  |  | 926 | my $perm = $self->GetNewValue('FilePermissions'); | 
| 2119 | 223 | 50 |  |  |  | 1238 | if (defined $perm) { | 
| 2120 | 0 | 0 |  |  |  | 0 | if (eval { chmod($perm & 07777, $file) }) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 2121 | 0 |  |  |  |  | 0 | $self->VerboseValue('+ FilePermissions', $perm); | 
| 2122 | 0 |  |  |  |  | 0 | $result = 1; | 
| 2123 |  |  |  |  |  |  | } else { | 
| 2124 | 0 |  |  |  |  | 0 | $self->WarnOnce('Error setting FilePermissions'); | 
| 2125 | 0 |  |  |  |  | 0 | $result = -1; | 
| 2126 |  |  |  |  |  |  | } | 
| 2127 |  |  |  |  |  |  | } | 
| 2128 | 223 |  |  |  |  | 883 | my $uid = $self->GetNewValue('FileUserID'); | 
| 2129 | 223 |  |  |  |  | 1346 | my $gid = $self->GetNewValue('FileGroupID'); | 
| 2130 | 223 | 50 | 33 |  |  | 2344 | if (defined $uid or defined $gid) { | 
| 2131 | 0 | 0 |  |  |  | 0 | defined $uid or $uid = -1; | 
| 2132 | 0 | 0 |  |  |  | 0 | defined $gid or $gid = -1; | 
| 2133 | 0 | 0 |  |  |  | 0 | if (eval { chown($uid, $gid, $file) }) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 2134 | 0 | 0 |  |  |  | 0 | $self->VerboseValue('+ FileUserID', $uid) if $uid >= 0; | 
| 2135 | 0 | 0 |  |  |  | 0 | $self->VerboseValue('+ FileGroupID', $gid) if $gid >= 0; | 
| 2136 | 0 |  |  |  |  | 0 | $result = 1; | 
| 2137 |  |  |  |  |  |  | } else { | 
| 2138 | 0 |  |  |  |  | 0 | $self->WarnOnce('Error setting FileGroup/UserID'); | 
| 2139 | 0 | 0 |  |  |  | 0 | $result = -1 unless $result; | 
| 2140 |  |  |  |  |  |  | } | 
| 2141 |  |  |  |  |  |  | } | 
| 2142 | 223 |  |  |  |  | 670 | my $tag; | 
| 2143 | 223 |  |  |  |  | 828 | foreach $tag (@writableMacOSTags) { | 
| 2144 | 1338 |  |  |  |  | 2136 | my $nvHash; | 
| 2145 | 1338 |  |  |  |  | 3312 | my $val = $self->GetNewValue($tag, \$nvHash); | 
| 2146 | 1338 | 50 |  |  |  | 4939 | next unless $nvHash; | 
| 2147 | 0 | 0 |  |  |  | 0 | if ($^O eq 'darwin') { | 
|  |  | 0 |  |  |  |  |  | 
| 2148 | 0 | 0 |  |  |  | 0 | ref $file and $self->Warn('Setting MDItem tags requires a file name'), last; | 
| 2149 | 0 |  |  |  |  | 0 | require Image::ExifTool::MacOS; | 
| 2150 | 0 |  |  |  |  | 0 | my $res = Image::ExifTool::MacOS::SetMacOSTags($self, $file, \@writableMacOSTags); | 
| 2151 | 0 | 0 | 0 |  |  | 0 | $result = $res if $res == 1 or not $result; | 
| 2152 | 0 |  |  |  |  | 0 | last; | 
| 2153 |  |  |  |  |  |  | } elsif ($tag ne 'FileCreateDate') { | 
| 2154 | 0 |  |  |  |  | 0 | $self->WarnOnce('Can only set MDItem tags on OS X'); | 
| 2155 | 0 |  |  |  |  | 0 | last; | 
| 2156 |  |  |  |  |  |  | } | 
| 2157 |  |  |  |  |  |  | } | 
| 2158 |  |  |  |  |  |  | # delete Windows Zone.Identifier if specified | 
| 2159 | 223 |  |  |  |  | 1536 | my $zhash = $self->GetNewValueHash($Image::ExifTool::Extra{ZoneIdentifier}); | 
| 2160 | 223 | 50 |  |  |  | 1411 | if ($zhash) { | 
| 2161 | 0 |  |  |  |  | 0 | my $res = -1; | 
| 2162 | 0 | 0 |  |  |  | 0 | if ($^O ne 'MSWin32') { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 2163 | 0 |  |  |  |  | 0 | $self->Warn('ZoneIdentifer is a Windows-only tag'); | 
| 2164 |  |  |  |  |  |  | } elsif (ref $file) { | 
| 2165 | 0 |  |  |  |  | 0 | $self->Warn('Writing ZoneIdentifer requires a file name'); | 
| 2166 |  |  |  |  |  |  | } elsif (defined $self->GetNewValue('ZoneIdentifier', \$zhash)) { | 
| 2167 | 0 |  |  |  |  | 0 | $self->Warn('ZoneIndentifier may only be delted'); | 
| 2168 | 0 |  |  |  |  | 0 | } elsif (not eval { require Win32API::File }) { | 
| 2169 | 0 |  |  |  |  | 0 | $self->Warn('Install Win32API::File to write ZoneIdentifier'); | 
| 2170 |  |  |  |  |  |  | } else { | 
| 2171 | 0 |  |  |  |  | 0 | my ($wattr, $wide); | 
| 2172 | 0 |  |  |  |  | 0 | my $zfile = "${file}:Zone.Identifier"; | 
| 2173 | 0 | 0 |  |  |  | 0 | if ($self->EncodeFileName($zfile)) { | 
| 2174 | 0 |  |  |  |  | 0 | $wide = 1; | 
| 2175 | 0 |  |  |  |  | 0 | $wattr = eval { Win32API::File::GetFileAttributesW($zfile) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2176 |  |  |  |  |  |  | } else { | 
| 2177 | 0 |  |  |  |  | 0 | $wattr = eval { Win32API::File::GetFileAttributes($zfile) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2178 |  |  |  |  |  |  | } | 
| 2179 | 0 | 0 |  |  |  | 0 | if ($wattr == Win32API::File::INVALID_FILE_ATTRIBUTES()) { | 
|  |  | 0 |  |  |  |  |  | 
| 2180 | 0 |  |  |  |  | 0 | $res = 0; # file doesn't exist, nothing to do | 
| 2181 |  |  |  |  |  |  | } elsif ($wattr & Win32API::File::FILE_ATTRIBUTE_READONLY()) { | 
| 2182 | 0 |  |  |  |  | 0 | $self->Warn('Zone.Identifier stream is read-only'); | 
| 2183 |  |  |  |  |  |  | } else { | 
| 2184 | 0 | 0 |  |  |  | 0 | if ($wide) { | 
| 2185 | 0 | 0 |  |  |  | 0 | $res = 1 if eval { Win32API::File::DeleteFileW($zfile) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2186 |  |  |  |  |  |  | } else { | 
| 2187 | 0 | 0 |  |  |  | 0 | $res = 1 if eval { Win32API::File::DeleteFile($zfile) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2188 |  |  |  |  |  |  | } | 
| 2189 | 0 | 0 |  |  |  | 0 | if ($res > 0) { | 
| 2190 | 0 |  |  |  |  | 0 | $self->VPrint(0, "  Deleting Zone.Identifier stream\n"); | 
| 2191 |  |  |  |  |  |  | } else { | 
| 2192 | 0 |  |  |  |  | 0 | $self->Warn('Error deleting Zone.Identifier stream'); | 
| 2193 |  |  |  |  |  |  | } | 
| 2194 |  |  |  |  |  |  | } | 
| 2195 |  |  |  |  |  |  | } | 
| 2196 | 0 | 0 | 0 |  |  | 0 | $result = $res if $res == 1 or not $result; | 
| 2197 |  |  |  |  |  |  | } | 
| 2198 | 223 |  |  |  |  | 1166 | return $result; | 
| 2199 |  |  |  |  |  |  | } | 
| 2200 |  |  |  |  |  |  |  | 
| 2201 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2202 |  |  |  |  |  |  | # Write information back to file | 
| 2203 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, | 
| 2204 |  |  |  |  |  |  | #         1) input filename, file ref, RAF ref, or scalar ref (or '' or undef to create from scratch) | 
| 2205 |  |  |  |  |  |  | #         2) output filename, file ref, or scalar ref (or undef to overwrite) | 
| 2206 |  |  |  |  |  |  | #         3) optional output file type (required only if input file is not specified | 
| 2207 |  |  |  |  |  |  | #            and output file is a reference) | 
| 2208 |  |  |  |  |  |  | # Returns: 1=file written OK, 2=file written but no changes made, 0=file write error | 
| 2209 |  |  |  |  |  |  | sub WriteInfo($$;$$) | 
| 2210 |  |  |  |  |  |  | { | 
| 2211 | 236 |  |  | 236 | 1 | 21001 | local ($_, *EXIFTOOL_FILE2, *EXIFTOOL_OUTFILE); | 
| 2212 | 236 |  |  |  |  | 1194 | my ($self, $infile, $outfile, $outType) = @_; | 
| 2213 | 236 |  |  |  |  | 1648 | my (@fileTypeList, $fileType, $tiffType, $hdr, $seekErr, $type, $tmpfile); | 
| 2214 | 236 |  |  |  |  | 0 | my ($inRef, $outRef, $closeIn, $closeOut, $outPos, $outBuff, $eraseIn, $raf, $fileExt); | 
| 2215 | 236 |  |  |  |  | 0 | my ($hardLink, $symLink, $testName); | 
| 2216 | 236 |  |  |  |  | 885 | my $oldRaf = $$self{RAF}; | 
| 2217 | 236 |  |  |  |  | 592 | my $rtnVal = 0; | 
| 2218 |  |  |  |  |  |  |  | 
| 2219 |  |  |  |  |  |  | # initialize member variables | 
| 2220 | 236 |  |  |  |  | 1572 | $self->Init(); | 
| 2221 | 236 |  |  |  |  | 987 | $$self{IsWriting} = 1; | 
| 2222 |  |  |  |  |  |  |  | 
| 2223 |  |  |  |  |  |  | # first, save original file modify date if necessary | 
| 2224 |  |  |  |  |  |  | # (do this now in case we are modifying file in place and shifting date) | 
| 2225 | 236 |  |  |  |  | 814 | my ($nvHash, $nvHash2, $originalTime, $createTime); | 
| 2226 | 236 |  |  |  |  | 1492 | my $setModDate = defined $self->GetNewValue('FileModifyDate', \$nvHash); | 
| 2227 | 236 |  |  |  |  | 1054 | my $setCreateDate = defined $self->GetNewValue('FileCreateDate', \$nvHash2); | 
| 2228 | 236 |  |  |  |  | 1093 | my ($aTime, $mTime, $cTime); | 
| 2229 | 236 | 0 | 33 |  |  | 1522 | if ($setModDate and $self->IsOverwriting($nvHash) < 0 and | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 2230 |  |  |  |  |  |  | defined $infile and ref $infile ne 'SCALAR') | 
| 2231 |  |  |  |  |  |  | { | 
| 2232 | 0 |  |  |  |  | 0 | ($aTime, $mTime, $cTime) = $self->GetFileTime($infile); | 
| 2233 | 0 |  |  |  |  | 0 | $originalTime = $mTime; | 
| 2234 |  |  |  |  |  |  | } | 
| 2235 | 236 | 0 | 33 |  |  | 1166 | if ($setCreateDate and $self->IsOverwriting($nvHash2) < 0 and | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 2236 |  |  |  |  |  |  | defined $infile and ref $infile ne 'SCALAR') | 
| 2237 |  |  |  |  |  |  | { | 
| 2238 | 0 | 0 |  |  |  | 0 | ($aTime, $mTime, $cTime) = $self->GetFileTime($infile) unless defined $cTime; | 
| 2239 | 0 |  |  |  |  | 0 | $createTime = $cTime; | 
| 2240 |  |  |  |  |  |  | } | 
| 2241 |  |  |  |  |  |  | # | 
| 2242 |  |  |  |  |  |  | # do quick in-place change of file dir/name or date if that is all we are doing | 
| 2243 |  |  |  |  |  |  | # | 
| 2244 | 236 |  |  |  |  | 1312 | my ($numNew, $numPseudo) = $self->CountNewValues(); | 
| 2245 | 236 | 100 | 66 |  |  | 1315 | if (not defined $outfile and defined $infile) { | 
| 2246 | 4 |  |  |  |  | 18 | $hardLink = $self->GetNewValue('HardLink'); | 
| 2247 | 4 |  |  |  |  | 23 | $symLink = $self->GetNewValue('SymLink'); | 
| 2248 | 4 |  |  |  |  | 35 | $testName = $self->GetNewValue('TestName'); | 
| 2249 | 4 | 50 | 33 |  |  | 41 | undef $hardLink if defined $hardLink and not length $hardLink; | 
| 2250 | 4 | 50 | 33 |  |  | 20 | undef $symLink if defined $symLink and not length $symLink; | 
| 2251 | 4 | 50 | 33 |  |  | 24 | undef $testName if defined $testName and not length $testName; | 
| 2252 | 4 |  |  |  |  | 15 | my $newFileName =  $self->GetNewValue('FileName', \$nvHash); | 
| 2253 | 4 |  |  |  |  | 22 | my $newDir = $self->GetNewValue('Directory'); | 
| 2254 | 4 | 50 | 33 |  |  | 38 | if (defined $newDir and length $newDir) { | 
| 2255 | 0 | 0 |  |  |  | 0 | $newDir .= '/' unless $newDir =~ m{/$}; | 
| 2256 |  |  |  |  |  |  | } else { | 
| 2257 | 4 |  |  |  |  | 8 | undef $newDir; | 
| 2258 |  |  |  |  |  |  | } | 
| 2259 | 4 | 100 | 33 |  |  | 33 | if ($numNew == $numPseudo) { | 
|  |  | 50 |  |  |  |  |  | 
| 2260 | 1 |  |  |  |  | 3 | $rtnVal = 2; | 
| 2261 | 1 | 50 | 33 |  |  | 10 | if ((defined $newFileName or defined $newDir) and not ref $infile) { | 
|  |  |  | 33 |  |  |  |  | 
| 2262 | 1 |  |  |  |  | 8 | my $result = $self->SetFileName($infile); | 
| 2263 | 1 | 50 |  |  |  | 5 | if ($result > 0) { | 
|  |  | 0 |  |  |  |  |  | 
| 2264 | 1 |  |  |  |  | 3 | $infile = $$self{NewName};  # file name changed | 
| 2265 | 1 |  |  |  |  | 4 | $rtnVal = 1; | 
| 2266 |  |  |  |  |  |  | } elsif ($result < 0) { | 
| 2267 | 0 |  |  |  |  | 0 | return 0;   # don't try to do anything else | 
| 2268 |  |  |  |  |  |  | } | 
| 2269 |  |  |  |  |  |  | } | 
| 2270 | 1 | 50 | 33 |  |  | 7 | if (not ref $infile or UNIVERSAL::isa($infile,'GLOB')) { | 
| 2271 | 1 | 50 | 0 |  |  | 4 | $self->SetFileModifyDate($infile) > 0 and $rtnVal = 1 if $setModDate; | 
| 2272 | 1 | 50 | 0 |  |  | 4 | $self->SetFileModifyDate($infile, undef, 'FileCreateDate') > 0 and $rtnVal = 1 if $setCreateDate; | 
| 2273 | 1 | 50 |  |  |  | 6 | $self->SetSystemTags($infile) > 0 and $rtnVal = 1; | 
| 2274 |  |  |  |  |  |  | } | 
| 2275 | 1 | 50 | 33 |  |  | 16 | if (defined $hardLink or defined $symLink or defined $testName) { | 
|  |  |  | 33 |  |  |  |  | 
| 2276 | 0 | 0 | 0 |  |  | 0 | $hardLink and $self->SetFileName($infile, $hardLink, 'HardLink') and $rtnVal = 1; | 
| 2277 | 0 | 0 | 0 |  |  | 0 | $symLink and $self->SetFileName($infile, $symLink, 'SymLink') and $rtnVal = 1; | 
| 2278 | 0 | 0 | 0 |  |  | 0 | $testName and $self->SetFileName($infile, $testName, 'Test') and $rtnVal = 1; | 
| 2279 |  |  |  |  |  |  | } | 
| 2280 | 1 |  |  |  |  | 7 | return $rtnVal; | 
| 2281 |  |  |  |  |  |  | } elsif (defined $newFileName and length $newFileName) { | 
| 2282 |  |  |  |  |  |  | # can't simply rename file, so just set the output name if new FileName | 
| 2283 |  |  |  |  |  |  | # --> in this case, must erase original copy | 
| 2284 | 0 | 0 |  |  |  | 0 | if (ref $infile) { | 
|  |  | 0 |  |  |  |  |  | 
| 2285 | 0 |  |  |  |  | 0 | $outfile = $newFileName; | 
| 2286 |  |  |  |  |  |  | # can't delete original | 
| 2287 |  |  |  |  |  |  | } elsif ($self->IsOverwriting($nvHash, $infile)) { | 
| 2288 | 0 |  |  |  |  | 0 | $outfile = GetNewFileName($infile, $newFileName); | 
| 2289 | 0 |  |  |  |  | 0 | $eraseIn = 1; # delete original | 
| 2290 |  |  |  |  |  |  | } | 
| 2291 |  |  |  |  |  |  | } | 
| 2292 |  |  |  |  |  |  | # set new directory if specified | 
| 2293 | 3 | 50 |  |  |  | 16 | if (defined $newDir) { | 
| 2294 | 0 | 0 | 0 |  |  | 0 | $outfile = $infile unless defined $outfile or ref $infile; | 
| 2295 | 0 | 0 |  |  |  | 0 | if (defined $outfile) { | 
| 2296 | 0 |  |  |  |  | 0 | $outfile = GetNewFileName($outfile, $newDir); | 
| 2297 | 0 | 0 |  |  |  | 0 | $eraseIn = 1 unless ref $infile; | 
| 2298 |  |  |  |  |  |  | } | 
| 2299 |  |  |  |  |  |  | } | 
| 2300 |  |  |  |  |  |  | } | 
| 2301 |  |  |  |  |  |  | # | 
| 2302 |  |  |  |  |  |  | # set up input file | 
| 2303 |  |  |  |  |  |  | # | 
| 2304 | 235 | 100 | 66 |  |  | 2212 | if (ref $infile) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 2305 | 5 |  |  |  |  | 12 | $inRef = $infile; | 
| 2306 | 5 | 100 | 33 |  |  | 66 | if (UNIVERSAL::isa($inRef,'GLOB')) { | 
|  |  | 50 | 33 |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 2307 | 1 |  |  |  |  | 14 | seek($inRef, 0, 0); # make sure we are at the start of the file | 
| 2308 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($inRef,'File::RandomAccess')) { | 
| 2309 | 0 |  |  |  |  | 0 | $inRef->Seek(0); | 
| 2310 | 0 |  |  |  |  | 0 | $raf = $inRef; | 
| 2311 |  |  |  |  |  |  | } elsif ($] >= 5.006 and (eval { require Encode; Encode::is_utf8($$inRef) } or $@)) { | 
| 2312 |  |  |  |  |  |  | # convert image data from UTF-8 to character stream if necessary | 
| 2313 | 0 | 0 |  |  |  | 0 | my $buff = $@ ? pack('C*',unpack($] < 5.010000 ? 'U0C*' : 'C0C*',$$inRef)) : Encode::encode('utf8',$$inRef); | 
|  |  | 0 |  |  |  |  |  | 
| 2314 | 0 | 0 |  |  |  | 0 | if (defined $outfile) { | 
| 2315 | 0 |  |  |  |  | 0 | $inRef = \$buff; | 
| 2316 |  |  |  |  |  |  | } else { | 
| 2317 | 0 |  |  |  |  | 0 | $$inRef = $buff; | 
| 2318 |  |  |  |  |  |  | } | 
| 2319 |  |  |  |  |  |  | } | 
| 2320 |  |  |  |  |  |  | } elsif (defined $infile and $infile ne '') { | 
| 2321 |  |  |  |  |  |  | # write to a temporary file if no output file given | 
| 2322 | 207 | 100 |  |  |  | 811 | $outfile = $tmpfile = "${infile}_exiftool_tmp" unless defined $outfile; | 
| 2323 | 207 | 50 |  |  |  | 1529 | if ($self->Open(\*EXIFTOOL_FILE2, $infile)) { | 
| 2324 | 207 |  |  |  |  | 1767 | $fileExt = GetFileExtension($infile); | 
| 2325 | 207 |  |  |  |  | 1246 | $fileType = GetFileType($infile); | 
| 2326 | 207 |  |  |  |  | 901 | @fileTypeList = GetFileType($infile); | 
| 2327 | 207 |  |  |  |  | 968 | $tiffType = $$self{FILE_EXT} = GetFileExtension($infile); | 
| 2328 | 207 |  |  |  |  | 2370 | $self->VPrint(0, "Rewriting $infile...\n"); | 
| 2329 | 207 |  |  |  |  | 670 | $inRef = \*EXIFTOOL_FILE2; | 
| 2330 | 207 |  |  |  |  | 763 | $closeIn = 1;   # we must close the file since we opened it | 
| 2331 |  |  |  |  |  |  | } else { | 
| 2332 | 0 |  |  |  |  | 0 | $self->Error('Error opening file'); | 
| 2333 | 0 |  |  |  |  | 0 | return 0; | 
| 2334 |  |  |  |  |  |  | } | 
| 2335 |  |  |  |  |  |  | } elsif (not defined $outfile) { | 
| 2336 | 0 |  |  |  |  | 0 | $self->Error("WriteInfo(): Must specify infile or outfile\n"); | 
| 2337 | 0 |  |  |  |  | 0 | return 0; | 
| 2338 |  |  |  |  |  |  | } else { | 
| 2339 |  |  |  |  |  |  | # create file from scratch | 
| 2340 | 23 | 100 | 66 |  |  | 282 | $outType = GetFileExtension($outfile) unless $outType or ref $outfile; | 
| 2341 | 23 | 50 |  |  |  | 193 | if (CanCreate($outType)) { | 
|  |  | 0 |  |  |  |  |  | 
| 2342 | 23 | 50 |  |  |  | 182 | if ($$self{OPTIONS}{WriteMode} =~ /g/i) { | 
| 2343 | 23 |  |  |  |  | 77 | $fileType = $tiffType = $outType;   # use output file type if no input file | 
| 2344 | 23 |  |  |  |  | 86 | $infile = "$fileType file";         # make bogus file name | 
| 2345 | 23 |  |  |  |  | 176 | $self->VPrint(0, "Creating $infile...\n"); | 
| 2346 | 23 |  |  |  |  | 90 | $inRef = \ '';      # set $inRef to reference to empty data | 
| 2347 |  |  |  |  |  |  | } else { | 
| 2348 | 0 |  |  |  |  | 0 | $self->Error("Not creating new $outType file (disallowed by WriteMode)"); | 
| 2349 | 0 |  |  |  |  | 0 | return 0; | 
| 2350 |  |  |  |  |  |  | } | 
| 2351 |  |  |  |  |  |  | } elsif ($outType) { | 
| 2352 | 0 |  |  |  |  | 0 | $self->Error("Can't create $outType files"); | 
| 2353 | 0 |  |  |  |  | 0 | return 0; | 
| 2354 |  |  |  |  |  |  | } else { | 
| 2355 | 0 |  |  |  |  | 0 | $self->Error("Can't create file (unknown type)"); | 
| 2356 | 0 |  |  |  |  | 0 | return 0; | 
| 2357 |  |  |  |  |  |  | } | 
| 2358 |  |  |  |  |  |  | } | 
| 2359 | 235 | 100 |  |  |  | 1137 | unless (@fileTypeList) { | 
| 2360 | 29 | 100 |  |  |  | 107 | if ($fileType) { | 
| 2361 | 23 |  |  |  |  | 86 | @fileTypeList = ( $fileType ); | 
| 2362 |  |  |  |  |  |  | } else { | 
| 2363 | 6 |  |  |  |  | 155 | @fileTypeList = @fileTypes; | 
| 2364 | 6 |  |  |  |  | 28 | $tiffType = 'TIFF'; | 
| 2365 |  |  |  |  |  |  | } | 
| 2366 |  |  |  |  |  |  | } | 
| 2367 |  |  |  |  |  |  | # | 
| 2368 |  |  |  |  |  |  | # set up output file | 
| 2369 |  |  |  |  |  |  | # | 
| 2370 | 235 | 100 |  |  |  | 2156 | if (ref $outfile) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 2371 | 13 |  |  |  |  | 31 | $outRef = $outfile; | 
| 2372 | 13 | 50 |  |  |  | 95 | if (UNIVERSAL::isa($outRef,'GLOB')) { | 
| 2373 | 0 |  |  |  |  | 0 | binmode($outRef); | 
| 2374 | 0 |  |  |  |  | 0 | $outPos = tell($outRef); | 
| 2375 |  |  |  |  |  |  | } else { | 
| 2376 |  |  |  |  |  |  | # initialize our output buffer if necessary | 
| 2377 | 13 | 50 |  |  |  | 82 | defined $$outRef or $$outRef = ''; | 
| 2378 | 13 |  |  |  |  | 42 | $outPos = length($$outRef); | 
| 2379 |  |  |  |  |  |  | } | 
| 2380 |  |  |  |  |  |  | } elsif (not defined $outfile) { | 
| 2381 |  |  |  |  |  |  | # editing in place, so write to memory first | 
| 2382 |  |  |  |  |  |  | # (only when infile is a file ref or scalar ref) | 
| 2383 | 1 | 50 |  |  |  | 7 | if ($raf) { | 
| 2384 | 0 |  |  |  |  | 0 | $self->Error("Can't edit File::RandomAccess object in place"); | 
| 2385 | 0 |  |  |  |  | 0 | return 0; | 
| 2386 |  |  |  |  |  |  | } | 
| 2387 | 1 |  |  |  |  | 5 | $outBuff = ''; | 
| 2388 | 1 |  |  |  |  | 2 | $outRef = \$outBuff; | 
| 2389 | 1 |  |  |  |  | 4 | $outPos = 0; | 
| 2390 |  |  |  |  |  |  | } elsif ($self->Exists($outfile)) { | 
| 2391 | 0 |  |  |  |  | 0 | $self->Error("File already exists: $outfile"); | 
| 2392 |  |  |  |  |  |  | } elsif ($self->Open(\*EXIFTOOL_OUTFILE, $outfile, '>')) { | 
| 2393 | 221 |  |  |  |  | 1360 | $outRef = \*EXIFTOOL_OUTFILE; | 
| 2394 | 221 |  |  |  |  | 731 | $closeOut = 1;  # we must close $outRef | 
| 2395 | 221 |  |  |  |  | 1050 | binmode($outRef); | 
| 2396 | 221 |  |  |  |  | 660 | $outPos = 0; | 
| 2397 |  |  |  |  |  |  | } else { | 
| 2398 | 0 | 0 |  |  |  | 0 | my $tmp = $tmpfile ? ' temporary' : ''; | 
| 2399 | 0 |  |  |  |  | 0 | $self->Error("Error creating$tmp file: $outfile"); | 
| 2400 |  |  |  |  |  |  | } | 
| 2401 |  |  |  |  |  |  | # | 
| 2402 |  |  |  |  |  |  | # write the file | 
| 2403 |  |  |  |  |  |  | # | 
| 2404 | 235 |  |  |  |  | 1434 | until ($$self{VALUE}{Error}) { | 
| 2405 |  |  |  |  |  |  | # create random access file object (disable seek test in case of straight copy) | 
| 2406 | 235 | 50 |  |  |  | 3027 | $raf or $raf = new File::RandomAccess($inRef, 1); | 
| 2407 | 235 |  |  |  |  | 1456 | $raf->BinMode(); | 
| 2408 | 235 | 100 | 33 |  |  | 3195 | if ($numNew == $numPseudo) { | 
|  |  | 50 | 66 |  |  |  |  | 
| 2409 | 1 |  |  |  |  | 3 | $rtnVal = 1; | 
| 2410 |  |  |  |  |  |  | # just do a straight copy of the file (no "real" tags are being changed) | 
| 2411 | 1 |  |  |  |  | 3 | my $buff; | 
| 2412 | 1 |  |  |  |  | 6 | while ($raf->Read($buff, 65536)) { | 
| 2413 | 1 | 50 |  |  |  | 7 | Write($outRef, $buff) or $rtnVal = -1, last; | 
| 2414 |  |  |  |  |  |  | } | 
| 2415 | 1 |  |  |  |  | 4 | last; | 
| 2416 |  |  |  |  |  |  | } elsif (not ref $infile and ($infile eq '-' or $infile =~ /\|$/)) { | 
| 2417 |  |  |  |  |  |  | # patch for Windows command shell pipe | 
| 2418 | 0 |  |  |  |  | 0 | $$raf{TESTED} = -1; # force buffering | 
| 2419 |  |  |  |  |  |  | } else { | 
| 2420 | 234 |  |  |  |  | 1237 | $raf->SeekTest(); | 
| 2421 |  |  |  |  |  |  | } | 
| 2422 |  |  |  |  |  |  | # $raf->Debug() and warn "  RAF debugging enabled!\n"; | 
| 2423 | 234 |  |  |  |  | 1355 | my $inPos = $raf->Tell(); | 
| 2424 | 234 |  |  |  |  | 1501 | $$self{RAF} = $raf; | 
| 2425 | 234 |  |  |  |  | 1326 | my %dirInfo = ( | 
| 2426 |  |  |  |  |  |  | RAF => $raf, | 
| 2427 |  |  |  |  |  |  | OutFile => $outRef, | 
| 2428 |  |  |  |  |  |  | ); | 
| 2429 | 234 | 100 |  |  |  | 1263 | $raf->Read($hdr, 1024) or $hdr = ''; | 
| 2430 | 234 | 50 |  |  |  | 1449 | $raf->Seek($inPos, 0) or $seekErr = 1; | 
| 2431 | 234 |  |  |  |  | 775 | my $wrongType; | 
| 2432 | 234 |  |  |  |  | 1132 | until ($seekErr) { | 
| 2433 | 269 |  |  |  |  | 807 | $type = shift @fileTypeList; | 
| 2434 |  |  |  |  |  |  | # do quick test to see if this is the right file type | 
| 2435 | 269 | 100 | 66 |  |  | 6978 | if ($magicNumber{$type} and length($hdr) and $hdr !~ /^$magicNumber{$type}/s) { | 
|  |  |  | 100 |  |  |  |  | 
| 2436 | 35 | 50 |  |  |  | 124 | next if @fileTypeList; | 
| 2437 | 0 |  |  |  |  | 0 | $wrongType = 1; | 
| 2438 | 0 |  |  |  |  | 0 | last; | 
| 2439 |  |  |  |  |  |  | } | 
| 2440 |  |  |  |  |  |  | # save file type in member variable | 
| 2441 | 234 |  |  |  |  | 1912 | $dirInfo{Parent} = $$self{FILE_TYPE} = $$self{PATH}[0] = $type; | 
| 2442 |  |  |  |  |  |  | # determine which directories we must write for this file type | 
| 2443 | 234 |  |  |  |  | 1699 | $self->InitWriteDirs($type); | 
| 2444 | 234 | 100 | 100 |  |  | 2174 | if ($type eq 'JPEG' or $type eq 'EXV') { | 
|  |  | 100 | 33 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 2445 | 107 |  |  |  |  | 740 | $rtnVal = $self->WriteJPEG(\%dirInfo); | 
| 2446 |  |  |  |  |  |  | } elsif ($type eq 'TIFF') { | 
| 2447 |  |  |  |  |  |  | # disallow writing of some TIFF-based RAW images: | 
| 2448 | 13 | 50 |  |  |  | 41 | if (grep /^$tiffType$/, @{$noWriteFile{TIFF}}) { | 
|  | 13 |  |  |  |  | 270 |  | 
| 2449 | 0 |  |  |  |  | 0 | $fileType = $tiffType; | 
| 2450 | 0 |  |  |  |  | 0 | undef $rtnVal; | 
| 2451 |  |  |  |  |  |  | } else { | 
| 2452 | 13 | 50 |  |  |  | 133 | if ($tiffType eq 'FFF') { | 
| 2453 |  |  |  |  |  |  | # (see https://exiftool.org/forum/index.php?topic=10848.0) | 
| 2454 | 0 |  |  |  |  | 0 | $self->Error('Phocus may not properly update previews of edited FFF images', 1); | 
| 2455 |  |  |  |  |  |  | } | 
| 2456 | 13 |  |  |  |  | 47 | $dirInfo{Parent} = $tiffType; | 
| 2457 | 13 |  |  |  |  | 97 | $rtnVal = $self->ProcessTIFF(\%dirInfo); | 
| 2458 |  |  |  |  |  |  | } | 
| 2459 | 0 |  |  |  |  | 0 | } elsif (exists $writableType{$type}) { | 
| 2460 | 112 |  |  |  |  | 307 | my ($module, $func); | 
| 2461 | 112 | 100 |  |  |  | 569 | if (ref $writableType{$type} eq 'ARRAY') { | 
| 2462 | 85 |  | 66 |  |  | 508 | $module = $writableType{$type}[0] || $type; | 
| 2463 | 85 |  |  |  |  | 289 | $func = $writableType{$type}[1]; | 
| 2464 |  |  |  |  |  |  | } else { | 
| 2465 | 27 |  | 66 |  |  | 126 | $module = $writableType{$type} || $type; | 
| 2466 |  |  |  |  |  |  | } | 
| 2467 | 112 |  |  |  |  | 1574 | require "Image/ExifTool/$module.pm"; | 
| 2468 | 112 |  | 66 |  |  | 817 | $func = "Image::ExifTool::${module}::" . ($func || "Process$type"); | 
| 2469 | 59 |  |  | 59 |  | 665 | no strict 'refs'; | 
|  | 59 |  |  |  |  | 184 |  | 
|  | 59 |  |  |  |  | 2962 |  | 
| 2470 | 112 |  |  |  |  | 1081 | $rtnVal = &$func($self, \%dirInfo); | 
| 2471 | 59 |  |  | 59 |  | 513 | use strict 'refs'; | 
|  | 59 |  |  |  |  | 180 |  | 
|  | 59 |  |  |  |  | 379750 |  | 
| 2472 |  |  |  |  |  |  | } elsif ($type eq 'ORF' or $type eq 'RAW') { | 
| 2473 | 0 |  |  |  |  | 0 | $rtnVal = $self->ProcessTIFF(\%dirInfo); | 
| 2474 |  |  |  |  |  |  | } elsif ($type eq 'EXIF') { | 
| 2475 |  |  |  |  |  |  | # go through WriteDirectory so block writes, etc are handled | 
| 2476 | 2 |  |  |  |  | 27 | my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main'); | 
| 2477 | 2 |  |  |  |  | 17 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF); | 
| 2478 | 2 | 50 |  |  |  | 9 | if (defined $buff) { | 
| 2479 | 2 | 50 |  |  |  | 12 | $rtnVal = Write($outRef, $buff) ? 1 : -1; | 
| 2480 |  |  |  |  |  |  | } else { | 
| 2481 | 0 |  |  |  |  | 0 | $rtnVal = 0; | 
| 2482 |  |  |  |  |  |  | } | 
| 2483 |  |  |  |  |  |  | } else { | 
| 2484 | 0 |  |  |  |  | 0 | undef $rtnVal;  # flag that we don't write this type of file | 
| 2485 |  |  |  |  |  |  | } | 
| 2486 |  |  |  |  |  |  | # all done unless we got the wrong type | 
| 2487 | 234 | 50 |  |  |  | 1247 | last if $rtnVal; | 
| 2488 | 0 | 0 |  |  |  | 0 | last unless @fileTypeList; | 
| 2489 |  |  |  |  |  |  | # seek back to original position in files for next try | 
| 2490 | 0 | 0 |  |  |  | 0 | $raf->Seek($inPos, 0) or $seekErr = 1, last; | 
| 2491 | 0 | 0 |  |  |  | 0 | if (UNIVERSAL::isa($outRef,'GLOB')) { | 
| 2492 | 0 |  |  |  |  | 0 | seek($outRef, 0, $outPos); | 
| 2493 |  |  |  |  |  |  | } else { | 
| 2494 | 0 |  |  |  |  | 0 | $$outRef = substr($$outRef, 0, $outPos); | 
| 2495 |  |  |  |  |  |  | } | 
| 2496 |  |  |  |  |  |  | } | 
| 2497 |  |  |  |  |  |  | # print file format errors | 
| 2498 | 234 | 50 |  |  |  | 994 | unless ($rtnVal) { | 
| 2499 | 0 |  |  |  |  | 0 | my $err; | 
| 2500 | 0 | 0 | 0 |  |  | 0 | if ($seekErr) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 2501 | 0 |  |  |  |  | 0 | $err = 'Error seeking in file'; | 
| 2502 |  |  |  |  |  |  | } elsif ($fileType and defined $rtnVal) { | 
| 2503 | 0 | 0 |  |  |  | 0 | if ($$self{VALUE}{Error}) { | 
|  |  | 0 |  |  |  |  |  | 
| 2504 |  |  |  |  |  |  | # existing error message will do | 
| 2505 |  |  |  |  |  |  | } elsif ($fileType eq 'RAW') { | 
| 2506 | 0 |  |  |  |  | 0 | $err = 'Writing this type of RAW file is not supported'; | 
| 2507 |  |  |  |  |  |  | } else { | 
| 2508 | 0 | 0 |  |  |  | 0 | if ($wrongType) { | 
| 2509 | 0 |  | 0 |  |  | 0 | my $type = $fileExt || ($fileType eq 'TIFF' ? $tiffType : $fileType); | 
| 2510 | 0 |  |  |  |  | 0 | $err = "Not a valid $type"; | 
| 2511 |  |  |  |  |  |  | # do a quick check to see what this file looks like | 
| 2512 | 0 |  |  |  |  | 0 | foreach $type (@fileTypes) { | 
| 2513 | 0 | 0 |  |  |  | 0 | next unless $magicNumber{$type}; | 
| 2514 | 0 | 0 |  |  |  | 0 | next unless $hdr =~ /^$magicNumber{$type}/s; | 
| 2515 | 0 |  |  |  |  | 0 | $err .= " (looks more like a $type)"; | 
| 2516 | 0 |  |  |  |  | 0 | last; | 
| 2517 |  |  |  |  |  |  | } | 
| 2518 |  |  |  |  |  |  | } else { | 
| 2519 | 0 |  |  |  |  | 0 | $err = 'Format error in file'; | 
| 2520 |  |  |  |  |  |  | } | 
| 2521 |  |  |  |  |  |  | } | 
| 2522 |  |  |  |  |  |  | } elsif ($fileType) { | 
| 2523 |  |  |  |  |  |  | # get specific type of file from extension | 
| 2524 | 0 | 0 | 0 |  |  | 0 | $fileType = GetFileExtension($infile) if $infile and GetFileType($infile); | 
| 2525 | 0 |  |  |  |  | 0 | $err = "Writing of $fileType files is not yet supported"; | 
| 2526 |  |  |  |  |  |  | } else { | 
| 2527 | 0 |  |  |  |  | 0 | $err = 'Writing of this type of file is not supported'; | 
| 2528 |  |  |  |  |  |  | } | 
| 2529 | 0 | 0 |  |  |  | 0 | $self->Error($err) if $err; | 
| 2530 | 0 |  |  |  |  | 0 | $rtnVal = 0;    # (in case it was undef) | 
| 2531 |  |  |  |  |  |  | } | 
| 2532 |  |  |  |  |  |  | # $raf->Close();  # only used to force debug output | 
| 2533 | 234 |  |  |  |  | 1043 | last;   # (didn't really want to loop) | 
| 2534 |  |  |  |  |  |  | } | 
| 2535 |  |  |  |  |  |  | # don't return success code if any error occurred | 
| 2536 | 235 | 50 |  |  |  | 1153 | if ($rtnVal > 0) { | 
| 2537 | 235 | 50 | 66 |  |  | 1279 | if ($outType and $type and $outType ne $type) { | 
|  |  |  | 66 |  |  |  |  | 
| 2538 | 0 |  |  |  |  | 0 | my @types = GetFileType($outType); | 
| 2539 | 0 | 0 |  |  |  | 0 | unless (grep /^$type$/, @types) { | 
| 2540 | 0 |  |  |  |  | 0 | $self->Error("Can't create $outType file from $type"); | 
| 2541 | 0 |  |  |  |  | 0 | $rtnVal = 0; | 
| 2542 |  |  |  |  |  |  | } | 
| 2543 |  |  |  |  |  |  | } | 
| 2544 | 235 | 50 | 33 |  |  | 1647 | if ($rtnVal > 0 and not Tell($outRef) and not $$self{VALUE}{Error}) { | 
|  |  |  | 33 |  |  |  |  | 
| 2545 |  |  |  |  |  |  | # don't write a file with zero length | 
| 2546 | 0 | 0 | 0 |  |  | 0 | if (defined $hdr and length $hdr) { | 
| 2547 | 0 | 0 |  |  |  | 0 | $type = '' unless defined $type; | 
| 2548 | 0 |  |  |  |  | 0 | $self->Error("Can't delete all meta information from $type file"); | 
| 2549 |  |  |  |  |  |  | } else { | 
| 2550 | 0 |  |  |  |  | 0 | $self->Error('Nothing to write'); | 
| 2551 |  |  |  |  |  |  | } | 
| 2552 |  |  |  |  |  |  | } | 
| 2553 | 235 | 50 |  |  |  | 1396 | $rtnVal = 0 if $$self{VALUE}{Error}; | 
| 2554 |  |  |  |  |  |  | } | 
| 2555 |  |  |  |  |  |  |  | 
| 2556 |  |  |  |  |  |  | # rewrite original file in place if required | 
| 2557 | 235 | 100 |  |  |  | 949 | if (defined $outBuff) { | 
| 2558 | 1 | 50 | 33 |  |  | 15 | if ($rtnVal <= 0 or not $$self{CHANGED}) { | 
|  |  | 50 |  |  |  |  |  | 
| 2559 |  |  |  |  |  |  | # nothing changed, so no need to write $outBuff | 
| 2560 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($inRef,'GLOB')) { | 
| 2561 | 1 |  |  |  |  | 3 | my $len = length($outBuff); | 
| 2562 | 1 |  |  |  |  | 3 | my $size; | 
| 2563 |  |  |  |  |  |  | $rtnVal = -1 unless | 
| 2564 |  |  |  |  |  |  | seek($inRef, 0, 2) and          # seek to the end of file | 
| 2565 |  |  |  |  |  |  | ($size = tell $inRef) >= 0 and  # get the file size | 
| 2566 |  |  |  |  |  |  | seek($inRef, 0, 0) and          # seek back to the start | 
| 2567 |  |  |  |  |  |  | print $inRef $outBuff and       # write the new data | 
| 2568 |  |  |  |  |  |  | ($len >= $size or               # if necessary: | 
| 2569 | 1 | 50 | 33 |  |  | 39 | eval { truncate($inRef, $len) }); #  shorten output file | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 2570 |  |  |  |  |  |  | } else { | 
| 2571 | 0 |  |  |  |  | 0 | $$inRef = $outBuff;                 # replace original data | 
| 2572 |  |  |  |  |  |  | } | 
| 2573 | 1 |  |  |  |  | 6 | $outBuff = '';  # free memory but leave $outBuff defined | 
| 2574 |  |  |  |  |  |  | } | 
| 2575 |  |  |  |  |  |  | # close input file if we opened it | 
| 2576 | 235 | 100 |  |  |  | 848 | if ($closeIn) { | 
| 2577 |  |  |  |  |  |  | # errors on input file are significant if we edited the file in place | 
| 2578 | 207 | 50 | 0 |  |  | 4718 | $rtnVal and $rtnVal = -1 unless close($inRef) or not defined $outBuff; | 
|  |  |  | 33 |  |  |  |  | 
| 2579 | 207 | 50 |  |  |  | 1115 | if ($rtnVal > 0) { | 
| 2580 |  |  |  |  |  |  | # copy Mac OS resource fork if it exists | 
| 2581 | 207 | 50 | 33 |  |  | 1593 | if ($^O eq 'darwin' and -s "$infile/..namedfork/rsrc") { | 
| 2582 | 0 | 0 |  |  |  | 0 | if ($$self{DEL_GROUP}{RSRC}) { | 
| 2583 | 0 |  |  |  |  | 0 | $self->VPrint(0,"Deleting Mac OS resource fork\n"); | 
| 2584 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 2585 |  |  |  |  |  |  | } else { | 
| 2586 | 0 |  |  |  |  | 0 | $self->VPrint(0,"Copying Mac OS resource fork\n"); | 
| 2587 | 0 |  |  |  |  | 0 | my ($buf, $err); | 
| 2588 | 0 |  |  |  |  | 0 | local (*SRC, *DST); | 
| 2589 | 0 | 0 |  |  |  | 0 | if ($self->Open(\*SRC, "$infile/..namedfork/rsrc")) { | 
| 2590 | 0 | 0 |  |  |  | 0 | if ($self->Open(\*DST, "$outfile/..namedfork/rsrc", '>')) { | 
| 2591 | 0 |  |  |  |  | 0 | binmode SRC; # (not necessary for Darwin, but let's be thorough) | 
| 2592 | 0 |  |  |  |  | 0 | binmode DST; | 
| 2593 | 0 |  |  |  |  | 0 | while (read SRC, $buf, 65536) { | 
| 2594 | 0 | 0 |  |  |  | 0 | print DST $buf or $err = 'copying', last; | 
| 2595 |  |  |  |  |  |  | } | 
| 2596 | 0 | 0 | 0 |  |  | 0 | close DST or $err or $err = 'closing'; | 
| 2597 |  |  |  |  |  |  | } else { | 
| 2598 |  |  |  |  |  |  | # (this is normal if the destination filesystem isn't Mac OS) | 
| 2599 | 0 |  |  |  |  | 0 | $self->Warn('Error creating Mac OS resource fork'); | 
| 2600 |  |  |  |  |  |  | } | 
| 2601 | 0 |  |  |  |  | 0 | close SRC; | 
| 2602 |  |  |  |  |  |  | } else { | 
| 2603 | 0 |  |  |  |  | 0 | $err = 'opening'; | 
| 2604 |  |  |  |  |  |  | } | 
| 2605 | 0 | 0 | 0 |  |  | 0 | $rtnVal = 0 if $err and $self->Error("Error $err Mac OS resource fork", 2); | 
| 2606 |  |  |  |  |  |  | } | 
| 2607 |  |  |  |  |  |  | } | 
| 2608 |  |  |  |  |  |  | # erase input file if renaming while editing information in place | 
| 2609 | 207 | 50 | 0 |  |  | 832 | $self->Unlink($infile) or $self->Warn('Error erasing original file') if $eraseIn; | 
| 2610 |  |  |  |  |  |  | } | 
| 2611 |  |  |  |  |  |  | } | 
| 2612 |  |  |  |  |  |  | # close output file if we created it | 
| 2613 | 235 | 100 |  |  |  | 914 | if ($closeOut) { | 
| 2614 |  |  |  |  |  |  | # close file and set $rtnVal to -1 if there was an error | 
| 2615 | 221 | 50 | 0 |  |  | 16594 | $rtnVal and $rtnVal = -1 unless close($outRef); | 
| 2616 |  |  |  |  |  |  | # erase the output file if we weren't successful | 
| 2617 | 221 | 50 |  |  |  | 1916 | if ($rtnVal <= 0) { | 
|  |  | 100 |  |  |  |  |  | 
| 2618 | 0 |  |  |  |  | 0 | $self->Unlink($outfile); | 
| 2619 |  |  |  |  |  |  | # else rename temporary file if necessary | 
| 2620 |  |  |  |  |  |  | } elsif ($tmpfile) { | 
| 2621 | 2 |  |  |  |  | 20 | $self->CopyFileAttrs($infile, $tmpfile);    # copy attributes to new file | 
| 2622 | 2 | 50 |  |  |  | 27 | unless ($self->Rename($tmpfile, $infile)) { | 
| 2623 |  |  |  |  |  |  | # some filesystems won't overwrite with 'rename', so try erasing original | 
| 2624 | 0 | 0 |  |  |  | 0 | if (not $self->Unlink($infile)) { | 
|  |  | 0 |  |  |  |  |  | 
| 2625 | 0 |  |  |  |  | 0 | $self->Unlink($tmpfile); | 
| 2626 | 0 |  |  |  |  | 0 | $self->Error('Error renaming temporary file'); | 
| 2627 | 0 |  |  |  |  | 0 | $rtnVal = 0; | 
| 2628 |  |  |  |  |  |  | } elsif (not $self->Rename($tmpfile, $infile)) { | 
| 2629 | 0 |  |  |  |  | 0 | $self->Error('Error renaming temporary file after deleting original'); | 
| 2630 | 0 |  |  |  |  | 0 | $rtnVal = 0; | 
| 2631 |  |  |  |  |  |  | } | 
| 2632 |  |  |  |  |  |  | } | 
| 2633 |  |  |  |  |  |  | # the output file should now have the name of the original infile | 
| 2634 | 2 | 50 |  |  |  | 13 | $outfile = $infile if $rtnVal > 0; | 
| 2635 |  |  |  |  |  |  | } | 
| 2636 |  |  |  |  |  |  | } | 
| 2637 |  |  |  |  |  |  | # set filesystem attributes if requested (and if possible!) | 
| 2638 | 235 | 50 | 100 |  |  | 2057 | if ($rtnVal > 0 and ($closeOut or (defined $outBuff and ($closeIn or UNIVERSAL::isa($infile,'GLOB'))))) { | 
|  |  |  | 66 |  |  |  |  | 
| 2639 | 222 | 100 |  |  |  | 1419 | my $target = $closeOut ? $outfile : $infile; | 
| 2640 |  |  |  |  |  |  | # set file permissions if requested | 
| 2641 | 222 | 50 |  |  |  | 1679 | ++$$self{CHANGED} if $self->SetSystemTags($target) > 0; | 
| 2642 | 222 | 100 |  |  |  | 980 | if ($closeIn) { # (no use setting file times unless the input file is closed) | 
| 2643 | 198 | 50 | 33 |  |  | 946 | ++$$self{CHANGED} if $setModDate and $self->SetFileModifyDate($target, $originalTime, undef, 1) > 0; | 
| 2644 |  |  |  |  |  |  | # set FileCreateDate if requested (and if possible!) | 
| 2645 | 198 | 50 | 33 |  |  | 935 | ++$$self{CHANGED} if $setCreateDate and $self->SetFileModifyDate($target, $createTime, 'FileCreateDate', 1) > 0; | 
| 2646 |  |  |  |  |  |  | # create hard link if requested and no output filename specified (and if possible!) | 
| 2647 | 198 | 50 | 33 |  |  | 932 | ++$$self{CHANGED} if defined $hardLink and $self->SetFileName($target, $hardLink, 'HardLink'); | 
| 2648 | 198 | 50 | 33 |  |  | 868 | ++$$self{CHANGED} if defined $symLink and $self->SetFileName($target, $symLink, 'SymLink'); | 
| 2649 | 198 | 50 |  |  |  | 815 | defined $testName and $self->SetFileName($target, $testName, 'Test'); | 
| 2650 |  |  |  |  |  |  | } | 
| 2651 |  |  |  |  |  |  | } | 
| 2652 |  |  |  |  |  |  | # check for write error and set appropriate error message and return value | 
| 2653 | 235 | 50 |  |  |  | 1366 | if ($rtnVal < 0) { | 
|  |  | 50 |  |  |  |  |  | 
| 2654 | 0 | 0 |  |  |  | 0 | $self->Error('Error writing output file') unless $$self{VALUE}{Error}; | 
| 2655 | 0 |  |  |  |  | 0 | $rtnVal = 0;    # return 0 on failure | 
| 2656 |  |  |  |  |  |  | } elsif ($rtnVal > 0) { | 
| 2657 | 235 | 100 |  |  |  | 1019 | ++$rtnVal unless $$self{CHANGED}; | 
| 2658 |  |  |  |  |  |  | } | 
| 2659 |  |  |  |  |  |  | # set things back to the way they were | 
| 2660 | 235 |  |  |  |  | 850 | $$self{RAF} = $oldRaf; | 
| 2661 |  |  |  |  |  |  |  | 
| 2662 | 235 |  |  |  |  | 2704 | return $rtnVal; | 
| 2663 |  |  |  |  |  |  | } | 
| 2664 |  |  |  |  |  |  |  | 
| 2665 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2666 |  |  |  |  |  |  | # Get list of all available tags for specified group | 
| 2667 |  |  |  |  |  |  | # Inputs: 0) optional group name (or string of names separated by colons) | 
| 2668 |  |  |  |  |  |  | # Returns: tag list (sorted alphabetically) | 
| 2669 |  |  |  |  |  |  | # Notes: Can't get tags for specific IFD | 
| 2670 |  |  |  |  |  |  | sub GetAllTags(;$) | 
| 2671 |  |  |  |  |  |  | { | 
| 2672 | 0 |  |  | 0 | 1 | 0 | local $_; | 
| 2673 | 0 |  |  |  |  | 0 | my $group = shift; | 
| 2674 | 0 |  |  |  |  | 0 | my (%allTags, @groups); | 
| 2675 | 0 | 0 |  |  |  | 0 | @groups = split ':', $group if $group; | 
| 2676 |  |  |  |  |  |  |  | 
| 2677 | 0 |  |  |  |  | 0 | my $et = new Image::ExifTool; | 
| 2678 | 0 |  |  |  |  | 0 | LoadAllTables();    # first load all our tables | 
| 2679 | 0 |  |  |  |  | 0 | my @tableNames = keys %allTables; | 
| 2680 |  |  |  |  |  |  |  | 
| 2681 |  |  |  |  |  |  | # loop through all tables and save tag names to %allTags hash | 
| 2682 | 0 |  |  |  |  | 0 | while (@tableNames) { | 
| 2683 | 0 |  |  |  |  | 0 | my $table = GetTagTable(pop @tableNames); | 
| 2684 |  |  |  |  |  |  | # generate flattened tag names for structure fields if this is an XMP table | 
| 2685 | 0 | 0 | 0 |  |  | 0 | if ($$table{GROUPS} and $$table{GROUPS}{0} eq 'XMP') { | 
| 2686 | 0 |  |  |  |  | 0 | Image::ExifTool::XMP::AddFlattenedTags($table); | 
| 2687 |  |  |  |  |  |  | } | 
| 2688 | 0 |  |  |  |  | 0 | my $tagID; | 
| 2689 | 0 |  |  |  |  | 0 | foreach $tagID (TagTableKeys($table)) { | 
| 2690 | 0 |  |  |  |  | 0 | my @infoArray = GetTagInfoList($table,$tagID); | 
| 2691 | 0 |  |  |  |  | 0 | my $tagInfo; | 
| 2692 | 0 |  |  |  |  | 0 | GATInfo:    foreach $tagInfo (@infoArray) { | 
| 2693 | 0 |  |  |  |  | 0 | my $tag = $$tagInfo{Name}; | 
| 2694 | 0 | 0 |  |  |  | 0 | $tag or warn("no name for tag!\n"), next; | 
| 2695 |  |  |  |  |  |  | # don't list subdirectories unless they are writable | 
| 2696 | 0 | 0 | 0 |  |  | 0 | next if $$tagInfo{SubDirectory} and not $$tagInfo{Writable}; | 
| 2697 | 0 | 0 |  |  |  | 0 | next if $$tagInfo{Hidden};  # ignore hidden tags | 
| 2698 | 0 | 0 |  |  |  | 0 | if (@groups) { | 
| 2699 | 0 |  |  |  |  | 0 | my @tg = $et->GetGroup($tagInfo); | 
| 2700 | 0 |  |  |  |  | 0 | foreach $group (@groups) { | 
| 2701 | 0 | 0 |  |  |  | 0 | next GATInfo unless grep /^$group$/i, @tg; | 
| 2702 |  |  |  |  |  |  | } | 
| 2703 |  |  |  |  |  |  | } | 
| 2704 | 0 |  |  |  |  | 0 | $allTags{$tag} = 1; | 
| 2705 |  |  |  |  |  |  | } | 
| 2706 |  |  |  |  |  |  | } | 
| 2707 |  |  |  |  |  |  | } | 
| 2708 | 0 |  |  |  |  | 0 | return sort keys %allTags; | 
| 2709 |  |  |  |  |  |  | } | 
| 2710 |  |  |  |  |  |  |  | 
| 2711 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2712 |  |  |  |  |  |  | # Get list of all writable tags | 
| 2713 |  |  |  |  |  |  | # Inputs: 0) optional group name (or names separated by colons) | 
| 2714 |  |  |  |  |  |  | # Returns: tag list (sorted alphabetically) | 
| 2715 |  |  |  |  |  |  | sub GetWritableTags(;$) | 
| 2716 |  |  |  |  |  |  | { | 
| 2717 | 0 |  |  | 0 | 1 | 0 | local $_; | 
| 2718 | 0 |  |  |  |  | 0 | my $group = shift; | 
| 2719 | 0 |  |  |  |  | 0 | my (%writableTags, @groups); | 
| 2720 | 0 | 0 |  |  |  | 0 | @groups = split ':', $group if $group; | 
| 2721 |  |  |  |  |  |  |  | 
| 2722 | 0 |  |  |  |  | 0 | my $et = new Image::ExifTool; | 
| 2723 | 0 |  |  |  |  | 0 | LoadAllTables(); | 
| 2724 | 0 |  |  |  |  | 0 | my @tableNames = keys %allTables; | 
| 2725 |  |  |  |  |  |  |  | 
| 2726 | 0 |  |  |  |  | 0 | while (@tableNames) { | 
| 2727 | 0 |  |  |  |  | 0 | my $tableName = pop @tableNames; | 
| 2728 | 0 |  |  |  |  | 0 | my $table = GetTagTable($tableName); | 
| 2729 |  |  |  |  |  |  | # generate flattened tag names for structure fields if this is an XMP table | 
| 2730 | 0 | 0 | 0 |  |  | 0 | if ($$table{GROUPS} and $$table{GROUPS}{0} eq 'XMP') { | 
| 2731 | 0 |  |  |  |  | 0 | Image::ExifTool::XMP::AddFlattenedTags($table); | 
| 2732 |  |  |  |  |  |  | } | 
| 2733 |  |  |  |  |  |  | # attempt to load Write tables if autoloaded | 
| 2734 | 0 |  |  |  |  | 0 | my @parts = split(/::/,$tableName); | 
| 2735 | 0 | 0 |  |  |  | 0 | if (@parts > 3) { | 
| 2736 | 0 |  |  |  |  | 0 | my $i = $#parts - 1; | 
| 2737 | 0 |  |  |  |  | 0 | $parts[$i] = "Write$parts[$i]";   # add 'Write' before class name | 
| 2738 | 0 |  |  |  |  | 0 | my $module = join('::',@parts[0..$i]); | 
| 2739 | 0 |  |  |  |  | 0 | eval { require $module }; # (fails silently if nothing loaded) | 
|  | 0 |  |  |  |  | 0 |  | 
| 2740 |  |  |  |  |  |  | } | 
| 2741 | 0 |  |  |  |  | 0 | my $tagID; | 
| 2742 | 0 |  |  |  |  | 0 | foreach $tagID (TagTableKeys($table)) { | 
| 2743 | 0 |  |  |  |  | 0 | my @infoArray = GetTagInfoList($table,$tagID); | 
| 2744 | 0 |  |  |  |  | 0 | my $tagInfo; | 
| 2745 | 0 |  |  |  |  | 0 | GWTInfo:    foreach $tagInfo (@infoArray) { | 
| 2746 | 0 |  |  |  |  | 0 | my $tag = $$tagInfo{Name}; | 
| 2747 | 0 | 0 |  |  |  | 0 | $tag or warn("no name for tag!\n"), next; | 
| 2748 | 0 |  |  |  |  | 0 | my $writable = $$tagInfo{Writable}; | 
| 2749 |  |  |  |  |  |  | next unless $writable or ($$table{WRITABLE} and | 
| 2750 | 0 | 0 | 0 |  |  | 0 | not defined $writable and not $$tagInfo{SubDirectory}); | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 2751 | 0 | 0 |  |  |  | 0 | next if $$tagInfo{Hidden};  # ignore hidden tags | 
| 2752 | 0 | 0 |  |  |  | 0 | if (@groups) { | 
| 2753 | 0 |  |  |  |  | 0 | my @tg = $et->GetGroup($tagInfo); | 
| 2754 | 0 |  |  |  |  | 0 | foreach $group (@groups) { | 
| 2755 | 0 | 0 |  |  |  | 0 | next GWTInfo unless grep /^$group$/i, @tg; | 
| 2756 |  |  |  |  |  |  | } | 
| 2757 |  |  |  |  |  |  | } | 
| 2758 | 0 |  |  |  |  | 0 | $writableTags{$tag} = 1; | 
| 2759 |  |  |  |  |  |  | } | 
| 2760 |  |  |  |  |  |  | } | 
| 2761 |  |  |  |  |  |  | } | 
| 2762 | 0 |  |  |  |  | 0 | return sort keys %writableTags; | 
| 2763 |  |  |  |  |  |  | } | 
| 2764 |  |  |  |  |  |  |  | 
| 2765 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2766 |  |  |  |  |  |  | # Get list of all group names | 
| 2767 |  |  |  |  |  |  | # Inputs: 0) [optional] ExifTool ref, 1) Group family number | 
| 2768 |  |  |  |  |  |  | # Returns: List of group names (sorted alphabetically) | 
| 2769 |  |  |  |  |  |  | sub GetAllGroups($;$) | 
| 2770 |  |  |  |  |  |  | { | 
| 2771 | 0 |  |  | 0 | 1 | 0 | local $_; | 
| 2772 | 0 |  | 0 |  |  | 0 | my $family = shift || 0; | 
| 2773 | 0 |  |  |  |  | 0 | my $self; | 
| 2774 | 0 | 0 | 0 |  |  | 0 | ref $family and $self = $family, $family = shift || 0; | 
| 2775 |  |  |  |  |  |  |  | 
| 2776 | 0 | 0 |  |  |  | 0 | $family == 3 and return('Doc#', 'Main'); | 
| 2777 | 0 | 0 |  |  |  | 0 | $family == 4 and return('Copy#'); | 
| 2778 | 0 | 0 |  |  |  | 0 | $family == 5 and return('[too many possibilities to list]'); | 
| 2779 | 0 | 0 |  |  |  | 0 | $family == 6 and return(@Image::ExifTool::Exif::formatName[1..$#Image::ExifTool::Exif::formatName]); | 
| 2780 | 0 | 0 |  |  |  | 0 | $family == 8 and return('File#'); | 
| 2781 |  |  |  |  |  |  |  | 
| 2782 | 0 |  |  |  |  | 0 | LoadAllTables();    # first load all our tables | 
| 2783 |  |  |  |  |  |  |  | 
| 2784 | 0 |  |  |  |  | 0 | my @tableNames = keys %allTables; | 
| 2785 |  |  |  |  |  |  |  | 
| 2786 | 0 |  |  |  |  | 0 | my %allGroups; | 
| 2787 |  |  |  |  |  |  | # add family 1 groups not in tables | 
| 2788 | 0 | 0 |  |  |  | 0 | $family == 1 and map { $allGroups{$_} = 1 } qw(Garmin); | 
|  | 0 |  |  |  |  | 0 |  | 
| 2789 |  |  |  |  |  |  | # loop through all tag tables and get all group names | 
| 2790 | 0 |  |  |  |  | 0 | while (@tableNames) { | 
| 2791 | 0 |  |  |  |  | 0 | my $table = GetTagTable(pop @tableNames); | 
| 2792 | 0 |  |  |  |  | 0 | my ($grps, $grp, $tag, $tagInfo); | 
| 2793 | 0 | 0 | 0 |  |  | 0 | $allGroups{$grp} = 1 if ($grps = $$table{GROUPS}) and ($grp = $$grps{$family}); | 
| 2794 | 0 |  |  |  |  | 0 | foreach $tag (TagTableKeys($table)) { | 
| 2795 | 0 |  |  |  |  | 0 | my @infoArray = GetTagInfoList($table, $tag); | 
| 2796 | 0 | 0 |  |  |  | 0 | if ($family == 7) { | 
| 2797 | 0 |  |  |  |  | 0 | foreach $tagInfo (@infoArray) { | 
| 2798 | 0 |  |  |  |  | 0 | my $id = $$tagInfo{TagID}; | 
| 2799 | 0 | 0 |  |  |  | 0 | if (not defined $id) { | 
|  |  | 0 |  |  |  |  |  | 
| 2800 | 0 |  |  |  |  | 0 | $id = '';   # (just to be safe) | 
| 2801 |  |  |  |  |  |  | } elsif ($id =~ /^\d+$/) { | 
| 2802 | 0 | 0 | 0 |  |  | 0 | $id = sprintf('0x%x', $id) if $self and $$self{OPTIONS}{HexTagIDs}; | 
| 2803 |  |  |  |  |  |  | } else { | 
| 2804 | 0 |  |  |  |  | 0 | $id =~ s/([^-_A-Za-z0-9])/sprintf('%.2x',ord $1)/ge; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2805 |  |  |  |  |  |  | } | 
| 2806 | 0 |  |  |  |  | 0 | $allGroups{'ID-' . $id} = 1; | 
| 2807 |  |  |  |  |  |  | } | 
| 2808 |  |  |  |  |  |  | } else { | 
| 2809 | 0 |  |  |  |  | 0 | foreach $tagInfo (@infoArray) { | 
| 2810 | 0 | 0 | 0 |  |  | 0 | next unless ($grps = $$tagInfo{Groups}) and ($grp = $$grps{$family}); | 
| 2811 | 0 |  |  |  |  | 0 | $allGroups{$grp} = 1; | 
| 2812 |  |  |  |  |  |  | } | 
| 2813 |  |  |  |  |  |  | } | 
| 2814 |  |  |  |  |  |  | } | 
| 2815 |  |  |  |  |  |  | } | 
| 2816 | 0 |  |  |  |  | 0 | delete $allGroups{'*'};     # (not a real group) | 
| 2817 | 0 |  |  |  |  | 0 | return sort keys %allGroups; | 
| 2818 |  |  |  |  |  |  | } | 
| 2819 |  |  |  |  |  |  |  | 
| 2820 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2821 |  |  |  |  |  |  | # get priority group list for new values | 
| 2822 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 2823 |  |  |  |  |  |  | # Returns: List of group names | 
| 2824 |  |  |  |  |  |  | sub GetNewGroups($) | 
| 2825 |  |  |  |  |  |  | { | 
| 2826 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 2827 | 0 |  |  |  |  | 0 | return @{$$self{WRITE_GROUPS}}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 2828 |  |  |  |  |  |  | } | 
| 2829 |  |  |  |  |  |  |  | 
| 2830 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2831 |  |  |  |  |  |  | # Get list of all deletable group names | 
| 2832 |  |  |  |  |  |  | # Returns: List of group names (sorted alphabetically) | 
| 2833 |  |  |  |  |  |  | sub GetDeleteGroups() | 
| 2834 |  |  |  |  |  |  | { | 
| 2835 | 0 |  |  | 0 | 1 | 0 | return sort @delGroups, @delGroup2; | 
| 2836 |  |  |  |  |  |  | } | 
| 2837 |  |  |  |  |  |  |  | 
| 2838 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2839 |  |  |  |  |  |  | # Add user-defined tags at run time | 
| 2840 |  |  |  |  |  |  | # Inputs: 0) destination table name, 1) tagID/tagInfo pairs for tags to add | 
| 2841 |  |  |  |  |  |  | # Returns: number of tags added | 
| 2842 |  |  |  |  |  |  | # Notes: will replace existing tags | 
| 2843 |  |  |  |  |  |  | sub AddUserDefinedTags($%) | 
| 2844 |  |  |  |  |  |  | { | 
| 2845 | 2 |  |  | 2 | 1 | 629 | local $_; | 
| 2846 | 2 |  |  |  |  | 14 | my ($tableName, %addTags) = @_; | 
| 2847 | 2 | 50 |  |  |  | 10 | my $table = GetTagTable($tableName) or return 0; | 
| 2848 |  |  |  |  |  |  | # add tags to writer lookup | 
| 2849 | 2 |  |  |  |  | 19 | Image::ExifTool::TagLookup::AddTags(\%addTags, $tableName); | 
| 2850 | 2 |  |  |  |  | 6 | my $tagID; | 
| 2851 | 2 |  |  |  |  | 7 | my $num = 0; | 
| 2852 | 2 |  |  |  |  | 9 | foreach $tagID (keys %addTags) { | 
| 2853 | 2 | 50 |  |  |  | 11 | next if $specialTags{$tagID}; | 
| 2854 | 2 |  |  |  |  | 11 | delete $$table{$tagID}; # delete old entry if it existed | 
| 2855 | 2 |  |  |  |  | 16 | AddTagToTable($table, $tagID, $addTags{$tagID}, 1); | 
| 2856 | 2 |  |  |  |  | 7 | ++$num; | 
| 2857 |  |  |  |  |  |  | } | 
| 2858 | 2 |  |  |  |  | 10 | return $num; | 
| 2859 |  |  |  |  |  |  | } | 
| 2860 |  |  |  |  |  |  |  | 
| 2861 |  |  |  |  |  |  | #============================================================================== | 
| 2862 |  |  |  |  |  |  | # Functions below this are not part of the public API | 
| 2863 |  |  |  |  |  |  |  | 
| 2864 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2865 |  |  |  |  |  |  | # Maintain backward compatibility for old GetNewValues function name | 
| 2866 |  |  |  |  |  |  | sub GetNewValues($$;$) | 
| 2867 |  |  |  |  |  |  | { | 
| 2868 | 0 |  |  | 0 | 0 | 0 | my ($self, $tag, $nvHashPt) = @_; | 
| 2869 | 0 |  |  |  |  | 0 | return $self->GetNewValue($tag, $nvHashPt); | 
| 2870 |  |  |  |  |  |  | } | 
| 2871 |  |  |  |  |  |  |  | 
| 2872 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2873 |  |  |  |  |  |  | # Un-escape string according to options settings and clear UTF-8 flag | 
| 2874 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) string ref or string ref ref | 
| 2875 |  |  |  |  |  |  | # Notes: also de-references SCALAR values | 
| 2876 |  |  |  |  |  |  | sub Sanitize($$) | 
| 2877 |  |  |  |  |  |  | { | 
| 2878 | 5400 |  |  | 5400 | 0 | 10868 | my ($self, $valPt) = @_; | 
| 2879 |  |  |  |  |  |  | # de-reference SCALAR references | 
| 2880 | 5400 | 50 |  |  |  | 13872 | $$valPt = $$$valPt if ref $$valPt eq 'SCALAR'; | 
| 2881 |  |  |  |  |  |  | # make sure the Perl UTF-8 flag is OFF for the value if perl 5.6 or greater | 
| 2882 |  |  |  |  |  |  | # (otherwise our byte manipulations get corrupted!!) | 
| 2883 | 5400 | 50 | 33 |  |  | 15654 | if ($] >= 5.006 and (eval { require Encode; Encode::is_utf8($$valPt) } or $@)) { | 
|  |  |  | 33 |  |  |  |  | 
| 2884 | 0 |  |  |  |  | 0 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 2885 |  |  |  |  |  |  | # repack by hand if Encode isn't available | 
| 2886 | 0 | 0 |  |  |  | 0 | $$valPt = $@ ? pack('C*',unpack($] < 5.010000 ? 'U0C*' : 'C0C*',$$valPt)) : Encode::encode('utf8',$$valPt); | 
|  |  | 0 |  |  |  |  |  | 
| 2887 |  |  |  |  |  |  | } | 
| 2888 |  |  |  |  |  |  | # un-escape value if necessary | 
| 2889 | 5400 | 100 |  |  |  | 19708 | if ($$self{OPTIONS}{Escape}) { | 
| 2890 |  |  |  |  |  |  | # (XMP.pm and HTML.pm were require'd as necessary when option was set) | 
| 2891 | 92 | 50 |  |  |  | 314 | if ($$self{OPTIONS}{Escape} eq 'XML') { | 
|  |  | 50 |  |  |  |  |  | 
| 2892 | 0 |  |  |  |  | 0 | $$valPt = Image::ExifTool::XMP::UnescapeXML($$valPt); | 
| 2893 |  |  |  |  |  |  | } elsif ($$self{OPTIONS}{Escape} eq 'HTML') { | 
| 2894 | 92 |  |  |  |  | 290 | $$valPt = Image::ExifTool::HTML::UnescapeHTML($$valPt, $$self{OPTIONS}{Charset}); | 
| 2895 |  |  |  |  |  |  | } | 
| 2896 |  |  |  |  |  |  | } | 
| 2897 |  |  |  |  |  |  | } | 
| 2898 |  |  |  |  |  |  |  | 
| 2899 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 2900 |  |  |  |  |  |  | # Apply inverse conversions | 
| 2901 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) value, 2) tagInfo (or Struct item) ref, | 
| 2902 |  |  |  |  |  |  | #         3) tag name, 4) group 1 name, 5) conversion type (or undef), | 
| 2903 |  |  |  |  |  |  | #         6) [optional] want group ("" for structure field) | 
| 2904 |  |  |  |  |  |  | # Returns: 0) converted value, 1) error string (or undef on success) | 
| 2905 |  |  |  |  |  |  | # Notes: | 
| 2906 |  |  |  |  |  |  | # - uses ExifTool "ConvType" member when conversion type is undef | 
| 2907 |  |  |  |  |  |  | # - conversion types other than 'ValueConv' and 'PrintConv' are treated as 'Raw' | 
| 2908 |  |  |  |  |  |  | sub ConvInv($$$$$;$$) | 
| 2909 |  |  |  |  |  |  | { | 
| 2910 | 28276 |  |  | 28276 | 0 | 77338 | my ($self, $val, $tagInfo, $tag, $wgrp1, $convType, $wantGroup) = @_; | 
| 2911 | 28276 |  |  |  |  | 43774 | my ($err, $type); | 
| 2912 |  |  |  |  |  |  |  | 
| 2913 | 28276 | 100 | 50 |  |  | 56462 | $convType or $convType = $$self{ConvType} || 'PrintConv'; | 
| 2914 |  |  |  |  |  |  |  | 
| 2915 | 28276 |  |  |  |  | 49509 | Conv: for (;;) { | 
| 2916 | 73158 | 100 |  |  |  | 171961 | if (not defined $type) { | 
|  |  | 100 |  |  |  |  |  | 
| 2917 |  |  |  |  |  |  | # split value into list if necessary | 
| 2918 | 28276 | 100 |  |  |  | 70869 | if ($$tagInfo{List}) { | 
| 2919 | 583 |  | 100 |  |  | 3214 | my $listSplit = $$tagInfo{AutoSplit} || $$self{OPTIONS}{ListSplit}; | 
| 2920 | 583 | 50 | 100 |  |  | 2541 | if (defined $listSplit and not $$tagInfo{Struct} and | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 2921 |  |  |  |  |  |  | ($wantGroup or not defined $wantGroup)) | 
| 2922 |  |  |  |  |  |  | { | 
| 2923 | 74 | 50 | 66 |  |  | 487 | $listSplit = ',?\s+' if $listSplit eq '1' and $$tagInfo{AutoSplit}; | 
| 2924 | 74 |  |  |  |  | 825 | my @splitVal = split /$listSplit/, $val, -1; | 
| 2925 | 74 | 50 |  |  |  | 450 | $val = @splitVal > 1 ? \@splitVal : @splitVal ? $splitVal[0] : ''; | 
|  |  | 100 |  |  |  |  |  | 
| 2926 |  |  |  |  |  |  | } | 
| 2927 |  |  |  |  |  |  | } | 
| 2928 | 28276 |  |  |  |  | 47065 | $type = $convType; | 
| 2929 |  |  |  |  |  |  | } elsif ($type eq 'PrintConv') { | 
| 2930 | 21738 |  |  |  |  | 38024 | $type = 'ValueConv'; | 
| 2931 |  |  |  |  |  |  | } else { | 
| 2932 |  |  |  |  |  |  | # split raw value if necessary | 
| 2933 | 23144 | 50 | 66 |  |  | 56472 | if ($$tagInfo{RawJoin} and $$tagInfo{List} and not ref $val) { | 
|  |  |  | 33 |  |  |  |  | 
| 2934 | 13 |  |  |  |  | 72 | my @splitVal = split ' ', $val; | 
| 2935 | 13 | 50 |  |  |  | 78 | $val = \@splitVal if @splitVal > 1; | 
| 2936 |  |  |  |  |  |  | } | 
| 2937 |  |  |  |  |  |  | # finally, do our value check | 
| 2938 | 23144 |  |  |  |  | 36189 | my ($err2, $v); | 
| 2939 | 23144 | 100 |  |  |  | 60291 | if ($$tagInfo{WriteCheck}) { | 
| 2940 |  |  |  |  |  |  | #### eval WriteCheck ($self, $tagInfo, $val) | 
| 2941 | 296 |  |  |  |  | 23891 | $err2 = eval $$tagInfo{WriteCheck}; | 
| 2942 | 296 | 50 |  |  |  | 1611 | $@ and warn($@), $err2 = 'Error evaluating WriteCheck'; | 
| 2943 |  |  |  |  |  |  | } | 
| 2944 | 23144 | 100 |  |  |  | 51427 | unless ($err2) { | 
| 2945 | 23110 |  |  |  |  | 46337 | my $table = $$tagInfo{Table}; | 
| 2946 | 23110 | 100 | 100 |  |  | 138632 | if ($table and $$table{CHECK_PROC} and not $$tagInfo{RawConvInv}) { | 
|  |  |  | 100 |  |  |  |  | 
| 2947 | 22225 |  |  |  |  | 39397 | my $checkProc = $$table{CHECK_PROC}; | 
| 2948 | 22225 | 100 |  |  |  | 44875 | if (ref $val eq 'ARRAY') { | 
| 2949 |  |  |  |  |  |  | # loop through array values | 
| 2950 | 46 |  |  |  |  | 193 | foreach $v (@$val) { | 
| 2951 | 136 |  |  |  |  | 459 | $err2 = &$checkProc($self, $tagInfo, \$v, $convType); | 
| 2952 | 136 | 50 |  |  |  | 454 | last if $err2; | 
| 2953 |  |  |  |  |  |  | } | 
| 2954 |  |  |  |  |  |  | } else { | 
| 2955 | 22179 |  |  |  |  | 69076 | $err2 = &$checkProc($self, $tagInfo, \$val, $convType); | 
| 2956 |  |  |  |  |  |  | } | 
| 2957 |  |  |  |  |  |  | } | 
| 2958 |  |  |  |  |  |  | } | 
| 2959 | 23144 | 100 |  |  |  | 55299 | if (defined $err2) { | 
| 2960 | 3344 | 100 |  |  |  | 6957 | if ($err2) { | 
| 2961 | 3336 |  |  |  |  | 8583 | $err = "$err2 for $wgrp1:$tag"; | 
| 2962 | 3336 |  |  |  |  | 16108 | $self->VPrint(2, "$err\n"); | 
| 2963 | 3336 |  |  |  |  | 7023 | undef $val;     # value was invalid | 
| 2964 |  |  |  |  |  |  | } else { | 
| 2965 | 8 |  |  |  |  | 17 | $err = $err2;   # empty error (quietly don't write tag) | 
| 2966 |  |  |  |  |  |  | } | 
| 2967 |  |  |  |  |  |  | } | 
| 2968 | 23144 |  |  |  |  | 40299 | last; | 
| 2969 |  |  |  |  |  |  | } | 
| 2970 | 50014 |  |  |  |  | 103105 | my $conv = $$tagInfo{$type}; | 
| 2971 | 50014 |  |  |  |  | 119176 | my $convInv = $$tagInfo{"${type}Inv"}; | 
| 2972 |  |  |  |  |  |  | # nothing to do at this level if no conversion defined | 
| 2973 | 50014 | 100 | 100 |  |  | 146178 | next unless defined $conv or defined $convInv; | 
| 2974 |  |  |  |  |  |  |  | 
| 2975 | 22525 |  |  |  |  | 37966 | my (@valList, $index, $convList, $convInvList); | 
| 2976 | 22525 | 100 | 66 |  |  | 89844 | if (ref $val eq 'ARRAY') { | 
|  |  | 100 |  |  |  |  |  | 
| 2977 |  |  |  |  |  |  | # handle ValueConv of ListSplit and AutoSplit values | 
| 2978 | 12 |  |  |  |  | 58 | @valList = @$val; | 
| 2979 | 12 |  |  |  |  | 61 | $val = $valList[$index = 0]; | 
| 2980 |  |  |  |  |  |  | } elsif (ref $conv eq 'ARRAY' or ref $convInv eq 'ARRAY') { | 
| 2981 |  |  |  |  |  |  | # handle conversion lists | 
| 2982 | 153 |  |  |  |  | 1464 | @valList = split /$listSep{$type}/, $val; | 
| 2983 | 153 |  |  |  |  | 467 | $val = $valList[$index = 0]; | 
| 2984 | 153 | 50 |  |  |  | 526 | if (ref $conv eq 'ARRAY') { | 
| 2985 | 153 |  |  |  |  | 319 | $convList = $conv; | 
| 2986 | 153 |  |  |  |  | 518 | $conv = $$conv[0]; | 
| 2987 |  |  |  |  |  |  | } | 
| 2988 | 153 | 100 |  |  |  | 464 | if (ref $convInv eq 'ARRAY') { | 
| 2989 | 29 |  |  |  |  | 64 | $convInvList = $convInv; | 
| 2990 | 29 |  |  |  |  | 69 | $convInv = $$convInv[0]; | 
| 2991 |  |  |  |  |  |  | } | 
| 2992 |  |  |  |  |  |  | } | 
| 2993 |  |  |  |  |  |  | # loop through multiple values if necessary | 
| 2994 | 22525 |  |  |  |  | 32934 | for (;;) { | 
| 2995 | 22577 | 100 |  |  |  | 50205 | if ($convInv) { | 
|  |  | 100 |  |  |  |  |  | 
| 2996 |  |  |  |  |  |  | # capture eval warnings too | 
| 2997 | 13657 |  |  |  |  | 61393 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 2998 | 13657 |  |  |  |  | 27252 | undef $evalWarning; | 
| 2999 | 13657 | 100 |  |  |  | 27018 | if (ref($convInv) eq 'CODE') { | 
| 3000 | 144 |  |  |  |  | 756 | $val = &$convInv($val, $self); | 
| 3001 |  |  |  |  |  |  | } else { | 
| 3002 |  |  |  |  |  |  | #### eval PrintConvInv/ValueConvInv ($val, $self, $wantGroup) | 
| 3003 | 13513 |  |  |  |  | 888127 | $val = eval $convInv; | 
| 3004 | 13513 | 100 |  |  |  | 58785 | $@ and $evalWarning = $@; | 
| 3005 |  |  |  |  |  |  | } | 
| 3006 | 13657 | 100 |  |  |  | 59905 | if ($evalWarning) { | 
|  |  | 100 |  |  |  |  |  | 
| 3007 |  |  |  |  |  |  | # an empty warning ("\n") ignores tag with no error | 
| 3008 | 223 | 100 |  |  |  | 691 | if ($evalWarning eq "\n") { | 
| 3009 | 9 | 50 |  |  |  | 44 | $err = '' unless defined $err; | 
| 3010 |  |  |  |  |  |  | } else { | 
| 3011 | 214 |  |  |  |  | 748 | $err = CleanWarning() . " in $wgrp1:$tag (${type}Inv)"; | 
| 3012 | 214 |  |  |  |  | 1005 | $self->VPrint(2, "$err\n"); | 
| 3013 |  |  |  |  |  |  | } | 
| 3014 | 223 |  |  |  |  | 492 | undef $val; | 
| 3015 | 223 |  |  |  |  | 984 | last Conv; | 
| 3016 |  |  |  |  |  |  | } elsif (not defined $val) { | 
| 3017 | 124 |  |  |  |  | 626 | $err = "Error converting value for $wgrp1:$tag (${type}Inv)"; | 
| 3018 | 124 |  |  |  |  | 669 | $self->VPrint(2, "$err\n"); | 
| 3019 | 124 |  |  |  |  | 576 | last Conv; | 
| 3020 |  |  |  |  |  |  | } | 
| 3021 |  |  |  |  |  |  | } elsif ($conv) { | 
| 3022 | 8917 | 100 | 66 |  |  | 40440 | if (ref $conv eq 'HASH' and (not exists $$tagInfo{"${type}Inv"} or $convInvList)) { | 
|  |  | 100 | 66 |  |  |  |  | 
| 3023 | 8730 |  |  |  |  | 14670 | my ($multi, $lc); | 
| 3024 |  |  |  |  |  |  | # insert alternate language print conversions if required | 
| 3025 | 8730 | 0 | 33 |  |  | 22982 | if ($$self{CUR_LANG} and $type eq 'PrintConv' and | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 3026 |  |  |  |  |  |  | ref($lc = $$self{CUR_LANG}{$tag}) eq 'HASH' and | 
| 3027 |  |  |  |  |  |  | ($lc = $$lc{PrintConv})) | 
| 3028 |  |  |  |  |  |  | { | 
| 3029 | 0 |  |  |  |  | 0 | my %newConv; | 
| 3030 | 0 |  |  |  |  | 0 | foreach (keys %$conv) { | 
| 3031 | 0 |  |  |  |  | 0 | my $val = $$conv{$_}; | 
| 3032 | 0 | 0 |  |  |  | 0 | defined $$lc{$val} or $newConv{$_} = $val, next; | 
| 3033 | 0 |  |  |  |  | 0 | $newConv{$_} = $self->Decode($$lc{$val}, 'UTF8'); | 
| 3034 |  |  |  |  |  |  | } | 
| 3035 | 0 | 0 |  |  |  | 0 | if ($$conv{BITMASK}) { | 
| 3036 | 0 |  |  |  |  | 0 | foreach (keys %{$$conv{BITMASK}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 3037 | 0 |  |  |  |  | 0 | my $val = $$conv{BITMASK}{$_}; | 
| 3038 | 0 | 0 |  |  |  | 0 | defined $$lc{$val} or $newConv{BITMASK}{$_} = $val, next; | 
| 3039 | 0 |  |  |  |  | 0 | $newConv{BITMASK}{$_} = $self->Decode($$lc{$val}, 'UTF8'); | 
| 3040 |  |  |  |  |  |  | } | 
| 3041 |  |  |  |  |  |  | } | 
| 3042 | 0 |  |  |  |  | 0 | $conv = \%newConv; | 
| 3043 |  |  |  |  |  |  | } | 
| 3044 | 8730 |  |  |  |  | 14233 | undef $evalWarning; | 
| 3045 | 8730 | 100 |  |  |  | 24216 | if ($$conv{BITMASK}) { | 
| 3046 | 104 |  |  |  |  | 328 | my $lookupBits = $$conv{BITMASK}; | 
| 3047 | 104 |  |  |  |  | 326 | my ($wbits, $tbits) = @$tagInfo{'BitsPerWord','BitsTotal'}; | 
| 3048 | 104 |  |  |  |  | 397 | my ($val2, $err2) = EncodeBits($val, $lookupBits, $wbits, $tbits); | 
| 3049 | 104 | 100 |  |  |  | 411 | if ($err2) { | 
|  |  | 100 |  |  |  |  |  | 
| 3050 |  |  |  |  |  |  | # ok, try matching a straight value | 
| 3051 | 2 |  |  |  |  | 8 | ($val, $multi) = ReverseLookup($val, $conv); | 
| 3052 | 2 | 50 |  |  |  | 14 | unless (defined $val) { | 
| 3053 | 2 |  |  |  |  | 12 | $err = "Can't encode $wgrp1:$tag ($err2)"; | 
| 3054 | 2 |  |  |  |  | 19 | $self->VPrint(2, "$err\n"); | 
| 3055 | 2 |  |  |  |  | 7 | last Conv; | 
| 3056 |  |  |  |  |  |  | } | 
| 3057 |  |  |  |  |  |  | } elsif (defined $val2) { | 
| 3058 | 71 |  |  |  |  | 181 | $val = $val2; | 
| 3059 |  |  |  |  |  |  | } else { | 
| 3060 | 31 |  |  |  |  | 93 | delete $$conv{BITMASK}; | 
| 3061 | 31 |  |  |  |  | 100 | ($val, $multi) = ReverseLookup($val, $conv); | 
| 3062 | 31 |  |  |  |  | 103 | $$conv{BITMASK} = $lookupBits; | 
| 3063 |  |  |  |  |  |  | } | 
| 3064 |  |  |  |  |  |  | } else { | 
| 3065 | 8626 |  |  |  |  | 22565 | ($val, $multi) = ReverseLookup($val, $conv); | 
| 3066 |  |  |  |  |  |  | } | 
| 3067 | 8728 | 100 |  |  |  | 28515 | if (not defined $val) { | 
|  |  | 50 |  |  |  |  |  | 
| 3068 | 4619 | 100 |  |  |  | 16995 | my $prob = $evalWarning ? lcfirst CleanWarning() : ($multi ? 'matches more than one ' : 'not in ') . $type; | 
|  |  | 50 |  |  |  |  |  | 
| 3069 | 4619 |  |  |  |  | 12038 | $err = "Can't convert $wgrp1:$tag ($prob)"; | 
| 3070 | 4619 |  |  |  |  | 21600 | $self->VPrint(2, "$err\n"); | 
| 3071 | 4619 |  |  |  |  | 12235 | last Conv; | 
| 3072 |  |  |  |  |  |  | } elsif ($evalWarning) { | 
| 3073 | 0 |  |  |  |  | 0 | $self->VPrint(2, CleanWarning() . " for $wgrp1:$tag\n"); | 
| 3074 |  |  |  |  |  |  | } | 
| 3075 |  |  |  |  |  |  | } elsif (not $$tagInfo{WriteAlso}) { | 
| 3076 | 164 |  |  |  |  | 640 | $err = "Can't convert value for $wgrp1:$tag (no ${type}Inv)"; | 
| 3077 | 164 |  |  |  |  | 837 | $self->VPrint(2, "$err\n"); | 
| 3078 | 164 |  |  |  |  | 387 | undef $val; | 
| 3079 | 164 |  |  |  |  | 492 | last Conv; | 
| 3080 |  |  |  |  |  |  | } | 
| 3081 |  |  |  |  |  |  | } | 
| 3082 | 17445 | 100 |  |  |  | 58579 | last unless @valList; | 
| 3083 | 124 |  |  |  |  | 413 | $valList[$index] = $val; | 
| 3084 | 124 | 100 |  |  |  | 410 | if (++$index >= @valList) { | 
| 3085 |  |  |  |  |  |  | # leave AutoSplit lists in ARRAY form, or join conversion lists | 
| 3086 | 72 | 100 |  |  |  | 409 | $val = $$tagInfo{List} ? \@valList : join ' ', @valList; | 
| 3087 | 72 |  |  |  |  | 236 | last; | 
| 3088 |  |  |  |  |  |  | } | 
| 3089 | 52 | 100 |  |  |  | 173 | $conv = $$convList[$index] if $convList; | 
| 3090 | 52 | 100 |  |  |  | 160 | $convInv = $$convInvList[$index] if $convInvList; | 
| 3091 | 52 |  |  |  |  | 116 | $val = $valList[$index]; | 
| 3092 |  |  |  |  |  |  | } | 
| 3093 |  |  |  |  |  |  | } # end ValueConv/PrintConv loop | 
| 3094 |  |  |  |  |  |  |  | 
| 3095 | 28276 |  |  |  |  | 86165 | return($val, $err); | 
| 3096 |  |  |  |  |  |  | } | 
| 3097 |  |  |  |  |  |  |  | 
| 3098 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3099 |  |  |  |  |  |  | # Convert tag names to values or variables in a string | 
| 3100 |  |  |  |  |  |  | # (eg. '${EXIF:ISO}x $$' --> '100x $' without hash ref, or "$info{'EXIF:ISO'}x $" with) | 
| 3101 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) reference to list of found tags | 
| 3102 |  |  |  |  |  |  | #         2) string with embedded tag names, 3) Options: | 
| 3103 |  |  |  |  |  |  | #               undef    - set missing tags to '' | 
| 3104 |  |  |  |  |  |  | #              'Error'   - issue minor error on missing tag (and return undef) | 
| 3105 |  |  |  |  |  |  | #              'Warn'    - issue minor warning on missing tag (and return undef) | 
| 3106 |  |  |  |  |  |  | #              'Silent'  - just return undef on missing tag (no errors/warnings) | 
| 3107 |  |  |  |  |  |  | #               Hash ref - defined to interpolate as variables in string instead of values | 
| 3108 |  |  |  |  |  |  | #                          --> receives tag/value pairs for interpolation of the variables | 
| 3109 |  |  |  |  |  |  | #         4) document group name if extracting from a specific document | 
| 3110 |  |  |  |  |  |  | #         5) hash ref to cache tag keys for subsequent calls in document loop | 
| 3111 |  |  |  |  |  |  | # Returns: string with embedded tag values (or '$info{TAGNAME}' entries with Hash ref option) | 
| 3112 |  |  |  |  |  |  | # Notes: | 
| 3113 |  |  |  |  |  |  | # - tag names are not case sensitive and may end with '#' for ValueConv value | 
| 3114 |  |  |  |  |  |  | # - uses MissingTagValue option if set | 
| 3115 |  |  |  |  |  |  | # - '$GROUP:all' evaluates to 1 if any tag from GROUP exists, or 0 otherwise | 
| 3116 |  |  |  |  |  |  | # - advanced feature allows Perl expressions inside braces (eg. '${model;tr/ //d}') | 
| 3117 |  |  |  |  |  |  | # - an error/warning in an advanced expression ("${TAG;EXPR}") generates an error | 
| 3118 |  |  |  |  |  |  | #   if option set to 'Error', or a warning otherwise | 
| 3119 |  |  |  |  |  |  | sub InsertTagValues($$$;$$$) | 
| 3120 |  |  |  |  |  |  | { | 
| 3121 | 9 |  |  | 9 | 0 | 43 | local $_; | 
| 3122 | 9 |  |  |  |  | 51 | my ($self, $foundTags, $line, $opt, $docGrp, $cache) = @_; | 
| 3123 | 9 |  |  |  |  | 33 | my $rtnStr = ''; | 
| 3124 | 9 |  |  |  |  | 37 | my ($docNum, $tag); | 
| 3125 | 9 | 50 |  |  |  | 42 | if ($docGrp) { | 
| 3126 | 0 | 0 |  |  |  | 0 | $docNum = $docGrp =~ /(\d+)$/ ? $1 : 0; | 
| 3127 |  |  |  |  |  |  | } else { | 
| 3128 | 9 |  |  |  |  | 28 | undef $cache;   # no cache if no document groups | 
| 3129 |  |  |  |  |  |  | } | 
| 3130 | 9 |  |  |  |  | 123 | while ($line =~ s/(.*?)\$(\{\s*)?([-\w]*\w|\$|\/)//s) { | 
| 3131 | 13 |  |  |  |  | 78 | my ($pre, $bra, $var) = ($1, $2, $3); | 
| 3132 | 13 |  |  |  |  | 40 | my (@tags, $val, $tg, @val, $type, $expr, $didExpr, $level, $asList); | 
| 3133 |  |  |  |  |  |  | # "$$" represents a "$" symbol, and "$/" is a newline | 
| 3134 | 13 | 50 | 33 |  |  | 103 | if ($var eq '$' or $var eq '/') { | 
| 3135 | 0 | 0 |  |  |  | 0 | $line =~ s/^\s*\}// if $bra; | 
| 3136 | 0 | 0 | 0 |  |  | 0 | if ($var eq '/') { | 
|  |  | 0 |  |  |  |  |  | 
| 3137 | 0 |  |  |  |  | 0 | $var = "\n"; | 
| 3138 |  |  |  |  |  |  | } elsif ($line =~ /^self\b/ and not $rtnStr =~ /\$$/) { | 
| 3139 | 0 |  |  |  |  | 0 | $var = '$$';    # ("$$self{var}" in string) | 
| 3140 |  |  |  |  |  |  | } | 
| 3141 | 0 |  |  |  |  | 0 | $rtnStr .= "$pre$var"; | 
| 3142 | 0 |  |  |  |  | 0 | next; | 
| 3143 |  |  |  |  |  |  | } | 
| 3144 |  |  |  |  |  |  | # allow multiple group names | 
| 3145 | 13 |  |  |  |  | 78 | while ($line =~ /^:([-\w]*\w)(.*)/s) { | 
| 3146 | 7 |  |  |  |  | 25 | my $group = $var; | 
| 3147 | 7 |  |  |  |  | 22 | ($var, $line) = ($1, $2); | 
| 3148 | 7 |  |  |  |  | 30 | $var = "$group:$var"; | 
| 3149 |  |  |  |  |  |  | } | 
| 3150 |  |  |  |  |  |  | # allow trailing '#' to indicate ValueConv value | 
| 3151 | 13 | 50 |  |  |  | 51 | $type = 'ValueConv' if $line =~ s/^#//; | 
| 3152 |  |  |  |  |  |  | # special advanced formatting '@' feature to evaluate list values separately | 
| 3153 | 13 | 100 | 100 |  |  | 85 | if ($bra and $line =~ s/^\@(#)?//) { | 
| 3154 | 1 |  |  |  |  | 2 | $asList = 1; | 
| 3155 | 1 | 50 |  |  |  | 9 | $type = 'ValueConv' if $1; | 
| 3156 |  |  |  |  |  |  | } | 
| 3157 |  |  |  |  |  |  | # remove trailing bracket if there was a leading one | 
| 3158 |  |  |  |  |  |  | # and extract Perl expression from inside brackets if it exists | 
| 3159 | 13 | 100 | 100 |  |  | 133 | if ($bra and $line !~ s/^\s*\}// and $line =~ s/^\s*;\s*(.*?)\s*\}//s) { | 
|  |  |  | 66 |  |  |  |  | 
| 3160 | 3 |  |  |  |  | 16 | my $part = $1; | 
| 3161 | 3 |  |  |  |  | 11 | $expr = ''; | 
| 3162 | 3 |  |  |  |  | 14 | for ($level=0; ; --$level) { | 
| 3163 |  |  |  |  |  |  | # increase nesting level for each opening brace | 
| 3164 | 7 |  |  |  |  | 34 | ++$level while $part =~ /\{/g; | 
| 3165 | 7 |  |  |  |  | 19 | $expr .= $part; | 
| 3166 | 7 | 100 | 66 |  |  | 48 | last unless $level and $line =~ s/^(.*?)\s*\}//s; # get next part | 
| 3167 | 4 |  |  |  |  | 14 | $part = $1; | 
| 3168 | 4 |  |  |  |  | 7 | $expr .= '}';  # this brace was part of the expression | 
| 3169 |  |  |  |  |  |  | } | 
| 3170 |  |  |  |  |  |  | # use default Windows filename filter if expression is empty | 
| 3171 | 3 | 50 |  |  |  | 19 | $expr = 'tr(/\\\\?*:|"<>\\0)()d' unless length $expr; | 
| 3172 |  |  |  |  |  |  | } | 
| 3173 | 13 |  |  |  |  | 60 | push @tags, $var; | 
| 3174 | 13 |  |  |  |  | 80 | ExpandShortcuts(\@tags); | 
| 3175 | 13 | 50 |  |  |  | 80 | @tags or $rtnStr .= $pre, next; | 
| 3176 |  |  |  |  |  |  | # save advanced formatting expression to allow access by user-defined ValueConv | 
| 3177 | 13 |  |  |  |  | 57 | $$self{FMT_EXPR} = $expr; | 
| 3178 |  |  |  |  |  |  |  | 
| 3179 | 13 |  |  |  |  | 29 | for (;;) { | 
| 3180 |  |  |  |  |  |  | # temporarily reset ListJoin option if evaluating list values separately | 
| 3181 | 13 |  |  |  |  | 32 | my $oldListJoin; | 
| 3182 | 13 | 100 |  |  |  | 55 | $oldListJoin = $self->Options(ListJoin => undef) if $asList; | 
| 3183 | 13 |  |  |  |  | 34 | $tag = shift @tags; | 
| 3184 | 13 |  |  |  |  | 43 | my $lcTag = lc $tag; | 
| 3185 | 13 | 50 | 33 |  |  | 65 | if ($cache and $lcTag !~ /(^|:)all$/) { | 
| 3186 |  |  |  |  |  |  | # remove group from tag name (but not lower-case version) | 
| 3187 | 0 |  |  |  |  | 0 | my $group; | 
| 3188 | 0 | 0 |  |  |  | 0 | $tag =~ s/^(.*):// and $group = $1; | 
| 3189 |  |  |  |  |  |  | # cache tag keys to speed processing for a large number of sub-documents | 
| 3190 |  |  |  |  |  |  | # (similar to code in BuildCompositeTags(), but this is case-insensitive) | 
| 3191 | 0 |  |  |  |  | 0 | my $cacheTag = $$cache{$lcTag}; | 
| 3192 | 0 | 0 |  |  |  | 0 | unless ($cacheTag) { | 
| 3193 | 0 |  |  |  |  | 0 | $cacheTag = $$cache{$lcTag} = [ ]; | 
| 3194 |  |  |  |  |  |  | # find all matching keys, organize into groups, and store in cache | 
| 3195 | 0 |  |  |  |  | 0 | my $ex = $$self{TAG_EXTRA}; | 
| 3196 | 0 |  |  |  |  | 0 | my @matches = grep /^$tag(\s|$)/i, @$foundTags; | 
| 3197 | 0 | 0 |  |  |  | 0 | @matches = $self->GroupMatches($group, \@matches) if defined $group; | 
| 3198 | 0 |  |  |  |  | 0 | foreach (@matches) { | 
| 3199 | 0 | 0 | 0 |  |  | 0 | my $doc = $$ex{$_} ? $$ex{$_}{G3} || 0 : 0; | 
| 3200 | 0 | 0 |  |  |  | 0 | if (defined $$cacheTag[$doc]) { | 
| 3201 | 0 | 0 |  |  |  | 0 | next unless $$cacheTag[$doc] =~ / \((\d+)\)$/; | 
| 3202 | 0 |  |  |  |  | 0 | my $cur = $1; | 
| 3203 |  |  |  |  |  |  | # keep the most recently extracted tag | 
| 3204 | 0 | 0 | 0 |  |  | 0 | next if / \((\d+)\)$/ and $1 < $cur; | 
| 3205 |  |  |  |  |  |  | } | 
| 3206 | 0 |  |  |  |  | 0 | $$cacheTag[$doc] = $_; | 
| 3207 |  |  |  |  |  |  | } | 
| 3208 |  |  |  |  |  |  | } | 
| 3209 | 0 | 0 | 0 |  |  | 0 | my $doc = $lcTag =~ /\b(main|doc(\d+)):/ ? ($2 || 0) : $docNum; | 
| 3210 | 0 | 0 |  |  |  | 0 | if ($$cacheTag[$doc]) { | 
| 3211 | 0 |  |  |  |  | 0 | $tag = $$cacheTag[$doc]; | 
| 3212 | 0 |  |  |  |  | 0 | $val = $self->GetValue($tag, $type); | 
| 3213 |  |  |  |  |  |  | } | 
| 3214 |  |  |  |  |  |  | } else { | 
| 3215 |  |  |  |  |  |  | # add document number to tag if specified and it doesn't already exist | 
| 3216 | 13 | 50 | 33 |  |  | 60 | if ($docGrp and $lcTag !~ /\b(main|doc\d+):/) { | 
| 3217 | 0 |  |  |  |  | 0 | $tag = $docGrp . ':' . $tag; | 
| 3218 | 0 |  |  |  |  | 0 | $lcTag = lc $tag; | 
| 3219 |  |  |  |  |  |  | } | 
| 3220 | 13 |  |  |  |  | 34 | my $et = $self; | 
| 3221 | 13 | 100 |  |  |  | 83 | if ($tag =~ s/(\bfile\d+)://i) { | 
| 3222 | 3 | 50 |  |  |  | 28 | $et = $$self{ALT_EXIFTOOL}{ucfirst lc $1} or $et=$self, $tag = 'no_alt_file'; | 
| 3223 |  |  |  |  |  |  | } | 
| 3224 | 13 | 50 |  |  |  | 145 | if ($lcTag eq 'all') { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 3225 | 0 |  |  |  |  | 0 | $val = 1;   # always some tag available | 
| 3226 |  |  |  |  |  |  | } elsif (defined $$et{OPTIONS}{UserParam}{$lcTag}) { | 
| 3227 | 0 |  |  |  |  | 0 | $val = $$et{OPTIONS}{UserParam}{$lcTag}; | 
| 3228 |  |  |  |  |  |  | } elsif ($tag =~ /(.*):(.+)/) { | 
| 3229 | 3 |  |  |  |  | 10 | my $group; | 
| 3230 | 3 |  |  |  |  | 13 | ($group, $tag) = ($1, $2); | 
| 3231 | 3 | 50 |  |  |  | 17 | if (lc $tag eq 'all') { | 
| 3232 |  |  |  |  |  |  | # see if any tag from the specified group exists | 
| 3233 | 0 |  |  |  |  | 0 | my $match = $et->GroupMatches($group, $foundTags); | 
| 3234 | 0 | 0 |  |  |  | 0 | $val = $match ? 1 : 0; | 
| 3235 |  |  |  |  |  |  | } else { | 
| 3236 |  |  |  |  |  |  | # find the specified tag | 
| 3237 | 3 |  |  |  |  | 698 | my @matches = grep /^$tag(\s|$)/i, @$foundTags; | 
| 3238 | 3 |  |  |  |  | 23 | @matches = $et->GroupMatches($group, \@matches); | 
| 3239 | 3 |  |  |  |  | 20 | foreach $tg (@matches) { | 
| 3240 | 3 | 50 | 33 |  |  | 17 | if (defined $val and $tg =~ / \((\d+)\)$/) { | 
| 3241 |  |  |  |  |  |  | # take the most recently extracted tag | 
| 3242 | 0 |  |  |  |  | 0 | my $tagNum = $1; | 
| 3243 | 0 | 0 | 0 |  |  | 0 | next if $tag !~ / \((\d+)\)$/ or $1 > $tagNum; | 
| 3244 |  |  |  |  |  |  | } | 
| 3245 | 3 |  |  |  |  | 15 | $val = $et->GetValue($tg, $type); | 
| 3246 | 3 |  |  |  |  | 11 | $tag = $tg; | 
| 3247 | 3 | 100 |  |  |  | 24 | last unless $tag =~ / /;    # all done if we got our best match | 
| 3248 |  |  |  |  |  |  | } | 
| 3249 |  |  |  |  |  |  | } | 
| 3250 |  |  |  |  |  |  | } elsif ($tag eq 'self') { | 
| 3251 | 0 |  |  |  |  | 0 | $val = $et; # ("$self{var}" or "$file1:self{var}" in string) | 
| 3252 |  |  |  |  |  |  | } else { | 
| 3253 |  |  |  |  |  |  | # get the tag value | 
| 3254 | 10 |  |  |  |  | 52 | $val = $et->GetValue($tag, $type); | 
| 3255 | 10 | 100 |  |  |  | 63 | unless (defined $val) { | 
| 3256 |  |  |  |  |  |  | # check for tag name with different case | 
| 3257 | 7 |  |  |  |  | 763 | ($tg) = grep /^$tag$/i, @$foundTags; | 
| 3258 | 7 | 50 |  |  |  | 52 | if (defined $tg) { | 
| 3259 | 7 |  |  |  |  | 32 | $val = $et->GetValue($tg, $type); | 
| 3260 | 7 |  |  |  |  | 33 | $tag = $tg; | 
| 3261 |  |  |  |  |  |  | } | 
| 3262 |  |  |  |  |  |  | } | 
| 3263 |  |  |  |  |  |  | } | 
| 3264 |  |  |  |  |  |  | } | 
| 3265 | 13 | 100 |  |  |  | 81 | $self->Options(ListJoin => $oldListJoin) if $asList; | 
| 3266 | 13 | 100 |  |  |  | 108 | if (ref $val eq 'ARRAY') { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 3267 | 1 |  |  |  |  | 6 | push @val, @$val; | 
| 3268 | 1 |  |  |  |  | 3 | undef $val; | 
| 3269 | 1 | 50 |  |  |  | 7 | last unless @tags; | 
| 3270 |  |  |  |  |  |  | } elsif (ref $val eq 'SCALAR') { | 
| 3271 | 0 | 0 | 0 |  |  | 0 | if ($$self{OPTIONS}{Binary} or $$val =~ /^Binary data/) { | 
| 3272 | 0 |  |  |  |  | 0 | $val = $$val; | 
| 3273 |  |  |  |  |  |  | } else { | 
| 3274 | 0 |  |  |  |  | 0 | $val = 'Binary data ' . length($$val) . ' bytes'; | 
| 3275 |  |  |  |  |  |  | } | 
| 3276 |  |  |  |  |  |  | } elsif (ref $val eq 'HASH') { | 
| 3277 | 0 |  |  |  |  | 0 | require 'Image/ExifTool/XMPStruct.pl'; | 
| 3278 | 0 |  |  |  |  | 0 | $val = Image::ExifTool::XMP::SerializeStruct($val); | 
| 3279 |  |  |  |  |  |  | } elsif (not defined $val) { | 
| 3280 | 0 | 0 |  |  |  | 0 | $val = $$self{OPTIONS}{MissingTagValue} if $asList; | 
| 3281 |  |  |  |  |  |  | } | 
| 3282 | 12 | 50 |  |  |  | 51 | last unless @tags; | 
| 3283 | 0 | 0 |  |  |  | 0 | push @val, $val if defined $val; | 
| 3284 | 0 |  |  |  |  | 0 | undef $val; | 
| 3285 |  |  |  |  |  |  | } | 
| 3286 | 13 | 100 |  |  |  | 46 | if (@val) { | 
| 3287 | 1 | 50 |  |  |  | 5 | push @val, $val if defined $val; | 
| 3288 | 1 |  |  |  |  | 8 | $val = join $$self{OPTIONS}{ListSep}, @val; | 
| 3289 |  |  |  |  |  |  | } else { | 
| 3290 | 12 | 50 |  |  |  | 53 | push @val, $val if defined $val; # (so the eval has access to @val if required) | 
| 3291 |  |  |  |  |  |  | } | 
| 3292 |  |  |  |  |  |  | # evaluate advanced formatting expression if given (eg. "${TAG;EXPR}") | 
| 3293 | 13 | 100 | 66 |  |  | 92 | if (defined $expr and defined $val) { | 
| 3294 | 3 |  |  |  |  | 22 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 3295 | 3 |  |  |  |  | 10 | undef $evalWarning; | 
| 3296 | 3 |  |  |  |  | 10 | $advFmtSelf = $self; | 
| 3297 | 3 | 100 |  |  |  | 14 | if ($asList) { | 
| 3298 | 1 |  |  |  |  | 4 | foreach (@val) { | 
| 3299 |  |  |  |  |  |  | #### eval advanced formatting expression ($_, $self, @val, $advFmtSelf) | 
| 3300 | 3 |  |  |  |  | 279 | eval $expr; | 
| 3301 | 3 | 50 |  |  |  | 19 | $@ and $evalWarning = $@; | 
| 3302 |  |  |  |  |  |  | } | 
| 3303 |  |  |  |  |  |  | # join back together if any values are still defined | 
| 3304 | 1 |  |  |  |  | 8 | @val = grep defined, @val; | 
| 3305 | 1 | 50 |  |  |  | 9 | $val = @val ? join $$self{OPTIONS}{ListSep}, @val : undef; | 
| 3306 |  |  |  |  |  |  | } else { | 
| 3307 | 2 |  |  |  |  | 8 | $_ = $val; | 
| 3308 |  |  |  |  |  |  | #### eval advanced formatting expression ($_, $self, @val, $advFmtSelf) | 
| 3309 | 2 |  |  |  |  | 179 | eval $expr; | 
| 3310 | 2 | 50 |  |  |  | 17 | $@ and $evalWarning = $@; | 
| 3311 | 2 | 50 |  |  |  | 14 | $val = ref $_ eq 'ARRAY' ? join($$self{OPTIONS}{ListSep}, @$_): $_; | 
| 3312 |  |  |  |  |  |  | } | 
| 3313 | 3 | 50 |  |  |  | 19 | if ($evalWarning) { | 
| 3314 | 0 | 0 | 0 |  |  | 0 | my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : ''; | 
| 3315 | 0 |  |  |  |  | 0 | my $str = CleanWarning() . " for '$g3${var}'"; | 
| 3316 | 0 | 0 |  |  |  | 0 | if ($opt) { | 
| 3317 | 0 | 0 |  |  |  | 0 | if ($opt eq 'Error') { | 
|  |  | 0 |  |  |  |  |  | 
| 3318 | 0 |  |  |  |  | 0 | $self->Error($str); | 
| 3319 |  |  |  |  |  |  | } elsif ($opt ne 'Silent') { | 
| 3320 | 0 |  |  |  |  | 0 | $self->Warn($str); | 
| 3321 |  |  |  |  |  |  | } | 
| 3322 |  |  |  |  |  |  | } | 
| 3323 |  |  |  |  |  |  | } | 
| 3324 | 3 |  |  |  |  | 9 | undef $advFmtSelf; | 
| 3325 | 3 |  |  |  |  | 16 | $didExpr = 1;   # set flag indicating an expression was evaluated | 
| 3326 |  |  |  |  |  |  | } | 
| 3327 | 13 | 50 |  |  |  | 57 | unless (defined $val) { | 
| 3328 | 0 |  |  |  |  | 0 | $val = $$self{OPTIONS}{MissingTagValue}; | 
| 3329 | 0 | 0 |  |  |  | 0 | unless (defined $val) { | 
| 3330 | 0 | 0 | 0 |  |  | 0 | my $g3 = ($docGrp and $var !~ /\b(main|doc\d+):/i) ? $docGrp . ':' : ''; | 
| 3331 | 0 | 0 |  |  |  | 0 | my $msg = $didExpr ? "Advanced formatting expression returned undef for '$g3${var}'" : | 
| 3332 |  |  |  |  |  |  | "Tag '$g3${var}' not defined"; | 
| 3333 | 0 | 0 |  |  |  | 0 | if (ref $opt) { | 
|  |  | 0 |  |  |  |  |  | 
| 3334 | 0 | 0 |  |  |  | 0 | $self->Warn($msg,2) or $val = ''; | 
| 3335 |  |  |  |  |  |  | } elsif ($opt) { | 
| 3336 | 59 |  |  | 59 |  | 609 | no strict 'refs'; | 
|  | 59 |  |  |  |  | 194 |  | 
|  | 59 |  |  |  |  | 42906 |  | 
| 3337 | 0 | 0 | 0 |  |  | 0 | ($opt eq 'Silent' or &$opt($self, $msg, 2)) and return $$self{FMT_EXPR} = undef; | 
| 3338 | 0 |  |  |  |  | 0 | $val = ''; | 
| 3339 |  |  |  |  |  |  | } | 
| 3340 |  |  |  |  |  |  | } | 
| 3341 |  |  |  |  |  |  | } | 
| 3342 | 13 | 50 |  |  |  | 50 | if (ref $opt eq 'HASH') { | 
| 3343 | 0 | 0 |  |  |  | 0 | $var .= '#' if $type; | 
| 3344 | 0 | 0 |  |  |  | 0 | if (defined $expr) { | 
| 3345 |  |  |  |  |  |  | # generate unique variable name for this modified tag value | 
| 3346 | 0 |  |  |  |  | 0 | my $i = 1; | 
| 3347 | 0 |  |  |  |  | 0 | ++$i while exists $$opt{"$var.expr$i"}; | 
| 3348 | 0 |  |  |  |  | 0 | $var .= '.expr' . $i; | 
| 3349 |  |  |  |  |  |  | } | 
| 3350 | 0 |  |  |  |  | 0 | $rtnStr .= "$pre\$info{'${var}'}"; | 
| 3351 | 0 |  |  |  |  | 0 | $$opt{$var} = $val; | 
| 3352 |  |  |  |  |  |  | } else { | 
| 3353 | 13 |  |  |  |  | 97 | $rtnStr .= "$pre$val"; | 
| 3354 |  |  |  |  |  |  | } | 
| 3355 |  |  |  |  |  |  | } | 
| 3356 | 9 |  |  |  |  | 42 | $$self{FMT_EXPR} = undef; | 
| 3357 | 9 |  |  |  |  | 47 | return $rtnStr . $line; | 
| 3358 |  |  |  |  |  |  | } | 
| 3359 |  |  |  |  |  |  |  | 
| 3360 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3361 |  |  |  |  |  |  | # Reformat date/time value in $_ based on specified format string | 
| 3362 |  |  |  |  |  |  | # Inputs: 0) date/time format string | 
| 3363 |  |  |  |  |  |  | sub DateFmt($) | 
| 3364 |  |  |  |  |  |  | { | 
| 3365 | 0 |  |  | 0 | 0 | 0 | my $et = bless { OPTIONS => { DateFormat => shift, StrictDate => 1 } }; | 
| 3366 | 0 |  |  |  |  | 0 | my $shift; | 
| 3367 | 0 | 0 | 0 |  |  | 0 | if ($advFmtSelf and defined($shift = $$advFmtSelf{OPTIONS}{GlobalTimeShift})) { | 
| 3368 | 0 |  |  |  |  | 0 | $$et{OPTIONS}{GlobalTimeShift} = $shift; | 
| 3369 | 0 |  |  |  |  | 0 | $$et{GLOBAL_TIME_OFFSET} = $$advFmtSelf{GLOBAL_TIME_OFFSET}; | 
| 3370 |  |  |  |  |  |  | } | 
| 3371 | 0 |  |  |  |  | 0 | $_ = $et->ConvertDateTime($_); | 
| 3372 | 0 | 0 |  |  |  | 0 | defined $_ or warn "Error converting date/time\n"; | 
| 3373 | 0 | 0 |  |  |  | 0 | $$advFmtSelf{GLOBAL_TIME_OFFSET} = $$et{GLOBAL_TIME_OFFSET} if $shift; | 
| 3374 |  |  |  |  |  |  | } | 
| 3375 |  |  |  |  |  |  |  | 
| 3376 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3377 |  |  |  |  |  |  | # Utility routine to remove duplicate items from default input string | 
| 3378 |  |  |  |  |  |  | # Inputs: 0) true to set $_ to undef if not changed | 
| 3379 |  |  |  |  |  |  | # Notes: - for use only in advanced formatting expressions | 
| 3380 |  |  |  |  |  |  | sub NoDups | 
| 3381 |  |  |  |  |  |  | { | 
| 3382 | 0 |  |  | 0 | 0 | 0 | my %seen; | 
| 3383 | 0 | 0 |  |  |  | 0 | my $sep = $advFmtSelf ? $$advFmtSelf{OPTIONS}{ListSep} : ', '; | 
| 3384 | 0 |  |  |  |  | 0 | my $new = join $sep, grep { !$seen{$_}++ } split /\Q$sep\E/, $_; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3385 | 0 | 0 | 0 |  |  | 0 | $_ = ($_[0] and $new eq $_) ? undef : $new; | 
| 3386 |  |  |  |  |  |  | } | 
| 3387 |  |  |  |  |  |  |  | 
| 3388 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3389 |  |  |  |  |  |  | # Is specified tag writable | 
| 3390 |  |  |  |  |  |  | # Inputs: 0) tag name, case insensitive (optional group name currently ignored) | 
| 3391 |  |  |  |  |  |  | # Returns: 0=exists but not writable, 1=writable, undef=doesn't exist | 
| 3392 |  |  |  |  |  |  | sub IsWritable($) | 
| 3393 |  |  |  |  |  |  | { | 
| 3394 | 0 |  |  | 0 | 0 | 0 | my $tag = shift; | 
| 3395 | 0 |  |  |  |  | 0 | $tag =~ s/^(.*)://; # ignore group name | 
| 3396 | 0 |  |  |  |  | 0 | my @tagInfo = FindTagInfo($tag); | 
| 3397 | 0 | 0 |  |  |  | 0 | unless (@tagInfo) { | 
| 3398 | 0 | 0 |  |  |  | 0 | return 0 if TagExists($tag); | 
| 3399 | 0 |  |  |  |  | 0 | return undef; | 
| 3400 |  |  |  |  |  |  | } | 
| 3401 | 0 |  |  |  |  | 0 | my $tagInfo; | 
| 3402 | 0 |  |  |  |  | 0 | foreach $tagInfo (@tagInfo) { | 
| 3403 | 0 | 0 |  |  |  | 0 | return $$tagInfo{Writable} ? 1 : 0 if defined $$tagInfo{Writable}; | 
|  |  | 0 |  |  |  |  |  | 
| 3404 | 0 | 0 |  |  |  | 0 | return 1 if $$tagInfo{Table}{WRITABLE}; | 
| 3405 |  |  |  |  |  |  | # must call WRITE_PROC to autoload writer because this may set the writable tag | 
| 3406 | 0 |  |  |  |  | 0 | my $writeProc = $$tagInfo{Table}{WRITE_PROC}; | 
| 3407 | 0 | 0 |  |  |  | 0 | if ($writeProc) { | 
| 3408 | 59 |  |  | 59 |  | 614 | no strict 'refs'; | 
|  | 59 |  |  |  |  | 213 |  | 
|  | 59 |  |  |  |  | 16813 |  | 
| 3409 | 0 |  |  |  |  | 0 | &$writeProc();  # dummy call to autoload writer | 
| 3410 | 0 | 0 |  |  |  | 0 | return 1 if $$tagInfo{Writable}; | 
| 3411 |  |  |  |  |  |  | } | 
| 3412 |  |  |  |  |  |  | } | 
| 3413 | 0 |  |  |  |  | 0 | return 0; | 
| 3414 |  |  |  |  |  |  | } | 
| 3415 |  |  |  |  |  |  |  | 
| 3416 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3417 |  |  |  |  |  |  | # Check to see if these are the same file | 
| 3418 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) first file name, 2) second file name | 
| 3419 |  |  |  |  |  |  | # Returns: true if file names reference the same file | 
| 3420 |  |  |  |  |  |  | sub IsSameFile($$$) | 
| 3421 |  |  |  |  |  |  | { | 
| 3422 | 0 |  |  | 0 | 0 | 0 | my ($self, $file, $file2) = @_; | 
| 3423 | 0 | 0 |  |  |  | 0 | return 0 unless lc $file eq lc $file2;  # (only looking for differences in case) | 
| 3424 | 0 |  |  |  |  | 0 | my ($isSame, $interrupted); | 
| 3425 | 0 |  |  |  |  | 0 | my $tmp1 = "${file}_ExifTool_tmp_$$"; | 
| 3426 | 0 |  |  |  |  | 0 | my $tmp2 = "${file2}_ExifTool_tmp_$$"; | 
| 3427 |  |  |  |  |  |  | { | 
| 3428 | 0 |  |  |  |  | 0 | local *TMP1; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3429 | 0 |  |  | 0 |  | 0 | local $SIG{INT} = sub { $interrupted = 1 }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3430 | 0 | 0 |  |  |  | 0 | if ($self->Open(\*TMP1, $tmp1, '>')) { | 
| 3431 | 0 |  |  |  |  | 0 | close TMP1; | 
| 3432 | 0 | 0 |  |  |  | 0 | $isSame = 1 if $self->Exists($tmp2); | 
| 3433 | 0 |  |  |  |  | 0 | $self->Unlink($tmp1); | 
| 3434 |  |  |  |  |  |  | } | 
| 3435 |  |  |  |  |  |  | } | 
| 3436 | 0 | 0 | 0 |  |  | 0 | if ($interrupted and $SIG{INT}) { | 
| 3437 | 59 |  |  | 59 |  | 562 | no strict 'refs'; | 
|  | 59 |  |  |  |  | 182 |  | 
|  | 59 |  |  |  |  | 149220 |  | 
| 3438 | 0 |  |  |  |  | 0 | &{$SIG{INT}}(); | 
|  | 0 |  |  |  |  | 0 |  | 
| 3439 |  |  |  |  |  |  | } | 
| 3440 | 0 |  |  |  |  | 0 | return $isSame; | 
| 3441 |  |  |  |  |  |  | } | 
| 3442 |  |  |  |  |  |  |  | 
| 3443 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3444 |  |  |  |  |  |  | # Is this a raw file type? | 
| 3445 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref | 
| 3446 |  |  |  |  |  |  | # Returns: true if FileType is a type of RAW image | 
| 3447 |  |  |  |  |  |  | sub IsRawType($) | 
| 3448 |  |  |  |  |  |  | { | 
| 3449 | 12 |  |  | 12 | 0 | 38 | my $self = shift; | 
| 3450 | 12 |  |  |  |  | 123 | return $rawType{$$self{FileType}}; | 
| 3451 |  |  |  |  |  |  | } | 
| 3452 |  |  |  |  |  |  |  | 
| 3453 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3454 |  |  |  |  |  |  | # Create directory for specified file | 
| 3455 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) complete file name including path | 
| 3456 |  |  |  |  |  |  | # Returns: 1 = directory created, 0 = nothing done, -1 = error | 
| 3457 |  |  |  |  |  |  | my $k32CreateDir; | 
| 3458 |  |  |  |  |  |  | sub CreateDirectory($$) | 
| 3459 |  |  |  |  |  |  | { | 
| 3460 | 1 |  |  | 1 | 0 | 4 | local $_; | 
| 3461 | 1 |  |  |  |  | 4 | my ($self, $file) = @_; | 
| 3462 | 1 |  |  |  |  | 2 | my $rtnVal = 0; | 
| 3463 | 1 |  |  |  |  | 3 | my $enc = $$self{OPTIONS}{CharsetFileName}; | 
| 3464 | 1 |  |  |  |  | 2 | my $dir; | 
| 3465 | 1 |  |  |  |  | 12 | ($dir = $file) =~ s/[^\/]*$//;  # remove filename from path specification | 
| 3466 |  |  |  |  |  |  | # recode as UTF-8 if necessary | 
| 3467 | 1 | 50 | 33 |  |  | 10 | if ($dir and not $self->IsDirectory($dir)) { | 
| 3468 | 0 |  |  |  |  | 0 | my @parts = split /\//, $dir; | 
| 3469 | 0 |  |  |  |  | 0 | $dir = ''; | 
| 3470 | 0 |  |  |  |  | 0 | foreach (@parts) { | 
| 3471 | 0 |  |  |  |  | 0 | $dir .= $_; | 
| 3472 | 0 | 0 | 0 |  |  | 0 | if (length $dir and not $self->IsDirectory($dir)) { | 
| 3473 |  |  |  |  |  |  | # create directory since it doesn't exist | 
| 3474 | 0 |  |  |  |  | 0 | my $d2 = $dir; # (must make a copy in case EncodeFileName recodes it) | 
| 3475 | 0 | 0 |  |  |  | 0 | if ($self->EncodeFileName($d2)) { | 
| 3476 |  |  |  |  |  |  | # handle Windows Unicode directory names | 
| 3477 | 0 | 0 |  |  |  | 0 | unless (eval { require Win32::API }) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 3478 | 0 |  |  |  |  | 0 | $self->Warn('Install Win32::API to create directories with Unicode names'); | 
| 3479 | 0 |  |  |  |  | 0 | return -1; | 
| 3480 |  |  |  |  |  |  | } | 
| 3481 | 0 | 0 |  |  |  | 0 | unless ($k32CreateDir) { | 
| 3482 | 0 | 0 |  |  |  | 0 | return -1 if defined $k32CreateDir; | 
| 3483 | 0 |  |  |  |  | 0 | $k32CreateDir = new Win32::API('KERNEL32', 'CreateDirectoryW', 'PP', 'I'); | 
| 3484 | 0 | 0 |  |  |  | 0 | unless ($k32CreateDir) { | 
| 3485 | 0 |  |  |  |  | 0 | $self->Warn('Error calling Win32::API::CreateDirectoryW'); | 
| 3486 | 0 |  |  |  |  | 0 | $k32CreateDir = 0; | 
| 3487 | 0 |  |  |  |  | 0 | return -1; | 
| 3488 |  |  |  |  |  |  | } | 
| 3489 |  |  |  |  |  |  | } | 
| 3490 | 0 | 0 |  |  |  | 0 | $k32CreateDir->Call($d2, 0) or return -1; | 
| 3491 |  |  |  |  |  |  | } else { | 
| 3492 | 0 | 0 |  |  |  | 0 | mkdir($d2, 0777) or return -1; | 
| 3493 |  |  |  |  |  |  | } | 
| 3494 | 0 |  |  |  |  | 0 | $rtnVal = 1; | 
| 3495 |  |  |  |  |  |  | } | 
| 3496 | 0 |  |  |  |  | 0 | $dir .= '/'; | 
| 3497 |  |  |  |  |  |  | } | 
| 3498 |  |  |  |  |  |  | } | 
| 3499 | 1 |  |  |  |  | 7 | return $rtnVal; | 
| 3500 |  |  |  |  |  |  | } | 
| 3501 |  |  |  |  |  |  |  | 
| 3502 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3503 |  |  |  |  |  |  | # Copy file attributes from one file to another | 
| 3504 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) source file name, 2) destination file name | 
| 3505 |  |  |  |  |  |  | # Notes: eventually add support for extended attributes? | 
| 3506 |  |  |  |  |  |  | sub CopyFileAttrs($$$) | 
| 3507 |  |  |  |  |  |  | { | 
| 3508 | 2 |  |  | 2 | 0 | 12 | my ($self, $src, $dst) = @_; | 
| 3509 | 2 |  |  |  |  | 58 | my ($mode, $uid, $gid) = (stat($src))[2, 4, 5]; | 
| 3510 |  |  |  |  |  |  | # copy file attributes unless we already set them | 
| 3511 | 2 | 50 | 33 |  |  | 25 | if (defined $mode and not defined $self->GetNewValue('FilePermissions')) { | 
| 3512 | 2 |  |  |  |  | 6 | eval { chmod($mode & 07777, $dst) }; | 
|  | 2 |  |  |  |  | 61 |  | 
| 3513 |  |  |  |  |  |  | } | 
| 3514 | 2 |  |  |  |  | 17 | my $newUid = $self->GetNewValue('FileUserID'); | 
| 3515 | 2 |  |  |  |  | 9 | my $newGid = $self->GetNewValue('FileGroupID'); | 
| 3516 | 2 | 50 | 33 |  |  | 41 | if (defined $uid and defined $gid and (not defined $newUid or not defined $newGid)) { | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 3517 | 2 | 50 |  |  |  | 12 | defined $newGid and $gid = $newGid; | 
| 3518 | 2 | 50 |  |  |  | 9 | defined $newUid and $uid = $newUid; | 
| 3519 | 2 |  |  |  |  | 8 | eval { chown($uid, $gid, $dst) }; | 
|  | 2 |  |  |  |  | 53 |  | 
| 3520 |  |  |  |  |  |  | } | 
| 3521 |  |  |  |  |  |  | } | 
| 3522 |  |  |  |  |  |  |  | 
| 3523 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3524 |  |  |  |  |  |  | # Get new file path name | 
| 3525 |  |  |  |  |  |  | # Inputs: 0) existing name (may contain directory), | 
| 3526 |  |  |  |  |  |  | #         1) new file name, new directory, or new path (dir+name) | 
| 3527 |  |  |  |  |  |  | # Returns: new file path name | 
| 3528 |  |  |  |  |  |  | sub GetNewFileName($$) | 
| 3529 |  |  |  |  |  |  | { | 
| 3530 | 1 |  |  | 1 | 0 | 3 | my ($oldName, $newName) = @_; | 
| 3531 | 1 |  |  |  |  | 10 | my ($dir, $name) = ($oldName =~ m{(.*/)(.*)}); | 
| 3532 | 1 | 50 |  |  |  | 5 | ($dir, $name) = ('', $oldName) unless defined $dir; | 
| 3533 | 1 | 50 |  |  |  | 10 | if ($newName =~ m{/$}) { | 
|  |  | 50 |  |  |  |  |  | 
| 3534 | 0 |  |  |  |  | 0 | $newName = "$newName$name"; # change dir only | 
| 3535 |  |  |  |  |  |  | } elsif ($newName !~ m{/}) { | 
| 3536 | 1 |  |  |  |  | 6 | $newName = "$dir$newName";  # change name only if newname doesn't specify dir | 
| 3537 |  |  |  |  |  |  | }                               # else change dir and name | 
| 3538 | 1 |  |  |  |  | 5 | return $newName; | 
| 3539 |  |  |  |  |  |  | } | 
| 3540 |  |  |  |  |  |  |  | 
| 3541 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3542 |  |  |  |  |  |  | # Get next available tag key | 
| 3543 |  |  |  |  |  |  | # Inputs: 0) hash reference (keys are tag keys), 1) tag name | 
| 3544 |  |  |  |  |  |  | # Returns: next available tag key | 
| 3545 |  |  |  |  |  |  | sub NextFreeTagKey($$) | 
| 3546 |  |  |  |  |  |  | { | 
| 3547 | 0 |  |  | 0 | 0 | 0 | my ($info, $tag) = @_; | 
| 3548 | 0 | 0 |  |  |  | 0 | return $tag unless exists $$info{$tag}; | 
| 3549 | 0 |  |  |  |  | 0 | my $i; | 
| 3550 | 0 |  |  |  |  | 0 | for ($i=1; ; ++$i) { | 
| 3551 | 0 |  |  |  |  | 0 | my $key = "$tag ($i)"; | 
| 3552 | 0 | 0 |  |  |  | 0 | return $key unless exists $$info{$key}; | 
| 3553 |  |  |  |  |  |  | } | 
| 3554 |  |  |  |  |  |  | } | 
| 3555 |  |  |  |  |  |  |  | 
| 3556 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3557 |  |  |  |  |  |  | # Reverse hash lookup | 
| 3558 |  |  |  |  |  |  | # Inputs: 0) value, 1) hash reference | 
| 3559 |  |  |  |  |  |  | # Returns: Hash key or undef if not found (plus flag for multiple matches in list context) | 
| 3560 |  |  |  |  |  |  | sub ReverseLookup($$) | 
| 3561 |  |  |  |  |  |  | { | 
| 3562 | 8726 |  |  | 8726 | 0 | 19887 | my ($val, $conv) = @_; | 
| 3563 | 8726 | 100 |  |  |  | 18045 | return undef unless defined $val; | 
| 3564 | 8665 |  |  |  |  | 13154 | my $multi; | 
| 3565 | 8665 | 100 |  |  |  | 19224 | if ($val =~ /^Unknown\s*\((.*)\)$/i) { | 
| 3566 | 40 |  |  |  |  | 141 | $val = $1;    # was unknown | 
| 3567 | 40 | 50 |  |  |  | 119 | if ($val =~ /^0x([\da-fA-F]+)$/) { | 
| 3568 |  |  |  |  |  |  | # disable "Hexadecimal number > 0xffffffff non-portable" warning | 
| 3569 | 0 |  |  | 0 |  | 0 | local $SIG{'__WARN__'} = sub { }; | 
| 3570 | 0 |  |  |  |  | 0 | $val = hex($val);   # convert hex value | 
| 3571 |  |  |  |  |  |  | } | 
| 3572 |  |  |  |  |  |  | } else { | 
| 3573 | 8625 |  |  |  |  | 14071 | my $qval = $val; | 
| 3574 | 8625 |  |  |  |  | 20129 | $qval =~ s/\s+$//;      # remove trailing whitespace | 
| 3575 | 8625 |  |  |  |  | 15260 | $qval = quotemeta $qval; | 
| 3576 | 8625 |  |  |  |  | 35672 | my @patterns = ( | 
| 3577 |  |  |  |  |  |  | "^$qval\$",         # exact match | 
| 3578 |  |  |  |  |  |  | "^(?i)$qval\$",     # case-insensitive | 
| 3579 |  |  |  |  |  |  | "^(?i)$qval",       # beginning of string | 
| 3580 |  |  |  |  |  |  | "(?i)$qval",        # substring | 
| 3581 |  |  |  |  |  |  | ); | 
| 3582 |  |  |  |  |  |  | # hash entries to ignore in reverse lookup | 
| 3583 | 8625 |  |  |  |  | 14474 | my ($pattern, $found, $matches); | 
| 3584 | 8625 |  |  |  |  | 18204 | PAT:    foreach $pattern (@patterns) { | 
| 3585 | 22085 |  |  |  |  | 401122 | $matches = scalar grep /$pattern/, values(%$conv); | 
| 3586 | 22085 | 100 |  |  |  | 59902 | next unless $matches; | 
| 3587 |  |  |  |  |  |  | # multiple matches are bad unless they were exact | 
| 3588 | 6524 | 100 | 100 |  |  | 22768 | if ($matches > 1 and $pattern !~ /\$$/) { | 
| 3589 |  |  |  |  |  |  | # don't match entries that we should ignore | 
| 3590 | 3212 |  |  |  |  | 9067 | foreach (keys %ignorePrintConv) { | 
| 3591 | 9636 | 100 | 100 |  |  | 24646 | --$matches if defined $$conv{$_} and $$conv{$_} =~ /$pattern/; | 
| 3592 |  |  |  |  |  |  | } | 
| 3593 | 3212 | 100 |  |  |  | 11031 | last if $matches > 1; | 
| 3594 |  |  |  |  |  |  | } | 
| 3595 | 3442 |  |  |  |  | 53245 | foreach (sort keys %$conv) { | 
| 3596 | 10741 | 100 | 100 |  |  | 44744 | next if $$conv{$_} !~ /$pattern/ or $ignorePrintConv{$_}; | 
| 3597 | 3416 |  |  |  |  | 6654 | $val = $_; | 
| 3598 | 3416 |  |  |  |  | 5355 | $found = 1; | 
| 3599 | 3416 |  |  |  |  | 7403 | last PAT; | 
| 3600 |  |  |  |  |  |  | } | 
| 3601 |  |  |  |  |  |  | } | 
| 3602 | 8625 | 100 |  |  |  | 23778 | unless ($found) { | 
| 3603 |  |  |  |  |  |  | # call OTHER conversion routine if available | 
| 3604 | 5209 | 100 |  |  |  | 13342 | if ($$conv{OTHER}) { | 
| 3605 | 792 |  |  |  |  | 4099 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 3606 | 792 |  |  |  |  | 1472 | undef $evalWarning; | 
| 3607 | 792 |  |  |  |  | 1328 | $val = &{$$conv{OTHER}}($val,1,$conv); | 
|  | 792 |  |  |  |  | 3327 |  | 
| 3608 |  |  |  |  |  |  | } else { | 
| 3609 | 4417 |  |  |  |  | 7567 | $val = undef; | 
| 3610 |  |  |  |  |  |  | } | 
| 3611 | 5209 | 100 |  |  |  | 14671 | $multi = 1 if $matches > 1; | 
| 3612 |  |  |  |  |  |  | } | 
| 3613 |  |  |  |  |  |  | } | 
| 3614 | 8665 | 100 |  |  |  | 31022 | return ($val, $multi) if wantarray; | 
| 3615 | 47 |  |  |  |  | 164 | return $val; | 
| 3616 |  |  |  |  |  |  | } | 
| 3617 |  |  |  |  |  |  |  | 
| 3618 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3619 |  |  |  |  |  |  | # Return true if we are deleting or overwriting the specified tag | 
| 3620 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) new value hash reference | 
| 3621 |  |  |  |  |  |  | #         2) optional tag value (before RawConv) if deleting specific values | 
| 3622 |  |  |  |  |  |  | # Returns: >0 - tag should be overwritten | 
| 3623 |  |  |  |  |  |  | #          =0 - the tag should be preserved | 
| 3624 |  |  |  |  |  |  | #          <0 - not sure, we need the old value to tell (if there is no old value | 
| 3625 |  |  |  |  |  |  | #               then the tag should be written if $$nvHash{IsCreating} is true) | 
| 3626 |  |  |  |  |  |  | # Notes: $$nvHash{Value} is updated with the new value when shifting a value | 
| 3627 |  |  |  |  |  |  | sub IsOverwriting($$;$) | 
| 3628 |  |  |  |  |  |  | { | 
| 3629 | 6231 |  |  | 6231 | 0 | 13029 | my ($self, $nvHash, $val) = @_; | 
| 3630 | 6231 | 100 |  |  |  | 14616 | return 0 unless $nvHash; | 
| 3631 |  |  |  |  |  |  | # overwrite regardless if no DelValues specified | 
| 3632 | 6190 | 100 |  |  |  | 26633 | return 1 unless $$nvHash{DelValue}; | 
| 3633 |  |  |  |  |  |  | # never overwrite if DelValue list exists but is empty | 
| 3634 | 117 |  |  |  |  | 312 | my $shift = $$nvHash{Shift}; | 
| 3635 | 117 | 100 | 100 |  |  | 224 | return 0 unless @{$$nvHash{DelValue}} or defined $shift; | 
|  | 117 |  |  |  |  | 549 |  | 
| 3636 |  |  |  |  |  |  | # return "don't know" if we don't have a value to test | 
| 3637 | 104 | 100 |  |  |  | 384 | return -1 unless defined $val; | 
| 3638 |  |  |  |  |  |  | # apply raw conversion if necessary | 
| 3639 | 46 |  |  |  |  | 121 | my $tagInfo = $$nvHash{TagInfo}; | 
| 3640 | 46 |  |  |  |  | 127 | my $conv = $$tagInfo{RawConv}; | 
| 3641 | 46 | 100 |  |  |  | 148 | if ($conv) { | 
| 3642 | 3 |  |  |  |  | 22 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 3643 | 3 |  |  |  |  | 11 | undef $evalWarning; | 
| 3644 | 3 | 50 |  |  |  | 16 | if (ref $conv eq 'CODE') { | 
| 3645 | 0 |  |  |  |  | 0 | $val = &$conv($val, $self); | 
| 3646 |  |  |  |  |  |  | } else { | 
| 3647 | 3 |  |  |  |  | 9 | my ($priority, @grps); | 
| 3648 | 3 |  |  |  |  | 9 | my $tag = $$tagInfo{Name}; | 
| 3649 |  |  |  |  |  |  | #### eval RawConv ($self, $val, $tag, $tagInfo, $priority, @grps) | 
| 3650 | 3 |  |  |  |  | 324 | $val = eval $conv; | 
| 3651 | 3 | 50 |  |  |  | 23 | $@ and $evalWarning = $@; | 
| 3652 |  |  |  |  |  |  | } | 
| 3653 | 3 | 50 |  |  |  | 23 | return -1 unless defined $val; | 
| 3654 |  |  |  |  |  |  | } | 
| 3655 |  |  |  |  |  |  | # do not overwrite if only creating | 
| 3656 | 46 | 100 |  |  |  | 178 | return 0 if $$nvHash{CreateOnly}; | 
| 3657 |  |  |  |  |  |  | # apply time/number shift if necessary | 
| 3658 | 40 | 100 |  |  |  | 115 | if (defined $shift) { | 
| 3659 | 13 |  |  |  |  | 39 | my $shiftType = $$tagInfo{Shift}; | 
| 3660 | 13 | 100 | 66 |  |  | 70 | unless ($shiftType and $shiftType eq 'Time') { | 
| 3661 | 6 | 50 |  |  |  | 23 | unless (IsFloat($val)) { | 
| 3662 |  |  |  |  |  |  | # do the ValueConv to try to get a number | 
| 3663 | 0 |  |  |  |  | 0 | my $conv = $$tagInfo{ValueConv}; | 
| 3664 | 0 | 0 |  |  |  | 0 | if (defined $conv) { | 
| 3665 | 0 |  |  |  |  | 0 | local $SIG{'__WARN__'} = \&SetWarning; | 
| 3666 | 0 |  |  |  |  | 0 | undef $evalWarning; | 
| 3667 | 0 | 0 |  |  |  | 0 | if (ref $conv eq 'CODE') { | 
|  |  | 0 |  |  |  |  |  | 
| 3668 | 0 |  |  |  |  | 0 | $val = &$conv($val, $self); | 
| 3669 |  |  |  |  |  |  | } elsif (not ref $conv) { | 
| 3670 |  |  |  |  |  |  | #### eval ValueConv ($val, $self) | 
| 3671 | 0 |  |  |  |  | 0 | $val = eval $conv; | 
| 3672 | 0 | 0 |  |  |  | 0 | $@ and $evalWarning = $@; | 
| 3673 |  |  |  |  |  |  | } | 
| 3674 | 0 | 0 |  |  |  | 0 | if ($evalWarning) { | 
| 3675 | 0 |  |  |  |  | 0 | $self->Warn("ValueConv $$tagInfo{Name}: " . CleanWarning()); | 
| 3676 | 0 |  |  |  |  | 0 | return 0; | 
| 3677 |  |  |  |  |  |  | } | 
| 3678 |  |  |  |  |  |  | } | 
| 3679 | 0 | 0 | 0 |  |  | 0 | unless (defined $val and IsFloat($val)) { | 
| 3680 | 0 |  |  |  |  | 0 | $self->Warn("Can't shift $$tagInfo{Name} (not a number)"); | 
| 3681 | 0 |  |  |  |  | 0 | return 0; | 
| 3682 |  |  |  |  |  |  | } | 
| 3683 |  |  |  |  |  |  | } | 
| 3684 | 6 |  |  |  |  | 17 | $shiftType = 'Number';  # allow any number to be shifted | 
| 3685 |  |  |  |  |  |  | } | 
| 3686 | 13 |  |  |  |  | 106 | require 'Image/ExifTool/Shift.pl'; | 
| 3687 | 13 |  |  |  |  | 81 | my $err = $self->ApplyShift($shiftType, $shift, $val, $nvHash); | 
| 3688 | 13 | 50 |  |  |  | 42 | if ($err) { | 
| 3689 | 0 |  |  |  |  | 0 | $self->Warn("$err when shifting $$tagInfo{Name}"); | 
| 3690 | 0 |  |  |  |  | 0 | return 0; | 
| 3691 |  |  |  |  |  |  | } | 
| 3692 |  |  |  |  |  |  | # ensure that the shifted value is valid and reformat if necessary | 
| 3693 | 13 |  |  |  |  | 63 | my $checkVal = $self->GetNewValue($nvHash); | 
| 3694 | 13 | 50 |  |  |  | 43 | return 0 unless defined $checkVal; | 
| 3695 |  |  |  |  |  |  | # don't bother overwriting if value is the same | 
| 3696 | 13 | 50 |  |  |  | 61 | return 0 if $val eq $$nvHash{Value}[0]; | 
| 3697 | 13 |  |  |  |  | 69 | return 1; | 
| 3698 |  |  |  |  |  |  | } | 
| 3699 |  |  |  |  |  |  | # return 1 if value matches a DelValue | 
| 3700 | 27 |  |  |  |  | 51 | my $delVal; | 
| 3701 | 27 |  |  |  |  | 51 | foreach $delVal (@{$$nvHash{DelValue}}) { | 
|  | 27 |  |  |  |  | 79 |  | 
| 3702 | 32 | 100 |  |  |  | 115 | return 1 if $val eq $delVal; | 
| 3703 |  |  |  |  |  |  | } | 
| 3704 | 17 |  |  |  |  | 65 | return 0; | 
| 3705 |  |  |  |  |  |  | } | 
| 3706 |  |  |  |  |  |  |  | 
| 3707 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3708 |  |  |  |  |  |  | # Get write group for specified tag | 
| 3709 |  |  |  |  |  |  | # Inputs: 0) new value hash reference | 
| 3710 |  |  |  |  |  |  | # Returns: Write group name | 
| 3711 |  |  |  |  |  |  | sub GetWriteGroup($) | 
| 3712 |  |  |  |  |  |  | { | 
| 3713 | 0 |  |  | 0 | 0 | 0 | return $_[0]{WriteGroup}; | 
| 3714 |  |  |  |  |  |  | } | 
| 3715 |  |  |  |  |  |  |  | 
| 3716 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3717 |  |  |  |  |  |  | # Get name of write group or family 1 group | 
| 3718 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) tagInfo ref, 2) write group name | 
| 3719 |  |  |  |  |  |  | # Returns: Name of group for verbose message | 
| 3720 |  |  |  |  |  |  | sub GetWriteGroup1($$) | 
| 3721 |  |  |  |  |  |  | { | 
| 3722 | 32511 |  |  | 32511 | 0 | 69255 | my ($self, $tagInfo, $writeGroup) = @_; | 
| 3723 | 32511 | 100 |  |  |  | 143084 | return $writeGroup unless $writeGroup =~ /^(MakerNotes|XMP|Composite|QuickTime)$/; | 
| 3724 | 27117 |  |  |  |  | 97308 | return $self->GetGroup($tagInfo, 1); | 
| 3725 |  |  |  |  |  |  | } | 
| 3726 |  |  |  |  |  |  |  | 
| 3727 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3728 |  |  |  |  |  |  | # Get new value hash for specified tagInfo/writeGroup | 
| 3729 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) reference to tag info hash | 
| 3730 |  |  |  |  |  |  | #         2) Write group name, 3) Options: 'delete' or 'create' new value hash | 
| 3731 |  |  |  |  |  |  | #         4) optional ProtectSaved value, 5) true if we are deleting a value | 
| 3732 |  |  |  |  |  |  | # Returns: new value hash reference for specified write group | 
| 3733 |  |  |  |  |  |  | #          (or first new value hash in linked list if write group not specified) | 
| 3734 |  |  |  |  |  |  | # Notes: May return undef when 'create' is used with ProtectSaved | 
| 3735 |  |  |  |  |  |  | sub GetNewValueHash($$;$$$$) | 
| 3736 |  |  |  |  |  |  | { | 
| 3737 | 67739 |  |  | 67739 | 0 | 166811 | my ($self, $tagInfo, $writeGroup, $opts) = @_; | 
| 3738 | 67739 | 100 |  |  |  | 136621 | return undef unless $tagInfo; | 
| 3739 | 67738 |  |  |  |  | 176279 | my $nvHash = $$self{NEW_VALUE}{$tagInfo}; | 
| 3740 |  |  |  |  |  |  |  | 
| 3741 | 67738 |  |  |  |  | 92541 | my %opts;   # quick lookup for options | 
| 3742 | 67738 | 100 |  |  |  | 147340 | $opts and $opts{$opts} = 1; | 
| 3743 | 67738 | 100 |  |  |  | 127812 | $writeGroup = '' unless defined $writeGroup; | 
| 3744 |  |  |  |  |  |  |  | 
| 3745 | 67738 | 100 |  |  |  | 120816 | if ($writeGroup) { | 
| 3746 |  |  |  |  |  |  | # find the new value in the list with the specified write group | 
| 3747 | 46539 |  | 100 |  |  | 115791 | while ($nvHash and $$nvHash{WriteGroup} ne $writeGroup) { | 
| 3748 |  |  |  |  |  |  | # QuickTime and All are special cases because all group1 tags may be updated at once | 
| 3749 | 2010 | 100 |  |  |  | 6504 | last if $$nvHash{WriteGroup} =~ /^(QuickTime|All)$/; | 
| 3750 |  |  |  |  |  |  | # replace existing entry if WriteGroup is 'All' (avoids confusion of forum10349) | 
| 3751 | 1974 | 100 | 100 |  |  | 5700 | last if $$tagInfo{WriteGroup} and $$tagInfo{WriteGroup} eq 'All'; | 
| 3752 | 1962 |  |  |  |  | 4666 | $nvHash = $$nvHash{Next}; | 
| 3753 |  |  |  |  |  |  | } | 
| 3754 |  |  |  |  |  |  | } | 
| 3755 |  |  |  |  |  |  | # remove this entry if deleting, or if creating a new entry and | 
| 3756 |  |  |  |  |  |  | # this entry is marked with "Save" flag | 
| 3757 | 67738 | 100 | 100 |  |  | 179240 | if (defined $nvHash and ($opts{'delete'} or ($opts{'create'} and $$nvHash{Save}))) { | 
|  |  |  | 100 |  |  |  |  | 
| 3758 | 2406 |  | 33 |  |  | 7588 | my $protect = (defined $_[4] and defined $$nvHash{Save} and $$nvHash{Save} > $_[4]); | 
| 3759 |  |  |  |  |  |  | # this is a bit tricky:  we want to add to a protected nvHash only if we | 
| 3760 |  |  |  |  |  |  | # are adding a conditional delete ($_[5] true or DelValue with no Shift) | 
| 3761 |  |  |  |  |  |  | # or accumulating List items (NoReplace true) | 
| 3762 | 2406 | 50 | 0 |  |  | 8408 | if ($protect and not ($opts{create} and ($$nvHash{NoReplace} or $_[5] or | 
|  |  | 100 | 33 |  |  |  |  | 
| 3763 |  |  |  |  |  |  | ($$nvHash{DelValue} and not defined $$nvHash{Shift})))) | 
| 3764 |  |  |  |  |  |  | { | 
| 3765 | 0 |  |  |  |  | 0 | return undef;   # honour ProtectSaved value by not writing this tag | 
| 3766 |  |  |  |  |  |  | } elsif ($opts{'delete'}) { | 
| 3767 | 2396 |  |  |  |  | 8103 | $self->RemoveNewValueHash($nvHash, $tagInfo); | 
| 3768 | 2396 |  |  |  |  | 7972 | undef $nvHash; | 
| 3769 |  |  |  |  |  |  | } else { | 
| 3770 |  |  |  |  |  |  | # save a copy of this new value hash | 
| 3771 | 10 |  |  |  |  | 147 | my %copy = %$nvHash; | 
| 3772 |  |  |  |  |  |  | # make copy of Value and DelValue lists | 
| 3773 | 10 |  |  |  |  | 39 | my $key; | 
| 3774 | 10 |  |  |  |  | 36 | foreach $key (keys %copy) { | 
| 3775 | 75 | 100 |  |  |  | 171 | next unless ref $copy{$key} eq 'ARRAY'; | 
| 3776 | 10 |  |  |  |  | 17 | $copy{$key} = [ @{$copy{$key}} ]; | 
|  | 10 |  |  |  |  | 76 |  | 
| 3777 |  |  |  |  |  |  | } | 
| 3778 | 10 |  |  |  |  | 36 | my $saveHash = $$self{SAVE_NEW_VALUE}; | 
| 3779 |  |  |  |  |  |  | # add to linked list of saved new value hashes | 
| 3780 | 10 |  |  |  |  | 26 | $copy{Next} = $$saveHash{$tagInfo}; | 
| 3781 | 10 |  |  |  |  | 30 | $$saveHash{$tagInfo} = \%copy; | 
| 3782 | 10 |  |  |  |  | 21 | delete $$nvHash{Save}; # don't save it again | 
| 3783 | 10 | 0 | 33 |  |  | 40 | $$nvHash{AddBefore} = scalar @{$$nvHash{Value}} if $protect and $$nvHash{Value}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 3784 |  |  |  |  |  |  | } | 
| 3785 |  |  |  |  |  |  | } | 
| 3786 | 67738 | 100 | 100 |  |  | 192009 | if (not defined $nvHash and $opts{'create'}) { | 
| 3787 |  |  |  |  |  |  | # create a new entry | 
| 3788 | 23209 |  |  |  |  | 98364 | $nvHash = { | 
| 3789 |  |  |  |  |  |  | TagInfo => $tagInfo, | 
| 3790 |  |  |  |  |  |  | WriteGroup => $writeGroup, | 
| 3791 |  |  |  |  |  |  | IsNVH => 1, # set flag so we can recognize a new value hash | 
| 3792 |  |  |  |  |  |  | }; | 
| 3793 |  |  |  |  |  |  | # add entry to our NEW_VALUE hash | 
| 3794 | 23209 | 100 |  |  |  | 59540 | if ($$self{NEW_VALUE}{$tagInfo}) { | 
| 3795 |  |  |  |  |  |  | # add to end of linked list | 
| 3796 | 33 |  |  |  |  | 229 | my $lastHash = LastInList($$self{NEW_VALUE}{$tagInfo}); | 
| 3797 | 33 |  |  |  |  | 140 | $$lastHash{Next} = $nvHash; | 
| 3798 |  |  |  |  |  |  | } else { | 
| 3799 | 23176 |  |  |  |  | 68412 | $$self{NEW_VALUE}{$tagInfo} = $nvHash; | 
| 3800 |  |  |  |  |  |  | } | 
| 3801 |  |  |  |  |  |  | } | 
| 3802 | 67738 |  |  |  |  | 167204 | return $nvHash; | 
| 3803 |  |  |  |  |  |  | } | 
| 3804 |  |  |  |  |  |  |  | 
| 3805 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3806 |  |  |  |  |  |  | # Load all tag tables | 
| 3807 |  |  |  |  |  |  | sub LoadAllTables() | 
| 3808 |  |  |  |  |  |  | { | 
| 3809 | 0 | 0 |  | 0 | 0 | 0 | return if $loadedAllTables; | 
| 3810 |  |  |  |  |  |  |  | 
| 3811 |  |  |  |  |  |  | # load all of our non-referenced tables (first our modules) | 
| 3812 | 0 |  |  |  |  | 0 | my $table; | 
| 3813 | 0 |  |  |  |  | 0 | foreach $table (@loadAllTables) { | 
| 3814 | 0 |  |  |  |  | 0 | my $tableName = "Image::ExifTool::$table"; | 
| 3815 | 0 | 0 |  |  |  | 0 | $tableName .= '::Main' unless $table =~ /:/; | 
| 3816 | 0 |  |  |  |  | 0 | GetTagTable($tableName); | 
| 3817 |  |  |  |  |  |  | } | 
| 3818 |  |  |  |  |  |  | # (then our special tables) | 
| 3819 | 0 |  |  |  |  | 0 | GetTagTable('Image::ExifTool::Extra'); | 
| 3820 | 0 |  |  |  |  | 0 | GetTagTable('Image::ExifTool::Composite'); | 
| 3821 |  |  |  |  |  |  | # recursively load all tables referenced by the current tables | 
| 3822 | 0 |  |  |  |  | 0 | my @tableNames = keys %allTables; | 
| 3823 | 0 |  |  |  |  | 0 | my %pushedTables; | 
| 3824 | 0 |  |  |  |  | 0 | while (@tableNames) { | 
| 3825 | 0 |  |  |  |  | 0 | $table = GetTagTable(shift @tableNames); | 
| 3826 |  |  |  |  |  |  | # call write proc if it exists in case it adds tags to the table | 
| 3827 | 0 |  |  |  |  | 0 | my $writeProc = $$table{WRITE_PROC}; | 
| 3828 | 0 | 0 |  |  |  | 0 | if ($writeProc) { | 
| 3829 | 59 |  |  | 59 |  | 706 | no strict 'refs'; | 
|  | 59 |  |  |  |  | 214 |  | 
|  | 59 |  |  |  |  | 183616 |  | 
| 3830 | 0 |  |  |  |  | 0 | &$writeProc(); | 
| 3831 |  |  |  |  |  |  | } | 
| 3832 |  |  |  |  |  |  | # recursively scan through tables in subdirectories | 
| 3833 | 0 |  |  |  |  | 0 | foreach (TagTableKeys($table)) { | 
| 3834 | 0 |  |  |  |  | 0 | my @infoArray = GetTagInfoList($table,$_); | 
| 3835 | 0 |  |  |  |  | 0 | my $tagInfo; | 
| 3836 | 0 |  |  |  |  | 0 | foreach $tagInfo (@infoArray) { | 
| 3837 | 0 | 0 |  |  |  | 0 | my $subdir = $$tagInfo{SubDirectory} or next; | 
| 3838 | 0 | 0 |  |  |  | 0 | my $tableName = $$subdir{TagTable} or next; | 
| 3839 |  |  |  |  |  |  | # next if table already loaded or queued for loading | 
| 3840 | 0 | 0 | 0 |  |  | 0 | next if $allTables{$tableName} or $pushedTables{$tableName}; | 
| 3841 | 0 |  |  |  |  | 0 | push @tableNames, $tableName;   # must scan this one too | 
| 3842 | 0 |  |  |  |  | 0 | $pushedTables{$tableName} = 1; | 
| 3843 |  |  |  |  |  |  | } | 
| 3844 |  |  |  |  |  |  | } | 
| 3845 |  |  |  |  |  |  | } | 
| 3846 | 0 |  |  |  |  | 0 | $loadedAllTables = 1; | 
| 3847 |  |  |  |  |  |  | } | 
| 3848 |  |  |  |  |  |  |  | 
| 3849 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3850 |  |  |  |  |  |  | # Remove new value hash from linked list (and save if necessary) | 
| 3851 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) new value hash ref, 2) tagInfo ref | 
| 3852 |  |  |  |  |  |  | sub RemoveNewValueHash($$$) | 
| 3853 |  |  |  |  |  |  | { | 
| 3854 | 2666 |  |  | 2666 | 0 | 5073 | my ($self, $nvHash, $tagInfo) = @_; | 
| 3855 | 2666 |  |  |  |  | 5992 | my $firstHash = $$self{NEW_VALUE}{$tagInfo}; | 
| 3856 | 2666 | 50 |  |  |  | 7045 | if ($nvHash eq $firstHash) { | 
| 3857 |  |  |  |  |  |  | # remove first entry from linked list | 
| 3858 | 2666 | 50 |  |  |  | 5794 | if ($$nvHash{Next}) { | 
| 3859 | 0 |  |  |  |  | 0 | $$self{NEW_VALUE}{$tagInfo} = $$nvHash{Next}; | 
| 3860 |  |  |  |  |  |  | } else { | 
| 3861 | 2666 |  |  |  |  | 7438 | delete $$self{NEW_VALUE}{$tagInfo}; | 
| 3862 |  |  |  |  |  |  | } | 
| 3863 |  |  |  |  |  |  | } else { | 
| 3864 |  |  |  |  |  |  | # find the list element pointing to this hash | 
| 3865 | 0 |  |  |  |  | 0 | $firstHash = $$firstHash{Next} while $$firstHash{Next} ne $nvHash; | 
| 3866 |  |  |  |  |  |  | # remove from linked list | 
| 3867 | 0 |  |  |  |  | 0 | $$firstHash{Next} = $$nvHash{Next}; | 
| 3868 |  |  |  |  |  |  | } | 
| 3869 |  |  |  |  |  |  | # save the existing entry if necessary | 
| 3870 | 2666 | 100 |  |  |  | 8146 | if ($$nvHash{Save}) { | 
| 3871 | 80 |  |  |  |  | 163 | my $saveHash = $$self{SAVE_NEW_VALUE}; | 
| 3872 |  |  |  |  |  |  | # add to linked list of saved new value hashes | 
| 3873 | 80 |  |  |  |  | 216 | $$nvHash{Next} = $$saveHash{$tagInfo}; | 
| 3874 | 80 |  |  |  |  | 291 | $$saveHash{$tagInfo} = $nvHash; | 
| 3875 |  |  |  |  |  |  | } | 
| 3876 |  |  |  |  |  |  | } | 
| 3877 |  |  |  |  |  |  |  | 
| 3878 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3879 |  |  |  |  |  |  | # Remove all new value entries for specified group | 
| 3880 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) group name | 
| 3881 |  |  |  |  |  |  | sub RemoveNewValuesForGroup($$) | 
| 3882 |  |  |  |  |  |  | { | 
| 3883 | 784 |  |  | 784 | 0 | 1286 | my ($self, $group) = @_; | 
| 3884 |  |  |  |  |  |  |  | 
| 3885 | 784 | 100 |  |  |  | 1611 | return unless $$self{NEW_VALUE}; | 
| 3886 |  |  |  |  |  |  |  | 
| 3887 |  |  |  |  |  |  | # make list of all groups we must remove | 
| 3888 | 7 |  |  |  |  | 27 | my @groups = ( $group ); | 
| 3889 | 7 | 100 |  |  |  | 39 | push @groups, @{$removeGroups{$group}} if $removeGroups{$group}; | 
|  | 2 |  |  |  |  | 9 |  | 
| 3890 |  |  |  |  |  |  |  | 
| 3891 | 7 |  |  |  |  | 22 | my ($out, @keys, $hashKey); | 
| 3892 | 7 | 50 |  |  |  | 36 | $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose} > 1; | 
| 3893 |  |  |  |  |  |  |  | 
| 3894 |  |  |  |  |  |  | # loop though all new values, and remove any in this group | 
| 3895 | 7 |  |  |  |  | 18 | @keys = keys %{$$self{NEW_VALUE}}; | 
|  | 7 |  |  |  |  | 754 |  | 
| 3896 | 7 |  |  |  |  | 44 | foreach $hashKey (@keys) { | 
| 3897 | 1985 |  |  |  |  | 5572 | my $nvHash = $$self{NEW_VALUE}{$hashKey}; | 
| 3898 |  |  |  |  |  |  | # loop through each entry in linked list | 
| 3899 | 1985 |  |  |  |  | 2832 | for (;;) { | 
| 3900 | 1991 |  |  |  |  | 4500 | my $nextHash = $$nvHash{Next}; | 
| 3901 | 1991 |  |  |  |  | 4620 | my $tagInfo = $$nvHash{TagInfo}; | 
| 3902 | 1991 |  |  |  |  | 5354 | my ($grp0,$grp1) = $self->GetGroup($tagInfo); | 
| 3903 | 1991 |  |  |  |  | 5938 | my $wgrp = $$nvHash{WriteGroup}; | 
| 3904 |  |  |  |  |  |  | # use group1 if write group is not specific | 
| 3905 | 1991 | 100 |  |  |  | 4399 | $wgrp = $grp1 if $wgrp eq $grp0; | 
| 3906 | 1991 | 100 |  |  |  | 45239 | if (grep /^($grp0|$wgrp)$/i, @groups) { | 
| 3907 | 270 | 50 |  |  |  | 772 | $out and print $out "Removed new value for $wgrp:$$tagInfo{Name}\n"; | 
| 3908 |  |  |  |  |  |  | # remove from linked list | 
| 3909 | 270 |  |  |  |  | 712 | $self->RemoveNewValueHash($nvHash, $tagInfo); | 
| 3910 |  |  |  |  |  |  | } | 
| 3911 | 1991 | 100 |  |  |  | 8158 | $nvHash = $nextHash or last; | 
| 3912 |  |  |  |  |  |  | } | 
| 3913 |  |  |  |  |  |  | } | 
| 3914 |  |  |  |  |  |  | } | 
| 3915 |  |  |  |  |  |  |  | 
| 3916 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3917 |  |  |  |  |  |  | # Get list of tagInfo hashes for all new data | 
| 3918 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) optional tag table pointer | 
| 3919 |  |  |  |  |  |  | # Returns: list of tagInfo hashes | 
| 3920 |  |  |  |  |  |  | sub GetNewTagInfoList($;$) | 
| 3921 |  |  |  |  |  |  | { | 
| 3922 | 1207 |  |  | 1207 | 0 | 3049 | my ($self, $tagTablePtr) = @_; | 
| 3923 | 1207 |  |  |  |  | 2063 | my @tagInfoList; | 
| 3924 | 1207 |  |  |  |  | 3060 | my $nv = $$self{NEW_VALUE}; | 
| 3925 | 1207 | 100 |  |  |  | 3346 | if ($nv) { | 
| 3926 | 1183 |  |  |  |  | 1966 | my $hashKey; | 
| 3927 | 1183 |  |  |  |  | 22084 | foreach $hashKey (keys %$nv) { | 
| 3928 | 89484 |  |  |  |  | 162385 | my $tagInfo = $$nv{$hashKey}{TagInfo}; | 
| 3929 | 89484 | 100 | 100 |  |  | 262594 | next if $tagTablePtr and $tagTablePtr ne $$tagInfo{Table}; | 
| 3930 | 32539 |  |  |  |  | 57811 | push @tagInfoList, $tagInfo; | 
| 3931 |  |  |  |  |  |  | } | 
| 3932 |  |  |  |  |  |  | } | 
| 3933 | 1207 |  |  |  |  | 15307 | return @tagInfoList; | 
| 3934 |  |  |  |  |  |  | } | 
| 3935 |  |  |  |  |  |  |  | 
| 3936 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3937 |  |  |  |  |  |  | # Get hash of tagInfo references keyed on tagID for a specific table | 
| 3938 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1-N) tag table pointers | 
| 3939 |  |  |  |  |  |  | # Returns: hash reference | 
| 3940 |  |  |  |  |  |  | # Notes: returns only one tagInfo ref for each conditional list | 
| 3941 |  |  |  |  |  |  | sub GetNewTagInfoHash($@) | 
| 3942 |  |  |  |  |  |  | { | 
| 3943 | 474 |  |  | 474 | 0 | 914 | my $self = shift; | 
| 3944 | 474 |  |  |  |  | 879 | my (%tagInfoHash, $hashKey); | 
| 3945 | 474 |  |  |  |  | 1025 | my $nv = $$self{NEW_VALUE}; | 
| 3946 | 474 |  |  |  |  | 1253 | while ($nv) { | 
| 3947 | 923 |  | 100 |  |  | 2305 | my $tagTablePtr = shift || last; | 
| 3948 | 464 |  |  |  |  | 4791 | foreach $hashKey (keys %$nv) { | 
| 3949 | 21558 |  |  |  |  | 36425 | my $tagInfo = $$nv{$hashKey}{TagInfo}; | 
| 3950 | 21558 | 100 | 66 |  |  | 74453 | next if $tagTablePtr and $tagTablePtr ne $$tagInfo{Table}; | 
| 3951 | 288 |  |  |  |  | 1209 | $tagInfoHash{$$tagInfo{TagID}} = $tagInfo; | 
| 3952 |  |  |  |  |  |  | } | 
| 3953 |  |  |  |  |  |  | } | 
| 3954 | 474 |  |  |  |  | 1684 | return \%tagInfoHash; | 
| 3955 |  |  |  |  |  |  | } | 
| 3956 |  |  |  |  |  |  |  | 
| 3957 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3958 |  |  |  |  |  |  | # Get a tagInfo/tagID hash for subdirectories we need to add | 
| 3959 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) parent tag table reference | 
| 3960 |  |  |  |  |  |  | #         2) parent directory name (taken from GROUP0 of tag table if not defined) | 
| 3961 |  |  |  |  |  |  | # Returns: Reference to Hash of subdirectory tagInfo references keyed by tagID | 
| 3962 |  |  |  |  |  |  | #          (plus Reference to edit directory hash in list context) | 
| 3963 |  |  |  |  |  |  | sub GetAddDirHash($$;$) | 
| 3964 |  |  |  |  |  |  | { | 
| 3965 | 460 |  |  | 460 | 0 | 1322 | my ($self, $tagTablePtr, $parent) = @_; | 
| 3966 | 460 | 100 |  |  |  | 1361 | $parent or $parent = $$tagTablePtr{GROUPS}{0}; | 
| 3967 | 460 |  |  |  |  | 1336 | my $tagID; | 
| 3968 |  |  |  |  |  |  | my %addDirHash; | 
| 3969 | 460 |  |  |  |  | 0 | my %editDirHash; | 
| 3970 | 460 |  |  |  |  | 4656 | my $addDirs = $$self{ADD_DIRS}; | 
| 3971 | 460 |  |  |  |  | 992 | my $editDirs = $$self{EDIT_DIRS}; | 
| 3972 | 460 |  |  |  |  | 1756 | foreach $tagID (TagTableKeys($tagTablePtr)) { | 
| 3973 | 151029 |  |  |  |  | 269679 | my @infoArray = GetTagInfoList($tagTablePtr,$tagID); | 
| 3974 | 151029 |  |  |  |  | 193129 | my $tagInfo; | 
| 3975 | 151029 |  |  |  |  | 214546 | foreach $tagInfo (@infoArray) { | 
| 3976 | 187219 | 100 |  |  |  | 475417 | next unless $$tagInfo{SubDirectory}; | 
| 3977 |  |  |  |  |  |  | # get name for this sub directory | 
| 3978 |  |  |  |  |  |  | # (take directory name from SubDirectory DirName if it exists, | 
| 3979 |  |  |  |  |  |  | #  otherwise Group0 name of SubDirectory TagTable or tag Group1 name) | 
| 3980 | 34442 |  |  |  |  | 60975 | my $dirName = $$tagInfo{SubDirectory}{DirName}; | 
| 3981 | 34442 | 100 |  |  |  | 54277 | unless ($dirName) { | 
| 3982 |  |  |  |  |  |  | # use tag name for directory name and save for next time | 
| 3983 | 3797 |  |  |  |  | 7971 | $dirName = $$tagInfo{Name}; | 
| 3984 | 3797 |  |  |  |  | 5998 | $$tagInfo{SubDirectory}{DirName} = $dirName; | 
| 3985 |  |  |  |  |  |  | } | 
| 3986 |  |  |  |  |  |  | # save this directory information if we are writing it | 
| 3987 | 34442 | 100 | 100 |  |  | 77949 | if ($$editDirs{$dirName} and $$editDirs{$dirName} eq $parent) { | 
| 3988 | 254 |  |  |  |  | 945 | $editDirHash{$tagID} = $tagInfo; | 
| 3989 | 254 | 100 |  |  |  | 1317 | $addDirHash{$tagID} = $tagInfo if $$addDirs{$dirName}; | 
| 3990 |  |  |  |  |  |  | } | 
| 3991 |  |  |  |  |  |  | } | 
| 3992 |  |  |  |  |  |  | } | 
| 3993 | 460 | 100 |  |  |  | 7322 | return (\%addDirHash, \%editDirHash) if wantarray; | 
| 3994 | 384 |  |  |  |  | 2024 | return \%addDirHash; | 
| 3995 |  |  |  |  |  |  | } | 
| 3996 |  |  |  |  |  |  |  | 
| 3997 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 3998 |  |  |  |  |  |  | # Get localized version of tagInfo hash (used by MIE, XMP, PNG and QuickTime) | 
| 3999 |  |  |  |  |  |  | # Inputs: 0) tagInfo hash ref, 1) locale code (eg. "en_CA" for MIE) | 
| 4000 |  |  |  |  |  |  | # Returns: new tagInfo hash ref, or undef if invalid | 
| 4001 |  |  |  |  |  |  | # - sets LangCode member in new tagInfo | 
| 4002 |  |  |  |  |  |  | sub GetLangInfo($$) | 
| 4003 |  |  |  |  |  |  | { | 
| 4004 | 298 |  |  | 298 | 0 | 636 | my ($tagInfo, $langCode) = @_; | 
| 4005 |  |  |  |  |  |  | # make a new tagInfo hash for this locale | 
| 4006 | 298 |  |  |  |  | 581 | my $table = $$tagInfo{Table}; | 
| 4007 | 298 |  |  |  |  | 819 | my $tagID = $$tagInfo{TagID} . '-' . $langCode; | 
| 4008 | 298 |  |  |  |  | 694 | my $langInfo = $$table{$tagID}; | 
| 4009 | 298 | 100 |  |  |  | 771 | unless ($langInfo) { | 
| 4010 |  |  |  |  |  |  | # make a new tagInfo entry for this locale | 
| 4011 |  |  |  |  |  |  | $langInfo = { | 
| 4012 |  |  |  |  |  |  | %$tagInfo, | 
| 4013 |  |  |  |  |  |  | Name => $$tagInfo{Name} . '-' . $langCode, | 
| 4014 | 182 |  |  |  |  | 1049 | Description => Image::ExifTool::MakeDescription($$tagInfo{Name}) . | 
| 4015 |  |  |  |  |  |  | " ($langCode)", | 
| 4016 |  |  |  |  |  |  | LangCode => $langCode, | 
| 4017 |  |  |  |  |  |  | SrcTagInfo => $tagInfo, # save reference to original tagInfo | 
| 4018 |  |  |  |  |  |  | }; | 
| 4019 | 182 |  |  |  |  | 665 | AddTagToTable($table, $tagID, $langInfo); | 
| 4020 |  |  |  |  |  |  | } | 
| 4021 | 298 |  |  |  |  | 785 | return $langInfo; | 
| 4022 |  |  |  |  |  |  | } | 
| 4023 |  |  |  |  |  |  |  | 
| 4024 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4025 |  |  |  |  |  |  | # initialize ADD_DIRS and EDIT_DIRS hashes for all directories that need | 
| 4026 |  |  |  |  |  |  | # to be created or will have tags changed in them | 
| 4027 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) file type string (or map hash ref) | 
| 4028 |  |  |  |  |  |  | #         2) preferred family 0 group for creating tags, 3) alternate preferred group | 
| 4029 |  |  |  |  |  |  | # Notes: | 
| 4030 |  |  |  |  |  |  | # - the ADD_DIRS and EDIT_DIRS keys are the directory names, and the values | 
| 4031 |  |  |  |  |  |  | #   are the names of the parent directories (undefined for a top-level directory) | 
| 4032 |  |  |  |  |  |  | # - also initializes FORCE_WRITE lookup | 
| 4033 |  |  |  |  |  |  | sub InitWriteDirs($$;$$) | 
| 4034 |  |  |  |  |  |  | { | 
| 4035 | 318 |  |  | 318 | 0 | 1198 | my ($self, $fileType, $preferredGroup, $altGroup) = @_; | 
| 4036 | 318 |  |  |  |  | 1424 | my $editDirs = $$self{EDIT_DIRS} = { }; | 
| 4037 | 318 |  |  |  |  | 1259 | my $addDirs = $$self{ADD_DIRS} = { }; | 
| 4038 | 318 |  |  |  |  | 1207 | my $fileDirs = $dirMap{$fileType}; | 
| 4039 | 318 | 100 |  |  |  | 1180 | unless ($fileDirs) { | 
| 4040 | 192 | 100 |  |  |  | 853 | return unless ref $fileType eq 'HASH'; | 
| 4041 | 80 |  |  |  |  | 286 | $fileDirs = $fileType; | 
| 4042 |  |  |  |  |  |  | } | 
| 4043 | 206 |  |  |  |  | 1615 | my @tagInfoList = $self->GetNewTagInfoList(); | 
| 4044 | 206 |  |  |  |  | 704 | my ($tagInfo, $nvHash); | 
| 4045 |  |  |  |  |  |  |  | 
| 4046 |  |  |  |  |  |  | # save the preferred group | 
| 4047 | 206 |  |  |  |  | 847 | $$self{PreferredGroup} = $preferredGroup; | 
| 4048 |  |  |  |  |  |  |  | 
| 4049 | 206 |  |  |  |  | 683 | foreach $tagInfo (@tagInfoList) { | 
| 4050 |  |  |  |  |  |  | # cycle through all hashes in linked list | 
| 4051 | 12958 |  |  |  |  | 24830 | for ($nvHash=$self->GetNewValueHash($tagInfo); $nvHash; $nvHash=$$nvHash{Next}) { | 
| 4052 |  |  |  |  |  |  | # are we creating this tag? (otherwise just deleting or editing it) | 
| 4053 | 12985 |  |  |  |  | 27333 | my $isCreating = $$nvHash{IsCreating}; | 
| 4054 | 12985 | 100 |  |  |  | 21657 | if ($preferredGroup) { | 
| 4055 | 3536 |  |  |  |  | 8345 | my $g0 = $self->GetGroup($tagInfo, 0); | 
| 4056 | 3536 | 100 |  |  |  | 6928 | if ($isCreating) { | 
| 4057 |  |  |  |  |  |  | # if another group is taking priority, only create | 
| 4058 |  |  |  |  |  |  | # directory if specifically adding tags to this group | 
| 4059 |  |  |  |  |  |  | # or if this tag isn't being added to the priority group | 
| 4060 |  |  |  |  |  |  | $isCreating = 0 if $preferredGroup ne $g0 and | 
| 4061 | 826 | 100 | 100 |  |  | 3873 | $$nvHash{CreateGroups}{$preferredGroup} and | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 4062 |  |  |  |  |  |  | (not $altGroup or $altGroup ne $g0); | 
| 4063 |  |  |  |  |  |  | } else { | 
| 4064 |  |  |  |  |  |  | # create this directory if any tag is preferred and has a value | 
| 4065 |  |  |  |  |  |  | # (unless group creation is disabled via the WriteMode option) | 
| 4066 |  |  |  |  |  |  | $isCreating = 1 if $$nvHash{Value} and $preferredGroup eq $g0 and | 
| 4067 | 2710 | 50 | 100 |  |  | 10864 | not $$nvHash{EditOnly} and $$self{OPTIONS}{WriteMode} =~ /g/; | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 4068 |  |  |  |  |  |  | } | 
| 4069 |  |  |  |  |  |  | } | 
| 4070 |  |  |  |  |  |  | # tag belongs to directory specified by WriteGroup, or by | 
| 4071 |  |  |  |  |  |  | # the Group0 name if WriteGroup not defined | 
| 4072 | 12985 |  |  |  |  | 28494 | my $dirName = $$nvHash{WriteGroup}; | 
| 4073 |  |  |  |  |  |  | # remove MIE copy number(s) if they exist | 
| 4074 | 12985 | 100 |  |  |  | 29501 | if ($dirName =~ /^MIE\d*(-[a-z]+)?\d*$/i) { | 
| 4075 | 388 |  | 50 |  |  | 1868 | $dirName = 'MIE' . ($1 || ''); | 
| 4076 |  |  |  |  |  |  | } | 
| 4077 | 12985 |  |  |  |  | 17551 | my @dirNames; | 
| 4078 |  |  |  |  |  |  | # allow a group name of '*' to force writing EXIF/IPTC/XMP/PNG (ForceWrite tag) | 
| 4079 | 12985 | 50 | 33 |  |  | 33017 | if ($dirName eq '*' and $$nvHash{Value}) { | 
|  |  | 100 |  |  |  |  |  | 
| 4080 | 0 |  |  |  |  | 0 | my $val = $$nvHash{Value}[0]; | 
| 4081 | 0 | 0 |  |  |  | 0 | if ($val) { | 
| 4082 | 0 |  |  |  |  | 0 | foreach (qw(EXIF IPTC XMP PNG FixBase)) { | 
| 4083 | 0 | 0 |  |  |  | 0 | next unless $val =~ /\b($_|All)\b/i; | 
| 4084 | 0 |  |  |  |  | 0 | push @dirNames, $_; | 
| 4085 | 0 | 0 |  |  |  | 0 | push @dirNames, 'EXIF' if $_ eq 'FixBase'; | 
| 4086 | 0 |  |  |  |  | 0 | $$self{FORCE_WRITE}{$_} = 1; | 
| 4087 |  |  |  |  |  |  | } | 
| 4088 |  |  |  |  |  |  | } | 
| 4089 | 0 |  |  |  |  | 0 | $dirName = shift @dirNames; | 
| 4090 |  |  |  |  |  |  | } elsif ($dirName eq 'QuickTime') { | 
| 4091 |  |  |  |  |  |  | # write to specific QuickTime group | 
| 4092 | 46 |  |  |  |  | 308 | $dirName = $self->GetGroup($tagInfo, 1); | 
| 4093 |  |  |  |  |  |  | } | 
| 4094 | 12985 |  |  |  |  | 22356 | while ($dirName) { | 
| 4095 | 52730 |  |  |  |  | 78405 | my $parent = $$fileDirs{$dirName}; | 
| 4096 | 52730 | 100 |  |  |  | 86086 | if (ref $parent) { | 
| 4097 | 6366 |  |  |  |  | 12105 | push @dirNames, reverse @$parent; | 
| 4098 | 6366 |  |  |  |  | 9235 | $parent = pop @dirNames; | 
| 4099 |  |  |  |  |  |  | } | 
| 4100 | 52730 |  |  |  |  | 76562 | $$editDirs{$dirName} = $parent; | 
| 4101 | 52730 | 100 | 100 |  |  | 98974 | $$addDirs{$dirName} = $parent if $isCreating and $isCreating != 2; | 
| 4102 | 52730 |  | 100 |  |  | 143667 | $dirName = $parent || shift @dirNames | 
| 4103 |  |  |  |  |  |  | } | 
| 4104 |  |  |  |  |  |  | } | 
| 4105 |  |  |  |  |  |  | } | 
| 4106 | 206 | 100 |  |  |  | 735 | if (%{$$self{DEL_GROUP}}) { | 
|  | 206 |  |  |  |  | 1249 |  | 
| 4107 |  |  |  |  |  |  | # add delete groups to list of edited groups | 
| 4108 | 37 |  |  |  |  | 111 | foreach (keys %{$$self{DEL_GROUP}}) { | 
|  | 37 |  |  |  |  | 337 |  | 
| 4109 | 843 | 100 |  |  |  | 1631 | next if /^-/;   # ignore excluded groups | 
| 4110 | 841 |  |  |  |  | 1175 | my $dirName = $_; | 
| 4111 |  |  |  |  |  |  | # translate necessary group 0 names | 
| 4112 | 841 | 100 |  |  |  | 1706 | $dirName = $translateWriteGroup{$dirName} if $translateWriteGroup{$dirName}; | 
| 4113 |  |  |  |  |  |  | # convert XMP group 1 names | 
| 4114 | 841 | 100 |  |  |  | 1659 | $dirName = 'XMP' if $dirName =~ /^XMP-/; | 
| 4115 | 841 |  |  |  |  | 1134 | my @dirNames; | 
| 4116 | 841 |  |  |  |  | 1448 | while ($dirName) { | 
| 4117 | 1199 |  |  |  |  | 1917 | my $parent = $$fileDirs{$dirName}; | 
| 4118 | 1199 | 100 |  |  |  | 1991 | if (ref $parent) { | 
| 4119 | 13 |  |  |  |  | 60 | push @dirNames, reverse @$parent; | 
| 4120 | 13 |  |  |  |  | 28 | $parent = pop @dirNames; | 
| 4121 |  |  |  |  |  |  | } | 
| 4122 | 1199 |  |  |  |  | 2230 | $$editDirs{$dirName} = $parent; | 
| 4123 | 1199 |  | 100 |  |  | 3419 | $dirName = $parent || shift @dirNames | 
| 4124 |  |  |  |  |  |  | } | 
| 4125 |  |  |  |  |  |  | } | 
| 4126 |  |  |  |  |  |  | } | 
| 4127 |  |  |  |  |  |  | # special case to edit JFIF to get resolutions if editing EXIF information | 
| 4128 | 206 | 100 | 100 |  |  | 1671 | if ($$editDirs{IFD0} and $$fileDirs{JFIF}) { | 
| 4129 | 86 |  |  |  |  | 370 | $$editDirs{JFIF} = 'IFD1'; | 
| 4130 | 86 |  |  |  |  | 296 | $$editDirs{APP0} = undef; | 
| 4131 |  |  |  |  |  |  | } | 
| 4132 |  |  |  |  |  |  |  | 
| 4133 | 206 | 100 |  |  |  | 2513 | if ($$self{OPTIONS}{Verbose}) { | 
| 4134 | 2 |  |  |  |  | 11 | my $out = $$self{OPTIONS}{TextOut}; | 
| 4135 | 2 |  |  |  |  | 10 | print $out "  Editing tags in: "; | 
| 4136 | 2 |  |  |  |  | 17 | foreach (sort keys %$editDirs) { print $out "$_ "; } | 
|  | 10 |  |  |  |  | 25 |  | 
| 4137 | 2 |  |  |  |  | 11 | print $out "\n"; | 
| 4138 | 2 | 50 |  |  |  | 18 | return unless $$self{OPTIONS}{Verbose} > 1; | 
| 4139 | 2 |  |  |  |  | 7 | print $out "  Creating tags in: "; | 
| 4140 | 2 |  |  |  |  | 11 | foreach (sort keys %$addDirs) { print $out "$_ "; } | 
|  | 7 |  |  |  |  | 19 |  | 
| 4141 | 2 |  |  |  |  | 11 | print $out "\n"; | 
| 4142 |  |  |  |  |  |  | } | 
| 4143 |  |  |  |  |  |  | } | 
| 4144 |  |  |  |  |  |  |  | 
| 4145 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4146 |  |  |  |  |  |  | # Write an image directory | 
| 4147 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) source directory information reference | 
| 4148 |  |  |  |  |  |  | #         2) tag table reference, 3) optional reference to writing procedure | 
| 4149 |  |  |  |  |  |  | # Returns: New directory data or undefined on error (or empty string to delete directory) | 
| 4150 |  |  |  |  |  |  | sub WriteDirectory($$$;$) | 
| 4151 |  |  |  |  |  |  | { | 
| 4152 | 1728 |  |  | 1728 | 0 | 6272 | my ($self, $dirInfo, $tagTablePtr, $writeProc) = @_; | 
| 4153 | 1728 |  |  |  |  | 3268 | my ($out, $nvHash, $delFlag); | 
| 4154 |  |  |  |  |  |  |  | 
| 4155 | 1728 | 50 |  |  |  | 4031 | $tagTablePtr or return undef; | 
| 4156 | 1728 | 100 |  |  |  | 6408 | $out = $$self{OPTIONS}{TextOut} if $$self{OPTIONS}{Verbose}; | 
| 4157 |  |  |  |  |  |  | # set directory name from default group0 name if not done already | 
| 4158 | 1728 |  |  |  |  | 3846 | my $dirName = $$dirInfo{DirName}; | 
| 4159 | 1728 |  |  |  |  | 3131 | my $dataPt = $$dirInfo{DataPt}; | 
| 4160 | 1728 |  |  |  |  | 6972 | my $grp0 = $$tagTablePtr{GROUPS}{0}; | 
| 4161 | 1728 | 100 |  |  |  | 4631 | $dirName or $dirName = $$dirInfo{DirName} = $grp0; | 
| 4162 | 1728 | 100 |  |  |  | 2778 | if (%{$$self{DEL_GROUP}}) { | 
|  | 1728 |  |  |  |  | 5829 |  | 
| 4163 | 207 |  |  |  |  | 415 | my $delGroup = $$self{DEL_GROUP}; | 
| 4164 |  |  |  |  |  |  | # delete entire directory if specified | 
| 4165 | 207 |  |  |  |  | 458 | my $grp1 = $dirName; | 
| 4166 | 207 | 100 | 100 |  |  | 900 | $delFlag = ($$delGroup{$grp0} or $$delGroup{$grp1}) unless $permanentDir{$grp0}; | 
| 4167 |  |  |  |  |  |  | # (never delete an entire QuickTime group) | 
| 4168 | 207 | 100 |  |  |  | 614 | if ($delFlag) { | 
| 4169 | 40 | 50 | 100 |  |  | 528 | if (($grp0 =~ /^(MakerNotes)$/ or $grp1 =~ /^(IFD0|ExifIFD|MakerNotes)$/) and | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 4170 |  |  |  |  |  |  | $self->IsRawType() and | 
| 4171 |  |  |  |  |  |  | # allow non-permanent MakerNote directories to be deleted (ie. NikonCapture) | 
| 4172 |  |  |  |  |  |  | (not $$dirInfo{TagInfo} or not defined $$dirInfo{TagInfo}{Permanent} or | 
| 4173 |  |  |  |  |  |  | $$dirInfo{TagInfo}{Permanent})) | 
| 4174 |  |  |  |  |  |  | { | 
| 4175 | 0 |  |  |  |  | 0 | $self->WarnOnce("Can't delete $1 from $$self{FileType}",1); | 
| 4176 | 0 |  |  |  |  | 0 | undef $grp1; | 
| 4177 |  |  |  |  |  |  | } elsif (not $blockExifTypes{$$self{FILE_TYPE}}) { | 
| 4178 |  |  |  |  |  |  | # restrict delete logic to prevent entire tiff image from being killed | 
| 4179 |  |  |  |  |  |  | # (don't allow IFD0 to be deleted, and delete only ExifIFD if EXIF specified) | 
| 4180 | 10 | 50 | 33 |  |  | 156 | if ($$self{FILE_TYPE} eq 'PSD') { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 4181 |  |  |  |  |  |  | # don't delete Photoshop directories from PSD image | 
| 4182 | 0 | 0 |  |  |  | 0 | undef $grp1 if $grp0 eq 'Photoshop'; | 
| 4183 |  |  |  |  |  |  | } elsif ($$self{FILE_TYPE} =~ /^(EPS|PS)$/) { | 
| 4184 |  |  |  |  |  |  | # allow anything to be deleted from PostScript files | 
| 4185 |  |  |  |  |  |  | } elsif ($grp1 eq 'IFD0') { | 
| 4186 | 0 |  | 0 |  |  | 0 | my $type = $$self{TIFF_TYPE} || $$self{FILE_TYPE}; | 
| 4187 | 0 | 0 |  |  |  | 0 | $$delGroup{IFD0} and $self->Warn("Can't delete IFD0 from $type",1); | 
| 4188 | 0 |  |  |  |  | 0 | undef $grp1; | 
| 4189 |  |  |  |  |  |  | } elsif ($grp0 eq 'EXIF' and $$delGroup{$grp0}) { | 
| 4190 | 0 | 0 | 0 |  |  | 0 | undef $grp1 unless $$delGroup{$grp1} or $grp1 eq 'ExifIFD'; | 
| 4191 |  |  |  |  |  |  | } | 
| 4192 |  |  |  |  |  |  | } | 
| 4193 | 40 | 50 |  |  |  | 138 | if ($grp1) { | 
| 4194 | 40 | 100 | 66 |  |  | 202 | if ($dataPt or $$dirInfo{RAF}) { | 
| 4195 | 30 |  |  |  |  | 132 | ++$$self{CHANGED}; | 
| 4196 | 30 | 100 |  |  |  | 134 | $out and print $out "  Deleting $grp1\n"; | 
| 4197 | 30 | 100 |  |  |  | 133 | $self->Warn('ICC_Profile deleted. Image colors may be affected') if $grp1 eq 'ICC_Profile'; | 
| 4198 |  |  |  |  |  |  | # can no longer validate TIFF_END if deleting an entire IFD | 
| 4199 | 30 | 100 |  |  |  | 158 | delete $$self{TIFF_END} if $dirName =~ /IFD/; | 
| 4200 |  |  |  |  |  |  | } | 
| 4201 |  |  |  |  |  |  | # don't add back into the wrong location | 
| 4202 | 40 |  |  |  |  | 124 | my $right = $$self{ADD_DIRS}{$grp1}; | 
| 4203 |  |  |  |  |  |  | # (take care because EXIF directory name may be either EXIF or IFD0, | 
| 4204 |  |  |  |  |  |  | #  but IFD0 will be the one that appears in the directory map) | 
| 4205 | 40 | 100 | 100 |  |  | 214 | $right = $$self{ADD_DIRS}{IFD0} if not $right and $grp1 eq 'EXIF'; | 
| 4206 | 40 | 100 | 100 |  |  | 208 | if ($delFlag == 2 and $right) { | 
| 4207 |  |  |  |  |  |  | # also check grandparent because some routines create 2 levels in 1 | 
| 4208 | 21 |  | 100 |  |  | 121 | my $right2 = $$self{ADD_DIRS}{$right} || ''; | 
| 4209 | 21 |  |  |  |  | 55 | my $parent = $$dirInfo{Parent}; | 
| 4210 | 21 | 50 | 66 |  |  | 122 | if (not $parent or $parent eq $right or $parent eq $right2) { | 
|  |  |  | 33 |  |  |  |  | 
| 4211 |  |  |  |  |  |  | # prevent duplicate directories from being recreated at the same path | 
| 4212 | 21 |  |  |  |  | 44 | my $path = join '-', @{$$self{PATH}}, $dirName; | 
|  | 21 |  |  |  |  | 91 |  | 
| 4213 | 21 | 100 |  |  |  | 91 | $$self{Recreated} or $$self{Recreated} = { }; | 
| 4214 | 21 | 50 |  |  |  | 82 | if ($$self{Recreated}{$path}) { | 
| 4215 | 0 | 0 |  |  |  | 0 | my $p = $parent ? " in $parent" : ''; | 
| 4216 | 0 |  |  |  |  | 0 | $self->Warn("Not recreating duplicate $grp1$p",1); | 
| 4217 | 0 |  |  |  |  | 0 | return ''; | 
| 4218 |  |  |  |  |  |  | } | 
| 4219 | 21 |  |  |  |  | 75 | $$self{Recreated}{$path} = 1; | 
| 4220 |  |  |  |  |  |  | # empty the directory | 
| 4221 | 21 |  |  |  |  | 46 | my $data = ''; | 
| 4222 | 21 |  |  |  |  | 51 | $$dirInfo{DataPt}   = \$data; | 
| 4223 | 21 |  |  |  |  | 49 | $$dirInfo{DataLen}  = 0; | 
| 4224 | 21 |  |  |  |  | 58 | $$dirInfo{DirStart} = 0; | 
| 4225 | 21 |  |  |  |  | 83 | $$dirInfo{DirLen}   = 0; | 
| 4226 | 21 |  |  |  |  | 59 | delete $$dirInfo{RAF}; | 
| 4227 | 21 |  |  |  |  | 48 | delete $$dirInfo{Base}; | 
| 4228 | 21 |  |  |  |  | 57 | delete $$dirInfo{DataPos}; | 
| 4229 |  |  |  |  |  |  | } else { | 
| 4230 | 0 |  |  |  |  | 0 | $self->Warn("Not recreating $grp1 in $parent (should be in $right)",1); | 
| 4231 | 0 |  |  |  |  | 0 | return ''; | 
| 4232 |  |  |  |  |  |  | } | 
| 4233 |  |  |  |  |  |  | } else { | 
| 4234 | 19 | 100 |  |  |  | 138 | return '' unless $$dirInfo{NoDelete}; | 
| 4235 |  |  |  |  |  |  | } | 
| 4236 |  |  |  |  |  |  | } | 
| 4237 |  |  |  |  |  |  | } | 
| 4238 |  |  |  |  |  |  | } | 
| 4239 |  |  |  |  |  |  | # use default proc from tag table if no proc specified | 
| 4240 | 1710 | 100 | 100 |  |  | 8621 | $writeProc or $writeProc = $$tagTablePtr{WRITE_PROC} or return undef; | 
| 4241 |  |  |  |  |  |  |  | 
| 4242 |  |  |  |  |  |  | # are we rewriting a pre-existing directory? | 
| 4243 | 1478 |  | 100 |  |  | 6253 | my $isRewriting = ($$dirInfo{DirLen} or (defined $dataPt and length $$dataPt) or $$dirInfo{RAF}); | 
| 4244 |  |  |  |  |  |  |  | 
| 4245 |  |  |  |  |  |  | # copy or delete new directory as a block if specified | 
| 4246 | 1478 |  |  |  |  | 2700 | my $blockName = $dirName; | 
| 4247 | 1478 | 100 |  |  |  | 3617 | $blockName = 'EXIF' if $blockName eq 'IFD0'; | 
| 4248 | 1478 |  | 100 |  |  | 6294 | my $tagInfo = $Image::ExifTool::Extra{$blockName} || $$dirInfo{TagInfo}; | 
| 4249 | 1478 |  | 100 |  |  | 7927 | while ($tagInfo and ($nvHash = $$self{NEW_VALUE}{$tagInfo}) and | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 4250 |  |  |  |  |  |  | $self->IsOverwriting($nvHash) and not ($$nvHash{CreateOnly} and $isRewriting)) | 
| 4251 |  |  |  |  |  |  | { | 
| 4252 |  |  |  |  |  |  | # protect against writing EXIF to wrong file types, etc | 
| 4253 | 13 | 100 |  |  |  | 76 | if ($blockName eq 'EXIF') { | 
| 4254 | 1 | 50 |  |  |  | 9 | unless ($blockExifTypes{$$self{FILE_TYPE}}) { | 
| 4255 | 0 |  |  |  |  | 0 | $self->Warn("Can't write EXIF as a block to $$self{FILE_TYPE} file"); | 
| 4256 | 0 |  |  |  |  | 0 | last; | 
| 4257 |  |  |  |  |  |  | } | 
| 4258 |  |  |  |  |  |  | # this can happen if we call WriteDirectory for an EXIF directory without going | 
| 4259 |  |  |  |  |  |  | # through WriteTIFF as the WriteProc (which happens if conditionally replacing | 
| 4260 |  |  |  |  |  |  | # the EXIF block and the condition fails), but we never want to do a block write | 
| 4261 |  |  |  |  |  |  | # in this case because the EXIF block would end up with two TIFF headers | 
| 4262 | 1 | 50 |  |  |  | 7 | last unless $writeProc eq \&Image::ExifTool::WriteTIFF; | 
| 4263 |  |  |  |  |  |  | } | 
| 4264 | 13 | 100 |  |  |  | 79 | last unless $self->IsOverwriting($nvHash, $dataPt ? $$dataPt : ''); | 
|  |  | 50 |  |  |  |  |  | 
| 4265 | 13 |  |  |  |  | 54 | my $verb = 'Writing'; | 
| 4266 | 13 |  |  |  |  | 53 | my $newVal = $self->GetNewValue($nvHash); | 
| 4267 | 13 | 50 | 33 |  |  | 142 | unless (defined $newVal and length $newVal) { | 
| 4268 | 0 | 0 | 0 |  |  | 0 | return '' unless $dataPt or $$dirInfo{RAF}; # nothing to do if block never existed | 
| 4269 |  |  |  |  |  |  | # don't allow MakerNotes to be removed from RAW files | 
| 4270 | 0 | 0 | 0 |  |  | 0 | if ($blockName eq 'MakerNotes' and $rawType{$$self{FileType}}) { | 
| 4271 | 0 |  |  |  |  | 0 | $self->Warn("Can't delete MakerNotes from $$self{FileType}",1); | 
| 4272 | 0 |  |  |  |  | 0 | return undef; | 
| 4273 |  |  |  |  |  |  | } | 
| 4274 | 0 |  |  |  |  | 0 | $verb = 'Deleting'; | 
| 4275 | 0 |  |  |  |  | 0 | $newVal = ''; | 
| 4276 |  |  |  |  |  |  | } | 
| 4277 | 13 |  |  |  |  | 50 | $$dirInfo{BlockWrite} = 1;  # set flag indicating we did a block write | 
| 4278 | 13 | 50 |  |  |  | 56 | $out and print $out "  $verb $blockName as a block\n"; | 
| 4279 | 13 |  |  |  |  | 41 | ++$$self{CHANGED}; | 
| 4280 | 13 |  |  |  |  | 74 | return $newVal; | 
| 4281 |  |  |  |  |  |  | } | 
| 4282 |  |  |  |  |  |  | # guard against writing the same directory twice | 
| 4283 | 1465 | 100 | 100 |  |  | 10407 | if (defined $dataPt and defined $$dirInfo{DirStart} and defined $$dirInfo{DataPos} and | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 4284 |  |  |  |  |  |  | not $$dirInfo{NoRefTest}) | 
| 4285 |  |  |  |  |  |  | { | 
| 4286 | 691 |  | 100 |  |  | 3193 | my $addr = $$dirInfo{DirStart} + $$dirInfo{DataPos} + ($$dirInfo{Base}||0) + $$self{BASE}; | 
| 4287 |  |  |  |  |  |  | # (Phase One P25 IIQ files have ICC_Profile duplicated in IFD0 and IFD1) | 
| 4288 | 691 | 50 | 0 |  |  | 3232 | if ($$self{PROCESSED}{$addr} and ($dirName ne 'ICC_Profile' or $$self{TIFF_TYPE} ne 'IIQ')) { | 
|  |  |  | 33 |  |  |  |  | 
| 4289 | 0 | 0 | 0 |  |  | 0 | if (defined $$dirInfo{DirLen} and not $$dirInfo{DirLen} and $dirName ne $$self{PROCESSED}{$addr}) { | 
|  |  | 0 | 0 |  |  |  |  | 
| 4290 |  |  |  |  |  |  | # it is hypothetically possible to have 2 different directories | 
| 4291 |  |  |  |  |  |  | # with the same address if one has a length of zero | 
| 4292 |  |  |  |  |  |  | } elsif ($self->Error("$dirName pointer references previous $$self{PROCESSED}{$addr} directory", 2)) { | 
| 4293 | 0 |  |  |  |  | 0 | return undef; | 
| 4294 |  |  |  |  |  |  | } else { | 
| 4295 | 0 |  |  |  |  | 0 | $self->Warn("Deleting duplicate $dirName directory"); | 
| 4296 | 0 | 0 |  |  |  | 0 | $out and print $out "  Deleting $dirName\n"; | 
| 4297 |  |  |  |  |  |  | # delete the duplicate directory (don't recreate it when writing new | 
| 4298 |  |  |  |  |  |  | # tags to prevent propagating a duplicate IFD in cases like when the | 
| 4299 |  |  |  |  |  |  | # same ExifIFD exists in both IFD0 and IFD1) | 
| 4300 | 0 |  |  |  |  | 0 | return ''; | 
| 4301 |  |  |  |  |  |  | } | 
| 4302 |  |  |  |  |  |  | } else { | 
| 4303 | 691 |  |  |  |  | 2588 | $$self{PROCESSED}{$addr} = $dirName; | 
| 4304 |  |  |  |  |  |  | } | 
| 4305 |  |  |  |  |  |  | } | 
| 4306 | 1465 |  |  |  |  | 3420 | my $oldDir = $$self{DIR_NAME}; | 
| 4307 | 1465 |  |  |  |  | 4608 | my @save = @$self{'Compression','SubfileType'}; | 
| 4308 | 1465 |  |  |  |  | 2391 | my $name; | 
| 4309 | 1465 | 100 |  |  |  | 3417 | if ($out) { | 
| 4310 |  |  |  |  |  |  | $name = ($dirName eq 'MakerNotes' and $$dirInfo{TagInfo}) ? | 
| 4311 | 4 | 50 | 33 |  |  | 24 | $$dirInfo{TagInfo}{Name} : $dirName; | 
| 4312 | 4 | 100 | 100 |  |  | 27 | if (not defined $oldDir or $oldDir ne $name) { | 
| 4313 | 3 | 100 |  |  |  | 12 | my $verb = $isRewriting ? 'Rewriting' : 'Creating'; | 
| 4314 | 3 |  |  |  |  | 17 | print $out "  $verb $name\n"; | 
| 4315 |  |  |  |  |  |  | } | 
| 4316 |  |  |  |  |  |  | } | 
| 4317 | 1465 |  |  |  |  | 4413 | my $saveOrder = GetByteOrder(); | 
| 4318 | 1465 |  |  |  |  | 3235 | my $oldChanged = $$self{CHANGED}; | 
| 4319 | 1465 |  |  |  |  | 3181 | $$self{DIR_NAME} = $dirName; | 
| 4320 | 1465 |  |  |  |  | 2314 | push @{$$self{PATH}}, $dirName; | 
|  | 1465 |  |  |  |  | 3809 |  | 
| 4321 | 1465 |  |  |  |  | 3126 | $$dirInfo{IsWriting} = 1; | 
| 4322 | 1465 |  |  |  |  | 2373 | my $newData; | 
| 4323 |  |  |  |  |  |  | { | 
| 4324 | 59 |  |  | 59 |  | 616 | no strict 'refs'; | 
|  | 59 |  |  |  |  | 222 |  | 
|  | 59 |  |  |  |  | 1277738 |  | 
|  | 1465 |  |  |  |  | 2351 |  | 
| 4325 | 1465 |  |  |  |  | 12907 | $newData = &$writeProc($self, $dirInfo, $tagTablePtr); | 
| 4326 |  |  |  |  |  |  | } | 
| 4327 | 1465 |  |  |  |  | 2991 | pop @{$$self{PATH}}; | 
|  | 1465 |  |  |  |  | 3617 |  | 
| 4328 |  |  |  |  |  |  | # nothing changed if error occurred or nothing was created | 
| 4329 | 1465 | 100 | 100 |  |  | 7137 | $$self{CHANGED} = $oldChanged unless defined $newData and (length($newData) or $isRewriting); | 
|  |  |  | 100 |  |  |  |  | 
| 4330 | 1465 |  |  |  |  | 3393 | $$self{DIR_NAME} = $oldDir; | 
| 4331 | 1465 |  |  |  |  | 4321 | @$self{'Compression','SubfileType'} = @save; | 
| 4332 | 1465 |  |  |  |  | 5743 | SetByteOrder($saveOrder); | 
| 4333 | 1465 | 100 |  |  |  | 4153 | if ($out) { | 
| 4334 | 4 | 50 | 33 |  |  | 29 | print $out "  Deleting $name\n" if defined $newData and not length $newData; | 
| 4335 | 4 | 50 | 33 |  |  | 19 | if ($$self{CHANGED} == $oldChanged and $$self{OPTIONS}{Verbose} > 2) { | 
| 4336 | 0 |  |  |  |  | 0 | print $out "$$self{INDENT}  [nothing changed in $name]\n"; | 
| 4337 |  |  |  |  |  |  | } | 
| 4338 |  |  |  |  |  |  | } | 
| 4339 | 1465 |  |  |  |  | 7201 | return $newData; | 
| 4340 |  |  |  |  |  |  | } | 
| 4341 |  |  |  |  |  |  |  | 
| 4342 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4343 |  |  |  |  |  |  | # Uncommon utility routines to for reading binary data values | 
| 4344 |  |  |  |  |  |  | # Inputs: 0) data reference, 1) offset into data | 
| 4345 |  |  |  |  |  |  | sub Get64s($$) | 
| 4346 |  |  |  |  |  |  | { | 
| 4347 | 12 |  |  | 12 | 0 | 26 | my ($dataPt, $pos) = @_; | 
| 4348 | 12 | 50 |  |  |  | 28 | my $pt = GetByteOrder() eq 'MM' ? 0 : 4;    # get position of high word | 
| 4349 | 12 |  |  |  |  | 34 | my $hi = Get32s($dataPt, $pos + $pt);       # preserve sign bit of high word | 
| 4350 | 12 |  |  |  |  | 33 | my $lo = Get32u($dataPt, $pos + 4 - $pt); | 
| 4351 | 12 |  |  |  |  | 31 | return $hi * 4294967296 + $lo; | 
| 4352 |  |  |  |  |  |  | } | 
| 4353 |  |  |  |  |  |  | sub Get64u($$) | 
| 4354 |  |  |  |  |  |  | { | 
| 4355 | 189 |  |  | 189 | 0 | 470 | my ($dataPt, $pos) = @_; | 
| 4356 | 189 | 100 |  |  |  | 487 | my $pt = GetByteOrder() eq 'MM' ? 0 : 4;    # get position of high word | 
| 4357 | 189 |  |  |  |  | 646 | my $hi = Get32u($dataPt, $pos + $pt);       # (unsigned this time) | 
| 4358 | 189 |  |  |  |  | 612 | my $lo = Get32u($dataPt, $pos + 4 - $pt); | 
| 4359 | 189 |  |  |  |  | 777 | return $hi * 4294967296 + $lo; | 
| 4360 |  |  |  |  |  |  | } | 
| 4361 |  |  |  |  |  |  | sub GetFixed64s($$) | 
| 4362 |  |  |  |  |  |  | { | 
| 4363 | 0 |  |  | 0 | 0 | 0 | my ($dataPt, $pos) = @_; | 
| 4364 | 0 |  |  |  |  | 0 | my $val = Get64s($dataPt, $pos) / 4294967296; | 
| 4365 |  |  |  |  |  |  | # remove insignificant digits | 
| 4366 | 0 | 0 |  |  |  | 0 | return int($val * 1e10 + ($val>0 ? 0.5 : -0.5)) / 1e10; | 
| 4367 |  |  |  |  |  |  | } | 
| 4368 |  |  |  |  |  |  | # Decode extended 80-bit float used by Apple SANE and Intel 8087 | 
| 4369 |  |  |  |  |  |  | # (note: different than the IEEE standard 80-bit float) | 
| 4370 |  |  |  |  |  |  | sub GetExtended($$) | 
| 4371 |  |  |  |  |  |  | { | 
| 4372 | 1 |  |  | 1 | 0 | 4 | my ($dataPt, $pos) = @_; | 
| 4373 | 1 | 50 |  |  |  | 5 | my $pt = GetByteOrder() eq 'MM' ? 0 : 2;    # get position of exponent | 
| 4374 | 1 |  |  |  |  | 5 | my $exp = Get16u($dataPt, $pos + $pt); | 
| 4375 | 1 |  |  |  |  | 4 | my $sig = Get64u($dataPt, $pos + 2 - $pt);  # get significand as int64u | 
| 4376 | 1 | 50 |  |  |  | 4 | my $sign = $exp & 0x8000 ? -1 : 1; | 
| 4377 | 1 |  |  |  |  | 5 | $exp = ($exp & 0x7fff) - 16383 - 63; # (-63 to fractionalize significand) | 
| 4378 | 1 |  |  |  |  | 18 | return $sign * $sig * 2 ** $exp; | 
| 4379 |  |  |  |  |  |  | } | 
| 4380 |  |  |  |  |  |  |  | 
| 4381 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4382 |  |  |  |  |  |  | # Dump data in hex and ASCII to console | 
| 4383 |  |  |  |  |  |  | # Inputs: 0) data reference, 1) length or undef, 2-N) Options: | 
| 4384 |  |  |  |  |  |  | # Options: Start => offset to start of data (default=0) | 
| 4385 |  |  |  |  |  |  | #          Addr => address to print for data start (default=DataPos+Base+Start) | 
| 4386 |  |  |  |  |  |  | #          DataPos => position of data within block (relative to Base) | 
| 4387 |  |  |  |  |  |  | #          Base => base offset for pointers from start of file | 
| 4388 |  |  |  |  |  |  | #          Width => width of printout (bytes, default=16) | 
| 4389 |  |  |  |  |  |  | #          Prefix => prefix to print at start of line (default='') | 
| 4390 |  |  |  |  |  |  | #          MaxLen => maximum length to dump | 
| 4391 |  |  |  |  |  |  | #          Out => output file reference | 
| 4392 |  |  |  |  |  |  | #          Len => data length | 
| 4393 |  |  |  |  |  |  | sub HexDump($;$%) | 
| 4394 |  |  |  |  |  |  | { | 
| 4395 | 169 |  |  | 169 | 0 | 276 | my $dataPt = shift; | 
| 4396 | 169 |  |  |  |  | 316 | my $len    = shift; | 
| 4397 | 169 |  |  |  |  | 781 | my %opts   = @_; | 
| 4398 | 169 |  | 100 |  |  | 429 | my $start  = $opts{Start}  || 0; | 
| 4399 | 169 |  |  |  |  | 259 | my $addr   = $opts{Addr}; | 
| 4400 | 169 |  | 50 |  |  | 485 | my $wid    = $opts{Width}  || 16; | 
| 4401 | 169 |  | 100 |  |  | 363 | my $prefix = $opts{Prefix} || ''; | 
| 4402 | 169 |  | 50 |  |  | 361 | my $out    = $opts{Out}    || \*STDOUT; | 
| 4403 | 169 |  |  |  |  | 306 | my $maxLen = $opts{MaxLen}; | 
| 4404 | 169 |  |  |  |  | 326 | my $datLen = length($$dataPt) - $start; | 
| 4405 | 169 |  |  |  |  | 240 | my $more; | 
| 4406 | 169 | 50 |  |  |  | 378 | $len = $opts{Len} if defined $opts{Len}; | 
| 4407 |  |  |  |  |  |  |  | 
| 4408 | 169 | 100 | 50 |  |  | 506 | $addr = $start + ($opts{DataPos} || 0) + ($opts{Base} || 0) unless defined $addr; | 
|  |  |  | 50 |  |  |  |  | 
| 4409 | 169 | 100 |  |  |  | 350 | $len = $datLen unless defined $len; | 
| 4410 | 169 | 100 | 66 |  |  | 532 | if ($maxLen and $len > $maxLen) { | 
| 4411 |  |  |  |  |  |  | # print one line less to allow for $more line below | 
| 4412 | 5 |  |  |  |  | 16 | $maxLen = int(($maxLen - 1) / $wid) * $wid; | 
| 4413 | 5 |  |  |  |  | 8 | $more = $len - $maxLen; | 
| 4414 | 5 |  |  |  |  | 12 | $len = $maxLen; | 
| 4415 |  |  |  |  |  |  | } | 
| 4416 | 169 | 50 |  |  |  | 338 | if ($len > $datLen) { | 
| 4417 | 0 |  |  |  |  | 0 | print $out "$prefix    Warning: Attempted dump outside data\n"; | 
| 4418 | 0 |  |  |  |  | 0 | print $out "$prefix    ($len bytes specified, but only $datLen available)\n"; | 
| 4419 | 0 |  |  |  |  | 0 | $len = $datLen; | 
| 4420 |  |  |  |  |  |  | } | 
| 4421 | 169 |  |  |  |  | 549 | my $format = sprintf("%%-%ds", $wid * 3); | 
| 4422 | 169 |  |  |  |  | 383 | my $tmpl = 'H2' x $wid; # ('(H2)*' would have been nice, but older perl versions don't support it) | 
| 4423 | 169 |  |  |  |  | 265 | my $i; | 
| 4424 | 169 |  |  |  |  | 392 | for ($i=0; $i<$len; $i+=$wid) { | 
| 4425 | 228 | 100 |  |  |  | 609 | $wid > $len-$i and $wid = $len-$i, $tmpl = 'H2' x $wid; | 
| 4426 | 228 |  |  |  |  | 757 | printf $out "$prefix%8.4x: ", $addr+$i; | 
| 4427 | 228 |  |  |  |  | 563 | my $dat = substr($$dataPt, $i+$start, $wid); | 
| 4428 | 228 |  |  |  |  | 1074 | my $s = join(' ', unpack($tmpl, $dat)); | 
| 4429 | 228 |  |  |  |  | 764 | printf $out $format, $s; | 
| 4430 | 228 |  |  |  |  | 427 | $dat =~ tr /\x00-\x1f\x7f-\xff/./; | 
| 4431 | 228 |  |  |  |  | 701 | print $out "[$dat]\n"; | 
| 4432 |  |  |  |  |  |  | } | 
| 4433 | 169 | 100 |  |  |  | 1147 | $more and print $out "$prefix    [snip $more bytes]\n"; | 
| 4434 |  |  |  |  |  |  | } | 
| 4435 |  |  |  |  |  |  |  | 
| 4436 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4437 |  |  |  |  |  |  | # Print verbose tag information | 
| 4438 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) tag ID | 
| 4439 |  |  |  |  |  |  | #         2) tag info reference (or undef) | 
| 4440 |  |  |  |  |  |  | #         3-N) extra parms: | 
| 4441 |  |  |  |  |  |  | # Parms: Index => Index of tag in menu (starting at 0) | 
| 4442 |  |  |  |  |  |  | #        Value => Tag value | 
| 4443 |  |  |  |  |  |  | #        DataPt => reference to value data block | 
| 4444 |  |  |  |  |  |  | #        DataPos => location of data block in file | 
| 4445 |  |  |  |  |  |  | #        Base => base added to all offsets | 
| 4446 |  |  |  |  |  |  | #        Size => length of value data within block | 
| 4447 |  |  |  |  |  |  | #        Format => value format string | 
| 4448 |  |  |  |  |  |  | #        Count => number of values | 
| 4449 |  |  |  |  |  |  | #        Extra => Extra Verbose=2 information to put after tag number | 
| 4450 |  |  |  |  |  |  | #        Table => Reference to tag table | 
| 4451 |  |  |  |  |  |  | #        --> plus any of these HexDump() options: Start, Addr, Width | 
| 4452 |  |  |  |  |  |  | sub VerboseInfo($$$%) | 
| 4453 |  |  |  |  |  |  | { | 
| 4454 | 617 |  |  | 617 | 0 | 3487 | my ($self, $tagID, $tagInfo, %parms) = @_; | 
| 4455 | 617 |  |  |  |  | 1471 | my $verbose = $$self{OPTIONS}{Verbose}; | 
| 4456 | 617 |  |  |  |  | 1104 | my $out = $$self{OPTIONS}{TextOut}; | 
| 4457 | 617 |  |  |  |  | 966 | my ($tag, $line, $hexID); | 
| 4458 |  |  |  |  |  |  |  | 
| 4459 |  |  |  |  |  |  | # generate hex number if tagID is numerical | 
| 4460 | 617 | 100 |  |  |  | 1172 | if (defined $tagID) { | 
| 4461 | 578 | 100 |  |  |  | 3880 | $tagID =~ /^\d+$/ and $hexID = sprintf("0x%.4x", $tagID); | 
| 4462 |  |  |  |  |  |  | } else { | 
| 4463 | 39 |  |  |  |  | 67 | $tagID = 'Unknown'; | 
| 4464 |  |  |  |  |  |  | } | 
| 4465 |  |  |  |  |  |  | # get tag name | 
| 4466 | 617 | 50 | 33 |  |  | 2747 | if ($tagInfo and $$tagInfo{Name}) { | 
| 4467 | 617 |  |  |  |  | 1248 | $tag = $$tagInfo{Name}; | 
| 4468 |  |  |  |  |  |  | } else { | 
| 4469 | 0 |  |  |  |  | 0 | my $prefix; | 
| 4470 | 0 | 0 |  |  |  | 0 | $prefix = $parms{Table}{TAG_PREFIX} if $parms{Table}; | 
| 4471 | 0 | 0 | 0 |  |  | 0 | if ($prefix or $hexID) { | 
| 4472 | 0 | 0 |  |  |  | 0 | $prefix = 'Unknown' unless $prefix; | 
| 4473 | 0 | 0 |  |  |  | 0 | $tag = $prefix . '_' . ($hexID ? $hexID : $tagID); | 
| 4474 |  |  |  |  |  |  | } else { | 
| 4475 | 0 |  |  |  |  | 0 | $tag = $tagID; | 
| 4476 |  |  |  |  |  |  | } | 
| 4477 |  |  |  |  |  |  | } | 
| 4478 | 617 |  |  |  |  | 1077 | my $dataPt = $parms{DataPt}; | 
| 4479 | 617 |  |  |  |  | 1033 | my $size = $parms{Size}; | 
| 4480 | 617 | 50 | 66 |  |  | 1604 | $size = length $$dataPt unless defined $size or not $dataPt; | 
| 4481 | 617 |  |  |  |  | 1181 | my $indent = $$self{INDENT}; | 
| 4482 |  |  |  |  |  |  |  | 
| 4483 |  |  |  |  |  |  | # Level 1: print tag/value information | 
| 4484 | 617 |  |  |  |  | 955 | $line = $indent; | 
| 4485 | 617 |  |  |  |  | 1008 | my $index = $parms{Index}; | 
| 4486 | 617 | 100 |  |  |  | 1268 | if (defined $index) { | 
| 4487 | 365 |  |  |  |  | 705 | $line .= $index . ') '; | 
| 4488 | 365 | 100 |  |  |  | 826 | $line .= ' ' if length($index) < 2; | 
| 4489 | 365 |  |  |  |  | 570 | $indent .= '    '; # indent everything else to align with tag name | 
| 4490 |  |  |  |  |  |  | } | 
| 4491 | 617 |  |  |  |  | 1084 | $line .= $tag; | 
| 4492 | 617 | 100 | 66 |  |  | 2161 | if ($tagInfo and $$tagInfo{SubDirectory}) { | 
| 4493 | 39 |  |  |  |  | 72 | $line .= ' (SubDirectory) -->'; | 
| 4494 |  |  |  |  |  |  | } else { | 
| 4495 | 578 |  |  |  |  | 1016 | my $maxLen = 90 - length($line); | 
| 4496 | 578 |  |  |  |  | 907 | my $val = $parms{Value}; | 
| 4497 | 578 | 50 |  |  |  | 1101 | if (defined $val) { | 
|  |  | 0 |  |  |  |  |  | 
| 4498 | 578 | 50 |  |  |  | 1224 | $val = '[' . join(',',@$val) . ']' if ref $val eq 'ARRAY'; | 
| 4499 | 578 |  |  |  |  | 1750 | $line .= ' = ' . $self->Printable($val, $maxLen); | 
| 4500 |  |  |  |  |  |  | } elsif ($dataPt) { | 
| 4501 | 0 |  | 0 |  |  | 0 | my $start = $parms{Start} || 0; | 
| 4502 | 0 |  |  |  |  | 0 | $line .= ' = ' . $self->Printable(substr($$dataPt,$start,$size), $maxLen); | 
| 4503 |  |  |  |  |  |  | } | 
| 4504 |  |  |  |  |  |  | } | 
| 4505 | 617 |  |  |  |  | 1912 | print $out "$line\n"; | 
| 4506 |  |  |  |  |  |  |  | 
| 4507 |  |  |  |  |  |  | # Level 2: print detailed information about the tag | 
| 4508 | 617 | 50 | 66 |  |  | 2995 | if ($verbose > 1 and ($parms{Extra} or $parms{Format} or | 
|  |  |  | 66 |  |  |  |  | 
| 4509 |  |  |  |  |  |  | $parms{DataPt} or defined $size or $tagID =~ /\//)) | 
| 4510 |  |  |  |  |  |  | { | 
| 4511 | 390 |  |  |  |  | 700 | $line = $indent . '- Tag '; | 
| 4512 | 390 | 100 |  |  |  | 759 | if ($hexID) { | 
| 4513 | 389 |  |  |  |  | 575 | $line .= $hexID; | 
| 4514 |  |  |  |  |  |  | } else { | 
| 4515 | 1 |  |  |  |  | 7 | $tagID =~ s/([\0-\x1f\x7f-\xff])/sprintf('\\x%.2x',ord $1)/ge; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4516 | 1 |  |  |  |  | 3 | $line .= "'${tagID}'"; | 
| 4517 |  |  |  |  |  |  | } | 
| 4518 | 390 | 50 |  |  |  | 793 | $line .= $parms{Extra} if defined $parms{Extra}; | 
| 4519 | 390 |  |  |  |  | 622 | my $format = $parms{Format}; | 
| 4520 | 390 | 50 | 66 |  |  | 986 | if ($format or defined $size) { | 
| 4521 | 390 |  |  |  |  | 633 | $line .= ' ('; | 
| 4522 | 390 | 50 |  |  |  | 751 | if (defined $size) { | 
| 4523 | 390 |  |  |  |  | 685 | $line .= "$size bytes"; | 
| 4524 | 390 | 100 |  |  |  | 820 | $line .= ', ' if $format; | 
| 4525 |  |  |  |  |  |  | } | 
| 4526 | 390 | 100 |  |  |  | 732 | if ($format) { | 
| 4527 | 352 |  |  |  |  | 496 | $line .= $format; | 
| 4528 | 352 | 50 |  |  |  | 929 | $line .= '['.$parms{Count}.']' if $parms{Count}; | 
| 4529 |  |  |  |  |  |  | } | 
| 4530 | 390 |  |  |  |  | 613 | $line .= ')'; | 
| 4531 |  |  |  |  |  |  | } | 
| 4532 | 390 | 50 | 66 |  |  | 1062 | $line .= ':' if $verbose > 2 and $parms{DataPt}; | 
| 4533 | 390 |  |  |  |  | 898 | print $out "$line\n"; | 
| 4534 |  |  |  |  |  |  | } | 
| 4535 |  |  |  |  |  |  |  | 
| 4536 |  |  |  |  |  |  | # Level 3: do hex dump of value | 
| 4537 | 617 | 100 | 100 |  |  | 2985 | if ($verbose > 2 and $parms{DataPt} and (not $tagInfo or not $$tagInfo{ReadFromRAF})) { | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 4538 | 165 |  |  |  |  | 355 | $parms{Out} = $out; | 
| 4539 | 165 |  |  |  |  | 333 | $parms{Prefix} = $indent; | 
| 4540 |  |  |  |  |  |  | # limit dump length if Verbose < 5 | 
| 4541 | 165 | 50 |  |  |  | 493 | $parms{MaxLen} = $verbose == 3 ? 96 : 2048 if $verbose < 5; | 
|  |  | 50 |  |  |  |  |  | 
| 4542 | 165 |  |  |  |  | 1115 | HexDump($dataPt, $size, %parms); | 
| 4543 |  |  |  |  |  |  | } | 
| 4544 |  |  |  |  |  |  | } | 
| 4545 |  |  |  |  |  |  |  | 
| 4546 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4547 |  |  |  |  |  |  | # Dump trailer information | 
| 4548 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) dirInfo hash (RAF, DirName, DataPos, DirLen) | 
| 4549 |  |  |  |  |  |  | # Notes: Restores current file position before returning | 
| 4550 |  |  |  |  |  |  | sub DumpTrailer($$) | 
| 4551 |  |  |  |  |  |  | { | 
| 4552 | 1 |  |  | 1 | 0 | 5 | my ($self, $dirInfo) = @_; | 
| 4553 | 1 |  |  |  |  | 5 | my $raf = $$dirInfo{RAF}; | 
| 4554 | 1 |  |  |  |  | 5 | my $curPos = $raf->Tell(); | 
| 4555 | 1 |  | 50 |  |  | 6 | my $trailer = $$dirInfo{DirName} || 'Unknown'; | 
| 4556 | 1 |  |  |  |  | 4 | my $pos = $$dirInfo{DataPos}; | 
| 4557 | 1 |  |  |  |  | 4 | my $verbose = $$self{OPTIONS}{Verbose}; | 
| 4558 | 1 |  |  |  |  | 4 | my $htmlDump = $$self{HTML_DUMP}; | 
| 4559 | 1 |  |  |  |  | 2 | my ($buff, $buf2); | 
| 4560 | 1 |  |  |  |  | 3 | my $size = $$dirInfo{DirLen}; | 
| 4561 | 1 | 50 |  |  |  | 6 | $pos = $curPos unless defined $pos; | 
| 4562 |  |  |  |  |  |  |  | 
| 4563 |  |  |  |  |  |  | # get full trailer size if not specified | 
| 4564 | 1 |  |  |  |  | 3 | for (;;) { | 
| 4565 | 1 | 50 |  |  |  | 5 | unless ($size) { | 
| 4566 | 0 | 0 |  |  |  | 0 | $raf->Seek(0, 2) or last; | 
| 4567 | 0 |  |  |  |  | 0 | $size = $raf->Tell() - $pos; | 
| 4568 | 0 | 0 |  |  |  | 0 | last unless $size; | 
| 4569 |  |  |  |  |  |  | } | 
| 4570 | 1 | 50 |  |  |  | 6 | $raf->Seek($pos, 0) or last; | 
| 4571 | 1 | 50 |  |  |  | 6 | if ($htmlDump) { | 
| 4572 | 0 | 0 |  |  |  | 0 | my $num = $raf->Read($buff, $size) or return; | 
| 4573 | 0 |  |  |  |  | 0 | my $desc = "$trailer trailer"; | 
| 4574 | 0 | 0 |  |  |  | 0 | $desc = "[$desc]" if $trailer eq 'Unknown'; | 
| 4575 | 0 |  |  |  |  | 0 | $self->HDump($pos, $num, $desc, undef, 0x08); | 
| 4576 | 0 |  |  |  |  | 0 | last; | 
| 4577 |  |  |  |  |  |  | } | 
| 4578 | 1 |  |  |  |  | 4 | my $out = $$self{OPTIONS}{TextOut}; | 
| 4579 | 1 |  |  |  |  | 12 | printf $out "$trailer trailer (%d bytes at offset 0x%.4x):\n", $size, $pos; | 
| 4580 | 1 | 50 |  |  |  | 7 | last unless $verbose > 2; | 
| 4581 | 0 |  |  |  |  | 0 | my $num = $size;    # number of bytes to read | 
| 4582 |  |  |  |  |  |  | # limit size if not very verbose | 
| 4583 | 0 | 0 |  |  |  | 0 | if ($verbose < 5) { | 
| 4584 | 0 | 0 |  |  |  | 0 | my $limit = $verbose < 4 ? 96 : 512; | 
| 4585 | 0 | 0 |  |  |  | 0 | $num = $limit if $num > $limit; | 
| 4586 |  |  |  |  |  |  | } | 
| 4587 | 0 | 0 |  |  |  | 0 | $raf->Read($buff, $num) == $num or return; | 
| 4588 |  |  |  |  |  |  | # read the end of the trailer too if not done already | 
| 4589 | 0 | 0 |  |  |  | 0 | if ($size > 2 * $num) { | 
|  |  | 0 |  |  |  |  |  | 
| 4590 | 0 |  |  |  |  | 0 | $raf->Seek($pos + $size - $num, 0); | 
| 4591 | 0 |  |  |  |  | 0 | $raf->Read($buf2, $num); | 
| 4592 |  |  |  |  |  |  | } elsif ($size > $num) { | 
| 4593 | 0 |  |  |  |  | 0 | $raf->Seek($pos + $num, 0); | 
| 4594 | 0 |  |  |  |  | 0 | $raf->Read($buf2, $size - $num); | 
| 4595 | 0 |  |  |  |  | 0 | $buff .= $buf2; | 
| 4596 | 0 |  |  |  |  | 0 | undef $buf2; | 
| 4597 |  |  |  |  |  |  | } | 
| 4598 | 0 |  |  |  |  | 0 | HexDump(\$buff, undef, Addr => $pos, Out => $out); | 
| 4599 | 0 | 0 |  |  |  | 0 | if (defined $buf2) { | 
| 4600 | 0 |  |  |  |  | 0 | print $out "    [snip ", $size - $num * 2, " bytes]\n"; | 
| 4601 | 0 |  |  |  |  | 0 | HexDump(\$buf2, undef, Addr => $pos + $size - $num, Out => $out); | 
| 4602 |  |  |  |  |  |  | } | 
| 4603 | 0 |  |  |  |  | 0 | last; | 
| 4604 |  |  |  |  |  |  | } | 
| 4605 | 1 |  |  |  |  | 7 | $raf->Seek($curPos, 0); | 
| 4606 |  |  |  |  |  |  | } | 
| 4607 |  |  |  |  |  |  |  | 
| 4608 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4609 |  |  |  |  |  |  | # Dump unknown trailer information | 
| 4610 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) dirInfo ref (with RAF, DataPos and DirLen defined) | 
| 4611 |  |  |  |  |  |  | # Notes: changes dirInfo elements | 
| 4612 |  |  |  |  |  |  | sub DumpUnknownTrailer($$) | 
| 4613 |  |  |  |  |  |  | { | 
| 4614 | 0 |  |  | 0 | 0 | 0 | my ($self, $dirInfo) = @_; | 
| 4615 | 0 |  |  |  |  | 0 | my $pos = $$dirInfo{DataPos}; | 
| 4616 | 0 |  |  |  |  | 0 | my $endPos = $pos + $$dirInfo{DirLen}; | 
| 4617 |  |  |  |  |  |  | # account for preview/MPF image trailer | 
| 4618 | 0 |  | 0 |  |  | 0 | my $prePos = $$self{VALUE}{PreviewImageStart} || $$self{PreviewImageStart}; | 
| 4619 | 0 |  | 0 |  |  | 0 | my $preLen = $$self{VALUE}{PreviewImageLength} || $$self{PreviewImageLength}; | 
| 4620 | 0 |  |  |  |  | 0 | my $tag = 'PreviewImage'; | 
| 4621 | 0 |  |  |  |  | 0 | my $mpImageNum = 0; | 
| 4622 | 0 |  |  |  |  | 0 | my (%image, $lastOne); | 
| 4623 | 0 |  |  |  |  | 0 | for (;;) { | 
| 4624 |  |  |  |  |  |  | # add to Preview block list if valid and in the trailer | 
| 4625 | 0 | 0 | 0 |  |  | 0 | $image{$prePos} = [$tag, $preLen] if $prePos and $preLen and $prePos+$preLen > $pos; | 
|  |  |  | 0 |  |  |  |  | 
| 4626 | 0 | 0 |  |  |  | 0 | last if $lastOne;   # checked all images | 
| 4627 |  |  |  |  |  |  | # look for MPF images (in the proper order) | 
| 4628 | 0 |  |  |  |  | 0 | ++$mpImageNum; | 
| 4629 | 0 |  |  |  |  | 0 | $prePos = $$self{VALUE}{"MPImageStart ($mpImageNum)"}; | 
| 4630 | 0 | 0 |  |  |  | 0 | if (defined $prePos) { | 
| 4631 | 0 |  |  |  |  | 0 | $preLen = $$self{VALUE}{"MPImageLength ($mpImageNum)"}; | 
| 4632 |  |  |  |  |  |  | } else { | 
| 4633 | 0 |  |  |  |  | 0 | $prePos = $$self{VALUE}{'MPImageStart'}; | 
| 4634 | 0 |  |  |  |  | 0 | $preLen = $$self{VALUE}{'MPImageLength'}; | 
| 4635 | 0 |  |  |  |  | 0 | $lastOne = 1; | 
| 4636 |  |  |  |  |  |  | } | 
| 4637 | 0 |  |  |  |  | 0 | $tag = "MPImage$mpImageNum"; | 
| 4638 |  |  |  |  |  |  | } | 
| 4639 |  |  |  |  |  |  | # dump trailer sections in order | 
| 4640 | 0 |  |  |  |  | 0 | $image{$endPos} = [ '', 0 ];    # add terminator "image" | 
| 4641 | 0 |  |  |  |  | 0 | foreach $prePos (sort { $a <=> $b } keys %image) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 4642 | 0 | 0 |  |  |  | 0 | if ($pos < $prePos) { | 
| 4643 |  |  |  |  |  |  | # dump unknown trailer data | 
| 4644 | 0 |  |  |  |  | 0 | $$dirInfo{DirName} = 'Unknown'; | 
| 4645 | 0 |  |  |  |  | 0 | $$dirInfo{DataPos} = $pos; | 
| 4646 | 0 |  |  |  |  | 0 | $$dirInfo{DirLen} = $prePos - $pos; | 
| 4647 | 0 |  |  |  |  | 0 | $self->DumpTrailer($dirInfo); | 
| 4648 |  |  |  |  |  |  | } | 
| 4649 | 0 |  |  |  |  | 0 | ($tag, $preLen) = @{$image{$prePos}}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4650 | 0 | 0 |  |  |  | 0 | last unless $preLen; | 
| 4651 |  |  |  |  |  |  | # dump image if verbose (it is htmlDump'd by ExtractImage) | 
| 4652 | 0 | 0 |  |  |  | 0 | if ($$self{OPTIONS}{Verbose}) { | 
| 4653 | 0 |  |  |  |  | 0 | $$dirInfo{DirName} = $tag; | 
| 4654 | 0 |  |  |  |  | 0 | $$dirInfo{DataPos} = $prePos; | 
| 4655 | 0 |  |  |  |  | 0 | $$dirInfo{DirLen}  = $preLen; | 
| 4656 | 0 |  |  |  |  | 0 | $self->DumpTrailer($dirInfo); | 
| 4657 |  |  |  |  |  |  | } | 
| 4658 | 0 |  |  |  |  | 0 | $pos = $prePos + $preLen; | 
| 4659 |  |  |  |  |  |  | } | 
| 4660 |  |  |  |  |  |  | } | 
| 4661 |  |  |  |  |  |  |  | 
| 4662 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4663 |  |  |  |  |  |  | # Find last element in linked list | 
| 4664 |  |  |  |  |  |  | # Inputs: 0) element in list | 
| 4665 |  |  |  |  |  |  | # Returns: Last element in list | 
| 4666 |  |  |  |  |  |  | sub LastInList($) | 
| 4667 |  |  |  |  |  |  | { | 
| 4668 | 35 |  |  | 35 | 0 | 108 | my $element = shift; | 
| 4669 | 35 |  |  |  |  | 149 | while ($$element{Next}) { | 
| 4670 | 0 |  |  |  |  | 0 | $element = $$element{Next}; | 
| 4671 |  |  |  |  |  |  | } | 
| 4672 | 35 |  |  |  |  | 83 | return $element; | 
| 4673 |  |  |  |  |  |  | } | 
| 4674 |  |  |  |  |  |  |  | 
| 4675 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4676 |  |  |  |  |  |  | # Print verbose value while writing | 
| 4677 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) heading "eg. '+ IPTC:Keywords', | 
| 4678 |  |  |  |  |  |  | #         2) value, 3) [optional] extra text after value | 
| 4679 |  |  |  |  |  |  | sub VerboseValue($$$;$) | 
| 4680 |  |  |  |  |  |  | { | 
| 4681 | 1059 | 100 |  | 1059 | 0 | 3307 | return unless $_[0]{OPTIONS}{Verbose} > 1; | 
| 4682 | 14 |  |  |  |  | 33 | my ($self, $str, $val, $xtra) = @_; | 
| 4683 | 14 |  |  |  |  | 30 | my $out = $$self{OPTIONS}{TextOut}; | 
| 4684 | 14 | 100 |  |  |  | 38 | $xtra or $xtra = ''; | 
| 4685 | 14 |  |  |  |  | 32 | my $maxLen = 81 - length($str) - length($xtra); | 
| 4686 | 14 |  |  |  |  | 44 | $val = $self->Printable($val, $maxLen); | 
| 4687 | 14 |  |  |  |  | 73 | print $out "    $str = '${val}'$xtra\n"; | 
| 4688 |  |  |  |  |  |  | } | 
| 4689 |  |  |  |  |  |  |  | 
| 4690 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4691 |  |  |  |  |  |  | # Pack Unicode numbers into UTF8 string | 
| 4692 |  |  |  |  |  |  | # Inputs: 0-N) list of Unicode numbers | 
| 4693 |  |  |  |  |  |  | # Returns: Packed UTF-8 string | 
| 4694 |  |  |  |  |  |  | sub PackUTF8(@) | 
| 4695 |  |  |  |  |  |  | { | 
| 4696 | 0 |  |  | 0 | 0 | 0 | my @out; | 
| 4697 | 0 |  |  |  |  | 0 | while (@_) { | 
| 4698 | 0 |  |  |  |  | 0 | my $ch = pop; | 
| 4699 | 0 | 0 |  |  |  | 0 | unshift(@out, $ch), next if $ch < 0x80; | 
| 4700 | 0 |  |  |  |  | 0 | unshift(@out, 0x80 | ($ch & 0x3f)); | 
| 4701 | 0 |  |  |  |  | 0 | $ch >>= 6; | 
| 4702 | 0 | 0 |  |  |  | 0 | unshift(@out, 0xc0 | $ch), next if $ch < 0x20; | 
| 4703 | 0 |  |  |  |  | 0 | unshift(@out, 0x80 | ($ch & 0x3f)); | 
| 4704 | 0 |  |  |  |  | 0 | $ch >>= 6; | 
| 4705 | 0 | 0 |  |  |  | 0 | unshift(@out, 0xe0 | $ch), next if $ch < 0x10; | 
| 4706 | 0 |  |  |  |  | 0 | unshift(@out, 0x80 | ($ch & 0x3f)); | 
| 4707 | 0 |  |  |  |  | 0 | $ch >>= 6; | 
| 4708 | 0 |  |  |  |  | 0 | unshift(@out, 0xf0 | ($ch & 0x07)); | 
| 4709 |  |  |  |  |  |  | } | 
| 4710 | 0 |  |  |  |  | 0 | return pack('C*', @out); | 
| 4711 |  |  |  |  |  |  | } | 
| 4712 |  |  |  |  |  |  |  | 
| 4713 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4714 |  |  |  |  |  |  | # Unpack numbers from UTF8 string | 
| 4715 |  |  |  |  |  |  | # Inputs: 0) UTF-8 string | 
| 4716 |  |  |  |  |  |  | # Returns: List of Unicode numbers (sets $evalWarning on error) | 
| 4717 |  |  |  |  |  |  | sub UnpackUTF8($) | 
| 4718 |  |  |  |  |  |  | { | 
| 4719 | 0 |  |  | 0 | 0 | 0 | my (@out, $pos); | 
| 4720 | 0 |  |  |  |  | 0 | pos($_[0]) = $pos = 0;  # start at beginning of string | 
| 4721 | 0 |  |  |  |  | 0 | for (;;) { | 
| 4722 | 0 |  |  |  |  | 0 | my ($ch, $newPos, $val, $byte); | 
| 4723 | 0 | 0 |  |  |  | 0 | if ($_[0] =~ /([\x80-\xff])/g) { | 
| 4724 | 0 |  |  |  |  | 0 | $ch = ord($1); | 
| 4725 | 0 |  |  |  |  | 0 | $newPos = pos($_[0]) - 1; | 
| 4726 |  |  |  |  |  |  | } else { | 
| 4727 | 0 |  |  |  |  | 0 | $newPos = length $_[0]; | 
| 4728 |  |  |  |  |  |  | } | 
| 4729 |  |  |  |  |  |  | # unpack 7-bit characters | 
| 4730 | 0 |  |  |  |  | 0 | my $len = $newPos - $pos; | 
| 4731 | 0 | 0 |  |  |  | 0 | push @out, unpack("x${pos}C$len",$_[0]) if $len; | 
| 4732 | 0 | 0 |  |  |  | 0 | last unless defined $ch; | 
| 4733 | 0 |  |  |  |  | 0 | $pos = $newPos + 1; | 
| 4734 |  |  |  |  |  |  | # minimum lead byte for 2-byte sequence is 0xc2 (overlong sequences | 
| 4735 |  |  |  |  |  |  | # not allowed), 0xf8-0xfd are restricted by RFC 3629 (no 5 or 6 byte | 
| 4736 |  |  |  |  |  |  | # sequences), and 0xfe and 0xff are not valid in UTF-8 strings | 
| 4737 | 0 | 0 | 0 |  |  | 0 | if ($ch < 0xc2 or $ch >= 0xf8) { | 
| 4738 | 0 |  |  |  |  | 0 | push @out, ord('?');    # invalid UTF-8 | 
| 4739 | 0 |  |  |  |  | 0 | $evalWarning = 'Bad UTF-8'; | 
| 4740 | 0 |  |  |  |  | 0 | next; | 
| 4741 |  |  |  |  |  |  | } | 
| 4742 |  |  |  |  |  |  | # decode 2, 3 and 4-byte sequences | 
| 4743 | 0 |  |  |  |  | 0 | my $n = 1; | 
| 4744 | 0 | 0 |  |  |  | 0 | if ($ch < 0xe0) { | 
|  |  | 0 |  |  |  |  |  | 
| 4745 | 0 |  |  |  |  | 0 | $val = $ch & 0x1f;      # 2-byte sequence | 
| 4746 |  |  |  |  |  |  | } elsif ($ch < 0xf0) { | 
| 4747 | 0 |  |  |  |  | 0 | $val = $ch & 0x0f;      # 3-byte sequence | 
| 4748 | 0 |  |  |  |  | 0 | ++$n; | 
| 4749 |  |  |  |  |  |  | } else { | 
| 4750 | 0 |  |  |  |  | 0 | $val = $ch & 0x07;      # 4-byte sequence | 
| 4751 | 0 |  |  |  |  | 0 | $n += 2; | 
| 4752 |  |  |  |  |  |  | } | 
| 4753 | 0 | 0 |  |  |  | 0 | unless ($_[0] =~ /\G([\x80-\xbf]{$n})/g) { | 
| 4754 | 0 |  |  |  |  | 0 | pos($_[0]) = $pos;      # restore position | 
| 4755 | 0 |  |  |  |  | 0 | push @out, ord('?');    # invalid UTF-8 | 
| 4756 | 0 |  |  |  |  | 0 | $evalWarning = 'Bad UTF-8'; | 
| 4757 | 0 |  |  |  |  | 0 | next; | 
| 4758 |  |  |  |  |  |  | } | 
| 4759 | 0 |  |  |  |  | 0 | foreach $byte (unpack 'C*', $1) { | 
| 4760 | 0 |  |  |  |  | 0 | $val = ($val << 6) | ($byte & 0x3f); | 
| 4761 |  |  |  |  |  |  | } | 
| 4762 | 0 |  |  |  |  | 0 | push @out, $val;    # save Unicode character value | 
| 4763 | 0 |  |  |  |  | 0 | $pos += $n;         # position at end of UTF-8 character | 
| 4764 |  |  |  |  |  |  | } | 
| 4765 | 0 |  |  |  |  | 0 | return @out; | 
| 4766 |  |  |  |  |  |  | } | 
| 4767 |  |  |  |  |  |  |  | 
| 4768 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4769 |  |  |  |  |  |  | # Generate a new, random GUID | 
| 4770 |  |  |  |  |  |  | # Inputs: | 
| 4771 |  |  |  |  |  |  | # Returns: GUID string | 
| 4772 |  |  |  |  |  |  | my $guidCount; | 
| 4773 |  |  |  |  |  |  | sub NewGUID() | 
| 4774 |  |  |  |  |  |  | { | 
| 4775 | 61 |  |  | 61 | 0 | 1536 | my @tm = localtime time; | 
| 4776 | 61 | 100 | 66 |  |  | 664 | $guidCount = 0 unless defined $guidCount and ++$guidCount < 0x100; | 
| 4777 | 61 |  |  |  |  | 1834 | return sprintf('%.4d%.2d%.2d%.2d%.2d%.2d%.2X%.4X%.4X%.4X%.4X', | 
| 4778 |  |  |  |  |  |  | $tm[5]+1900, $tm[4]+1, $tm[3], $tm[2], $tm[1], $tm[0], $guidCount, | 
| 4779 |  |  |  |  |  |  | $$ & 0xffff, rand(0x10000), rand(0x10000), rand(0x10000)); | 
| 4780 |  |  |  |  |  |  | } | 
| 4781 |  |  |  |  |  |  |  | 
| 4782 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4783 |  |  |  |  |  |  | # Make TIFF header for raw data | 
| 4784 |  |  |  |  |  |  | # Inputs: 0) width, 1) height, 2) num colour components, 3) bits, 4) resolution | 
| 4785 |  |  |  |  |  |  | #         5) color-map data for palette-color image (8 or 16 bit) | 
| 4786 |  |  |  |  |  |  | # Returns: TIFF header | 
| 4787 |  |  |  |  |  |  | # Notes: Multi-byte data must be little-endian | 
| 4788 |  |  |  |  |  |  | sub MakeTiffHeader($$$$;$$) | 
| 4789 |  |  |  |  |  |  | { | 
| 4790 | 0 |  |  | 0 | 0 | 0 | my ($w, $h, $cols, $bits, $res, $cmap) = @_; | 
| 4791 | 0 | 0 |  |  |  | 0 | $res or $res = 72; | 
| 4792 | 0 |  |  |  |  | 0 | my $saveOrder = GetByteOrder(); | 
| 4793 | 0 |  |  |  |  | 0 | SetByteOrder('II'); | 
| 4794 | 0 | 0 |  |  |  | 0 | if (not $cmap) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 4795 | 0 |  |  |  |  | 0 | $cmap = ''; | 
| 4796 |  |  |  |  |  |  | } elsif (length $cmap == 3 * 2**$bits) { | 
| 4797 |  |  |  |  |  |  | # convert to short | 
| 4798 | 0 |  |  |  |  | 0 | $cmap = pack 'v*', map { $_ | ($_<<8) } unpack 'C*', $cmap; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4799 |  |  |  |  |  |  | } elsif (length $cmap != 6 * 2**$bits) { | 
| 4800 | 0 |  |  |  |  | 0 | $cmap = ''; | 
| 4801 |  |  |  |  |  |  | } | 
| 4802 | 0 | 0 |  |  |  | 0 | my $cmo = $cmap ? 12 : 0;   # offset due to ColorMap IFD entry | 
| 4803 | 0 | 0 |  |  |  | 0 | my $hdr = | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 4804 |  |  |  |  |  |  | "\x49\x49\x2a\0\x08\0\0\0\x0e\0" .                  # 0x00 14 menu entries: | 
| 4805 |  |  |  |  |  |  | "\xfe\x00\x04\0\x01\0\0\0\x00\0\0\0" .              # 0x0a SubfileType = 0 | 
| 4806 |  |  |  |  |  |  | "\x00\x01\x04\0\x01\0\0\0" . Set32u($w) .           # 0x16 ImageWidth | 
| 4807 |  |  |  |  |  |  | "\x01\x01\x04\0\x01\0\0\0" . Set32u($h) .           # 0x22 ImageHeight | 
| 4808 |  |  |  |  |  |  | "\x02\x01\x03\0" . Set32u($cols) .                  # 0x2e BitsPerSample | 
| 4809 |  |  |  |  |  |  | Set32u($cols == 1 ? $bits : 0xb6 + $cmo) . | 
| 4810 |  |  |  |  |  |  | "\x03\x01\x03\0\x01\0\0\0\x01\0\0\0" .              # 0x3a Compression = 1 | 
| 4811 |  |  |  |  |  |  | "\x06\x01\x03\0\x01\0\0\0" .                        # 0x46 PhotometricInterpretation | 
| 4812 |  |  |  |  |  |  | Set32u($cmap ? 3 : $cols == 1 ? 1 : 2) . | 
| 4813 |  |  |  |  |  |  | "\x11\x01\x04\0\x01\0\0\0" .                        # 0x52 StripOffsets | 
| 4814 |  |  |  |  |  |  | Set32u(0xcc + $cmo + length($cmap)) . | 
| 4815 |  |  |  |  |  |  | "\x15\x01\x03\0\x01\0\0\0" . Set32u($cols) .        # 0x5e SamplesPerPixel | 
| 4816 |  |  |  |  |  |  | "\x16\x01\x04\0\x01\0\0\0" . Set32u($h) .           # 0x6a RowsPerStrip | 
| 4817 |  |  |  |  |  |  | "\x17\x01\x04\0\x01\0\0\0" .                        # 0x76 StripByteCounts | 
| 4818 |  |  |  |  |  |  | Set32u($w * $h * $cols * int(($bits+7)/8)) . | 
| 4819 |  |  |  |  |  |  | "\x1a\x01\x05\0\x01\0\0\0" . Set32u(0xbc + $cmo) .  # 0x82 XResolution | 
| 4820 |  |  |  |  |  |  | "\x1b\x01\x05\0\x01\0\0\0" . Set32u(0xc4 + $cmo) .  # 0x8e YResolution | 
| 4821 |  |  |  |  |  |  | "\x1c\x01\x03\0\x01\0\0\0\x01\0\0\0" .              # 0x9a PlanarConfiguration = 1 | 
| 4822 |  |  |  |  |  |  | "\x28\x01\x03\0\x01\0\0\0\x02\0\0\0" .              # 0xa6 ResolutionUnit = 2 | 
| 4823 |  |  |  |  |  |  | ($cmap ?                                            # 0xb2 ColorMap [optional] | 
| 4824 |  |  |  |  |  |  | "\x40\x01\x03\0" . Set32u(3 * 2**$bits) . "\xd8\0\0\0" : '') . | 
| 4825 |  |  |  |  |  |  | "\0\0\0\0" .                                        # 0xb2+$cmo (no IFD1) | 
| 4826 |  |  |  |  |  |  | (Set16u($bits) x 3) .                               # 0xb6+$cmo BitsPerSample value | 
| 4827 |  |  |  |  |  |  | Set32u($res) . "\x01\0\0\0" .                       # 0xbc+$cmo XResolution = 72 | 
| 4828 |  |  |  |  |  |  | Set32u($res) . "\x01\0\0\0" .                       # 0xc4+$cmo YResolution = 72 | 
| 4829 |  |  |  |  |  |  | $cmap;                                              # 0xcc or 0xd8 (cmap and data go here) | 
| 4830 | 0 |  |  |  |  | 0 | SetByteOrder($saveOrder); | 
| 4831 | 0 |  |  |  |  | 0 | return $hdr; | 
| 4832 |  |  |  |  |  |  | } | 
| 4833 |  |  |  |  |  |  |  | 
| 4834 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4835 |  |  |  |  |  |  | # Return current time in EXIF format | 
| 4836 |  |  |  |  |  |  | # Inputs: 0) [optional] ExifTool ref, 1) flag to include timezone (0 to disable, | 
| 4837 |  |  |  |  |  |  | #            undef or 1 to include) | 
| 4838 |  |  |  |  |  |  | # Returns: time string | 
| 4839 |  |  |  |  |  |  | # - a consistent value is returned for each processed file | 
| 4840 |  |  |  |  |  |  | sub TimeNow(;$$) | 
| 4841 |  |  |  |  |  |  | { | 
| 4842 | 61 |  |  | 61 | 0 | 275 | my ($self, $tzFlag) = @_; | 
| 4843 | 61 |  |  |  |  | 153 | my $timeNow; | 
| 4844 | 61 | 50 |  |  |  | 301 | ref $self or $tzFlag = $self, $self = { }; | 
| 4845 | 61 | 50 |  |  |  | 287 | if ($$self{Now}) { | 
| 4846 | 0 |  |  |  |  | 0 | $timeNow = $$self{Now}[0]; | 
| 4847 |  |  |  |  |  |  | } else { | 
| 4848 | 61 |  |  |  |  | 190 | my $time = time(); | 
| 4849 | 61 |  |  |  |  | 2537 | my @tm = localtime $time; | 
| 4850 | 61 |  |  |  |  | 635 | my $tz = TimeZoneString(\@tm, $time); | 
| 4851 | 61 |  |  |  |  | 586 | $timeNow = sprintf("%4d:%.2d:%.2d %.2d:%.2d:%.2d", | 
| 4852 |  |  |  |  |  |  | $tm[5]+1900, $tm[4]+1, $tm[3], | 
| 4853 |  |  |  |  |  |  | $tm[2], $tm[1], $tm[0]); | 
| 4854 | 61 |  |  |  |  | 372 | $$self{Now} = [ $timeNow, $tz ]; | 
| 4855 |  |  |  |  |  |  | } | 
| 4856 | 61 | 50 | 33 |  |  | 656 | $timeNow .= $$self{Now}[1] if $tzFlag or not defined $tzFlag; | 
| 4857 | 61 |  |  |  |  | 382 | return $timeNow; | 
| 4858 |  |  |  |  |  |  | } | 
| 4859 |  |  |  |  |  |  |  | 
| 4860 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4861 |  |  |  |  |  |  | # Inverse date/time print conversion (reformat to YYYY:mm:dd HH:MM:SS[.ss][+-HH:MM|Z]) | 
| 4862 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) Date/Time string, 2) timezone flag: | 
| 4863 |  |  |  |  |  |  | #               0     - remove timezone and sub-seconds if they exist | 
| 4864 |  |  |  |  |  |  | #               1     - add timezone if it doesn't exist | 
| 4865 |  |  |  |  |  |  | #               undef - leave timezone alone | 
| 4866 |  |  |  |  |  |  | #         3) flag to allow date-only (YYYY, YYYY:mm or YYYY:mm:dd) or time without seconds | 
| 4867 |  |  |  |  |  |  | # Returns: formatted date/time string (or undef and issues warning on error) | 
| 4868 |  |  |  |  |  |  | # Notes: currently accepts different separators, but doesn't use DateFormat yet | 
| 4869 |  |  |  |  |  |  | my $strptimeLib; # strptime library name if available | 
| 4870 |  |  |  |  |  |  | sub InverseDateTime($$;$$) | 
| 4871 |  |  |  |  |  |  | { | 
| 4872 | 437 |  |  | 437 | 0 | 1323 | my ($self, $val, $tzFlag, $dateOnly) = @_; | 
| 4873 | 437 |  |  |  |  | 773 | my ($rtnVal, $tz); | 
| 4874 | 437 |  |  |  |  | 1281 | my $fmt = $$self{OPTIONS}{DateFormat}; | 
| 4875 |  |  |  |  |  |  | # strip off timezone first if it exists | 
| 4876 | 437 | 100 | 66 |  |  | 3987 | if (not $fmt and $val =~ s/([-+])(\d{1,2}):?(\d{2})\s*(DST)?$//i) { | 
|  |  | 50 | 33 |  |  |  |  | 
| 4877 | 6 |  |  |  |  | 89 | $tz = sprintf("$1%.2d:$3", $2); | 
| 4878 |  |  |  |  |  |  | } elsif (not $fmt and $val =~ s/Z$//i) { | 
| 4879 | 0 |  |  |  |  | 0 | $tz = 'Z'; | 
| 4880 |  |  |  |  |  |  | } else { | 
| 4881 | 431 |  |  |  |  | 898 | $tz = ''; | 
| 4882 |  |  |  |  |  |  | # allow special value of 'now' | 
| 4883 | 431 | 50 |  |  |  | 1251 | return $self->TimeNow($tzFlag) if lc($val) eq 'now'; | 
| 4884 |  |  |  |  |  |  | } | 
| 4885 |  |  |  |  |  |  | # only convert date if a format was specified and the date is recognizable | 
| 4886 | 437 | 50 |  |  |  | 1025 | if ($fmt) { | 
| 4887 | 0 | 0 |  |  |  | 0 | unless (defined $strptimeLib) { | 
| 4888 | 0 | 0 |  |  |  | 0 | if (eval { require POSIX::strptime }) { | 
|  | 0 | 0 |  |  |  | 0 |  | 
| 4889 | 0 |  |  |  |  | 0 | $strptimeLib = 'POSIX::strptime'; | 
| 4890 | 0 |  |  |  |  | 0 | } elsif (eval { require Time::Piece }) { | 
| 4891 | 0 |  |  |  |  | 0 | $strptimeLib = 'Time::Piece'; | 
| 4892 |  |  |  |  |  |  | # (call use_locale() to convert localized date/time, | 
| 4893 |  |  |  |  |  |  | #  only available in Time::Piece 1.32 and later) | 
| 4894 | 0 |  |  |  |  | 0 | eval { Time::Piece->use_locale() }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4895 |  |  |  |  |  |  | } else { | 
| 4896 | 0 |  |  |  |  | 0 | $strptimeLib = ''; | 
| 4897 |  |  |  |  |  |  | } | 
| 4898 |  |  |  |  |  |  | } | 
| 4899 |  |  |  |  |  |  | # handle factional seconds (%f), but only at the end of the string | 
| 4900 | 0 | 0 | 0 |  |  | 0 | my $fs = ($fmt =~ s/%f$// and $val =~ s/(\.\d+)\s*$//) ? $1 : ''; | 
| 4901 | 0 |  |  |  |  | 0 | my ($lib, $wrn, @a); | 
| 4902 | 0 |  |  |  |  | 0 | TryLib: for ($lib=$strptimeLib; ; $lib='') { | 
| 4903 | 0 | 0 |  |  |  | 0 | if (not $lib) { | 
|  |  | 0 |  |  |  |  |  | 
| 4904 | 0 | 0 |  |  |  | 0 | last unless $$self{OPTIONS}{StrictDate}; | 
| 4905 | 0 |  | 0 |  |  | 0 | warn $wrn || "Install POSIX::strptime or Time::Piece for inverse date/time conversions\n"; | 
| 4906 | 0 |  |  |  |  | 0 | return undef; | 
| 4907 |  |  |  |  |  |  | } elsif ($lib eq 'POSIX::strptime') { | 
| 4908 | 0 |  |  |  |  | 0 | @a = eval { POSIX::strptime($val, $fmt) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 4909 |  |  |  |  |  |  | } else { | 
| 4910 |  |  |  |  |  |  | # protect against a negative epoch time, it can cause a hard crash in Windows | 
| 4911 | 0 | 0 | 0 |  |  | 0 | if ($^O eq 'MSWin32' and $fmt =~ /%s/ and $val =~ /-\d/) { | 
|  |  |  | 0 |  |  |  |  | 
| 4912 | 0 |  |  |  |  | 0 | warn "Can't convert negative epoch time\n"; | 
| 4913 | 0 |  |  |  |  | 0 | return undef; | 
| 4914 |  |  |  |  |  |  | } | 
| 4915 | 0 |  |  |  |  | 0 | @a = eval { | 
| 4916 | 0 |  |  |  |  | 0 | my $t = Time::Piece->strptime($val, $fmt); | 
| 4917 | 0 |  |  |  |  | 0 | return ($t->sec, $t->min, $t->hour, $t->mday, $t->_mon, $t->_year); | 
| 4918 |  |  |  |  |  |  | }; | 
| 4919 |  |  |  |  |  |  | } | 
| 4920 | 0 | 0 | 0 |  |  | 0 | if (defined $a[5] and length $a[5]) { | 
| 4921 | 0 |  |  |  |  | 0 | $a[5] += 1900; # add 1900 to year | 
| 4922 |  |  |  |  |  |  | } else { | 
| 4923 | 0 |  |  |  |  | 0 | $wrn = "Invalid date/time (no year) using $lib\n"; | 
| 4924 | 0 |  |  |  |  | 0 | next; | 
| 4925 |  |  |  |  |  |  | } | 
| 4926 | 0 | 0 | 0 |  |  | 0 | ++$a[4] if defined $a[4] and length $a[4];  # add 1 to month | 
| 4927 | 0 |  |  |  |  | 0 | my $i; | 
| 4928 | 0 |  |  |  |  | 0 | foreach $i (0..4) { | 
| 4929 | 0 | 0 | 0 |  |  | 0 | if (not defined $a[$i] or not length $a[$i]) { | 
|  |  | 0 |  |  |  |  |  | 
| 4930 | 0 | 0 | 0 |  |  | 0 | if ($i < 2 or $dateOnly) { # (allow missing minutes/seconds) | 
| 4931 | 0 |  |  |  |  | 0 | $a[$i] = '  '; | 
| 4932 |  |  |  |  |  |  | } else { | 
| 4933 | 0 |  |  |  |  | 0 | $wrn = "Incomplete date/time specification using $lib\n"; | 
| 4934 | 0 |  |  |  |  | 0 | next TryLib; | 
| 4935 |  |  |  |  |  |  | } | 
| 4936 |  |  |  |  |  |  | } elsif (length($a[$i]) < 2) { | 
| 4937 | 0 |  |  |  |  | 0 | $a[$i] = "0$a[$i]"; # pad to 2 digits if necessary | 
| 4938 |  |  |  |  |  |  | } | 
| 4939 |  |  |  |  |  |  | } | 
| 4940 | 0 |  |  |  |  | 0 | $val = join(':', @a[5,4,3]) . ' ' . join(':', @a[2,1,0]) . $fs; | 
| 4941 | 0 |  |  |  |  | 0 | last; | 
| 4942 |  |  |  |  |  |  | } | 
| 4943 |  |  |  |  |  |  | } | 
| 4944 | 437 | 100 |  |  |  | 1970 | if ($val =~ /(\d{4})/g) {           # get YYYY | 
| 4945 | 430 |  |  |  |  | 1118 | my $yr = $1; | 
| 4946 | 430 |  |  |  |  | 2609 | my @a = ($val =~ /\d{1,2}/g);   # get mm, dd, HH, and maybe MM, SS | 
| 4947 | 430 |  | 66 |  |  | 2560 | length($_) < 2 and $_ = "0$_" foreach @a;   # pad to 2 digits if necessary | 
| 4948 | 430 | 100 |  |  |  | 1174 | if (@a >= 3) { | 
|  |  | 50 |  |  |  |  |  | 
| 4949 | 404 |  |  |  |  | 824 | my $ss = $a[4];             # get SS | 
| 4950 | 404 |  |  |  |  | 1147 | push @a, '00' while @a < 5; # add MM, SS if not given | 
| 4951 |  |  |  |  |  |  | # get sub-seconds if they exist (must be after SS, and have leading ".") | 
| 4952 | 404 | 100 | 100 |  |  | 1386 | my $fs = (@a > 5 and $val =~ /(\.\d+)\s*$/) ? $1 : ''; | 
| 4953 |  |  |  |  |  |  | # add/remove timezone if necessary | 
| 4954 | 404 | 100 |  |  |  | 1324 | if ($tzFlag) { | 
|  |  | 100 |  |  |  |  |  | 
| 4955 | 34 | 50 |  |  |  | 159 | if (not $tz) { | 
| 4956 | 34 | 50 |  |  |  | 89 | if (eval { require Time::Local }) { | 
|  | 34 |  |  |  |  | 945 |  | 
| 4957 |  |  |  |  |  |  | # determine timezone offset for this time | 
| 4958 | 34 |  |  |  |  | 2658 | my @args = ($a[4],$a[3],$a[2],$a[1],$a[0]-1,$yr); | 
| 4959 | 34 |  |  |  |  | 188 | my $diff = Time::Local::timegm(@args) - TimeLocal(@args); | 
| 4960 | 34 |  |  |  |  | 157 | $tz = TimeZoneString($diff / 60); | 
| 4961 |  |  |  |  |  |  | } else { | 
| 4962 | 0 |  |  |  |  | 0 | $tz = 'Z';  # don't know time zone | 
| 4963 |  |  |  |  |  |  | } | 
| 4964 |  |  |  |  |  |  | } | 
| 4965 |  |  |  |  |  |  | } elsif (defined $tzFlag) { | 
| 4966 | 92 |  |  |  |  | 257 | $tz = $fs = ''; # remove timezone and sub-seconds | 
| 4967 |  |  |  |  |  |  | } | 
| 4968 | 404 | 100 | 66 |  |  | 2168 | if (defined $ss and $ss < 60) { | 
|  |  | 50 |  |  |  |  |  | 
| 4969 | 403 |  |  |  |  | 1048 | $ss = ":$ss"; | 
| 4970 |  |  |  |  |  |  | } elsif ($dateOnly) { | 
| 4971 | 1 |  |  |  |  | 5 | $ss = ''; | 
| 4972 |  |  |  |  |  |  | } else { | 
| 4973 | 0 |  |  |  |  | 0 | $ss = ':00'; | 
| 4974 |  |  |  |  |  |  | } | 
| 4975 |  |  |  |  |  |  | # construct properly formatted date/time string | 
| 4976 | 404 | 50 | 33 |  |  | 1899 | if ($a[0] < 1 or $a[0] > 12) { | 
| 4977 | 0 |  |  |  |  | 0 | warn "Month '$a[0]' out of range 1..12\n"; | 
| 4978 | 0 |  |  |  |  | 0 | return undef; | 
| 4979 |  |  |  |  |  |  | } | 
| 4980 | 404 | 50 | 33 |  |  | 1719 | if ($a[1] < 1 or $a[1] > 31) { | 
| 4981 | 0 |  |  |  |  | 0 | warn "Day '$a[1]' out of range 1..31\n"; | 
| 4982 | 0 |  |  |  |  | 0 | return undef; | 
| 4983 |  |  |  |  |  |  | } | 
| 4984 | 404 | 50 |  |  |  | 969 | $a[2] > 24 and warn("Hour '$a[2]' out of range 0..24\n"), return undef; | 
| 4985 | 404 | 50 |  |  |  | 956 | $a[3] > 59 and warn("Minutes '$a[3]' out of range 0..59\n"), return undef; | 
| 4986 | 404 |  |  |  |  | 1697 | $rtnVal = "$yr:$a[0]:$a[1] $a[2]:$a[3]$ss$fs$tz"; | 
| 4987 |  |  |  |  |  |  | } elsif ($dateOnly) { | 
| 4988 | 26 |  |  |  |  | 154 | $rtnVal = join ':', $yr, @a; | 
| 4989 |  |  |  |  |  |  | } | 
| 4990 |  |  |  |  |  |  | } | 
| 4991 | 437 | 100 |  |  |  | 1102 | $rtnVal or warn "Invalid date/time (use YYYY:mm:dd HH:MM:SS[.ss][+/-HH:MM|Z])\n"; | 
| 4992 | 437 |  |  |  |  | 3975 | return $rtnVal; | 
| 4993 |  |  |  |  |  |  | } | 
| 4994 |  |  |  |  |  |  |  | 
| 4995 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 4996 |  |  |  |  |  |  | # Set byte order according to our current preferences | 
| 4997 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) default byte order | 
| 4998 |  |  |  |  |  |  | # Returns: new byte order ('II' or 'MM') and sets current byte order | 
| 4999 |  |  |  |  |  |  | # Notes: takes the first of the following that is valid: | 
| 5000 |  |  |  |  |  |  | #  1) ByteOrder option | 
| 5001 |  |  |  |  |  |  | #  2) new value for ExifByteOrder | 
| 5002 |  |  |  |  |  |  | #  3) default byte order passed to this routine | 
| 5003 |  |  |  |  |  |  | #  4) makenote byte order from last file read | 
| 5004 |  |  |  |  |  |  | #  5) big endian | 
| 5005 |  |  |  |  |  |  | sub SetPreferredByteOrder($;$) | 
| 5006 |  |  |  |  |  |  | { | 
| 5007 | 44 |  |  | 44 | 0 | 176 | my ($self, $default) = @_; | 
| 5008 |  |  |  |  |  |  | my $byteOrder = $self->Options('ByteOrder') || | 
| 5009 |  |  |  |  |  |  | $self->GetNewValue('ExifByteOrder') || | 
| 5010 | 44 |  | 100 |  |  | 228 | $default || $$self{MAKER_NOTE_BYTE_ORDER} || 'MM'; | 
| 5011 | 44 | 50 |  |  |  | 281 | unless (SetByteOrder($byteOrder)) { | 
| 5012 | 0 | 0 |  |  |  | 0 | warn "Invalid byte order '${byteOrder}'\n" if $self->Options('Verbose'); | 
| 5013 | 0 |  | 0 |  |  | 0 | $byteOrder = $$self{MAKER_NOTE_BYTE_ORDER} || 'MM'; | 
| 5014 | 0 |  |  |  |  | 0 | SetByteOrder($byteOrder); | 
| 5015 |  |  |  |  |  |  | } | 
| 5016 | 44 |  |  |  |  | 223 | return GetByteOrder(); | 
| 5017 |  |  |  |  |  |  | } | 
| 5018 |  |  |  |  |  |  |  | 
| 5019 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5020 |  |  |  |  |  |  | # Assemble a continuing fraction into a rational value | 
| 5021 |  |  |  |  |  |  | # Inputs: 0) numerator, 1) denominator | 
| 5022 |  |  |  |  |  |  | #         2-N) list of fraction denominators, deepest first | 
| 5023 |  |  |  |  |  |  | # Returns: numerator, denominator (in list context) | 
| 5024 |  |  |  |  |  |  | sub AssembleRational($$@) | 
| 5025 |  |  |  |  |  |  | { | 
| 5026 | 4968 | 100 |  | 4968 | 0 | 10947 | @_ < 3 and return @_; | 
| 5027 | 3417 |  |  |  |  | 6033 | my ($num, $denom, $frac) = splice(@_, 0, 3); | 
| 5028 | 3417 |  |  |  |  | 6565 | return AssembleRational($frac*$num+$denom, $num, @_); | 
| 5029 |  |  |  |  |  |  | } | 
| 5030 |  |  |  |  |  |  |  | 
| 5031 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5032 |  |  |  |  |  |  | # Convert a floating point number (or 'inf' or 'undef' or a fraction) into a rational | 
| 5033 |  |  |  |  |  |  | # Inputs: 0) floating point number, 1) optional maximum value (defaults to 0x7fffffff) | 
| 5034 |  |  |  |  |  |  | # Returns: numerator, denominator (in list context) | 
| 5035 |  |  |  |  |  |  | # Notes: | 
| 5036 |  |  |  |  |  |  | # - the returned rational will be accurate to at least 8 significant figures if possible | 
| 5037 |  |  |  |  |  |  | # - eg. an input of 3.14159265358979 returns a rational of 104348/33215, | 
| 5038 |  |  |  |  |  |  | #   which equals    3.14159265392142 and is accurate to 10 significant figures | 
| 5039 |  |  |  |  |  |  | # - the returned rational will be reduced to the lowest common denominator except when | 
| 5040 |  |  |  |  |  |  | #   the input is a fraction in which case the input is returned unchanged | 
| 5041 |  |  |  |  |  |  | # - these routines were a bit tricky, but fun to write! | 
| 5042 |  |  |  |  |  |  | sub Rationalize($;$) | 
| 5043 |  |  |  |  |  |  | { | 
| 5044 | 741 |  |  | 741 | 0 | 1747 | my $val = shift; | 
| 5045 | 741 | 50 |  |  |  | 1984 | return (1, 0) if $val eq 'inf'; | 
| 5046 | 741 | 50 |  |  |  | 1779 | return (0, 0) if $val eq 'undef'; | 
| 5047 | 741 | 100 |  |  |  | 2199 | return ($1,$2) if $val =~ m{^([-+]?\d+)/(\d+)$}; # accept fractional values | 
| 5048 |  |  |  |  |  |  | # Note: Just testing "if $val" doesn't work because '0.0' is true!  (ugghh!) | 
| 5049 | 725 | 100 |  |  |  | 2459 | return (0, 1) if $val == 0; | 
| 5050 | 686 | 100 |  |  |  | 1755 | my $sign = $val < 0 ? ($val = -$val, -1) : 1; | 
| 5051 | 686 |  |  |  |  | 1244 | my ($num, $denom, @fracs); | 
| 5052 | 686 |  |  |  |  | 1218 | my $frac = $val; | 
| 5053 | 686 |  | 100 |  |  | 2171 | my $maxInt = shift || 0x7fffffff; | 
| 5054 | 686 |  |  |  |  | 1099 | for (;;) { | 
| 5055 | 1551 |  |  |  |  | 4284 | my ($n, $d) = AssembleRational(int($frac + 0.5), 1, @fracs); | 
| 5056 | 1551 | 50 | 33 |  |  | 5729 | if ($n > $maxInt or $d > $maxInt) { | 
| 5057 | 0 | 0 |  |  |  | 0 | last if defined $num; | 
| 5058 | 0 | 0 |  |  |  | 0 | return ($sign, $maxInt) if $val < 1; | 
| 5059 | 0 |  |  |  |  | 0 | return ($sign * $maxInt, 1); | 
| 5060 |  |  |  |  |  |  | } | 
| 5061 | 1551 |  |  |  |  | 3027 | ($num, $denom) = ($n, $d);      # save last good values | 
| 5062 | 1551 |  |  |  |  | 3288 | my $err = ($n/$d-$val) / $val;  # get error of this rational | 
| 5063 | 1551 | 100 |  |  |  | 3684 | last if abs($err) < 1e-8;       # all done if error is small | 
| 5064 | 865 |  |  |  |  | 1383 | my $int = int($frac); | 
| 5065 | 865 |  |  |  |  | 1660 | unshift @fracs, $int; | 
| 5066 | 865 | 50 |  |  |  | 1898 | last unless $frac -= $int; | 
| 5067 | 865 |  |  |  |  | 1552 | $frac = 1 / $frac; | 
| 5068 |  |  |  |  |  |  | } | 
| 5069 | 686 |  |  |  |  | 2632 | return ($num * $sign, $denom); | 
| 5070 |  |  |  |  |  |  | } | 
| 5071 |  |  |  |  |  |  |  | 
| 5072 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5073 |  |  |  |  |  |  | # Utility routines to for writing binary data values | 
| 5074 |  |  |  |  |  |  | # Inputs: 0) value, 1) data ref, 2) offset | 
| 5075 |  |  |  |  |  |  | # Notes: prototype is (@) so values can be passed from list if desired | 
| 5076 |  |  |  |  |  |  | sub Set16s(@) | 
| 5077 |  |  |  |  |  |  | { | 
| 5078 | 188 |  |  | 188 | 0 | 334 | my $val = shift; | 
| 5079 | 188 | 100 |  |  |  | 473 | $val < 0 and $val += 0x10000; | 
| 5080 | 188 |  |  |  |  | 434 | return Set16u($val, @_); | 
| 5081 |  |  |  |  |  |  | } | 
| 5082 |  |  |  |  |  |  | sub Set32s(@) | 
| 5083 |  |  |  |  |  |  | { | 
| 5084 | 69 |  |  | 69 | 0 | 618 | my $val = shift; | 
| 5085 | 69 | 100 |  |  |  | 227 | $val < 0 and $val += 0xffffffff, ++$val; | 
| 5086 | 69 |  |  |  |  | 235 | return Set32u($val, @_); | 
| 5087 |  |  |  |  |  |  | } | 
| 5088 |  |  |  |  |  |  | sub Set64u(@) | 
| 5089 |  |  |  |  |  |  | { | 
| 5090 | 28 |  |  | 28 | 0 | 50 | my $val = $_[0]; | 
| 5091 | 28 |  |  |  |  | 65 | my $hi = int($val / 4294967296); | 
| 5092 | 28 |  |  |  |  | 72 | my $lo = Set32u($val - $hi * 4294967296); | 
| 5093 | 28 |  |  |  |  | 63 | $hi = Set32u($hi); | 
| 5094 | 28 | 100 |  |  |  | 74 | $val = GetByteOrder() eq 'MM' ? $hi . $lo : $lo . $hi; | 
| 5095 | 28 | 100 |  |  |  | 69 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 27 |  |  |  |  | 74 |  | 
| 5096 | 28 |  |  |  |  | 72 | return $val; | 
| 5097 |  |  |  |  |  |  | } | 
| 5098 |  |  |  |  |  |  | sub Set64s(@) | 
| 5099 |  |  |  |  |  |  | { | 
| 5100 | 0 |  |  | 0 | 0 | 0 | my $val = shift; | 
| 5101 | 0 | 0 |  |  |  | 0 | $val < 0 and $val += 4294967296 * 4294967296; # (temporary hack won't really work due to round-off errors) | 
| 5102 | 0 |  |  |  |  | 0 | return Set64u($val, @_); | 
| 5103 |  |  |  |  |  |  | } | 
| 5104 |  |  |  |  |  |  | sub SetRational64u(@) { | 
| 5105 | 428 |  |  | 428 | 0 | 1551 | my ($numer,$denom) = Rationalize($_[0],0xffffffff); | 
| 5106 | 428 |  |  |  |  | 1313 | my $val = Set32u($numer) . Set32u($denom); | 
| 5107 | 428 | 50 |  |  |  | 1337 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5108 | 428 |  |  |  |  | 1565 | return $val; | 
| 5109 |  |  |  |  |  |  | } | 
| 5110 |  |  |  |  |  |  | sub SetRational64s(@) { | 
| 5111 | 44 |  |  | 44 | 0 | 265 | my ($numer,$denom) = Rationalize($_[0]); | 
| 5112 | 44 |  |  |  |  | 208 | my $val = Set32s($numer) . Set32u($denom); | 
| 5113 | 44 | 50 |  |  |  | 210 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5114 | 44 |  |  |  |  | 166 | return $val; | 
| 5115 |  |  |  |  |  |  | } | 
| 5116 |  |  |  |  |  |  | sub SetRational32u(@) { | 
| 5117 | 0 |  |  | 0 | 0 | 0 | my ($numer,$denom) = Rationalize($_[0],0xffff); | 
| 5118 | 0 |  |  |  |  | 0 | my $val = Set16u($numer) . Set16u($denom); | 
| 5119 | 0 | 0 |  |  |  | 0 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5120 | 0 |  |  |  |  | 0 | return $val; | 
| 5121 |  |  |  |  |  |  | } | 
| 5122 |  |  |  |  |  |  | sub SetRational32s(@) { | 
| 5123 | 0 |  |  | 0 | 0 | 0 | my ($numer,$denom) = Rationalize($_[0],0x7fff); | 
| 5124 | 0 |  |  |  |  | 0 | my $val = Set16s($numer) . Set16u($denom); | 
| 5125 | 0 | 0 |  |  |  | 0 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5126 | 0 |  |  |  |  | 0 | return $val; | 
| 5127 |  |  |  |  |  |  | } | 
| 5128 |  |  |  |  |  |  | sub SetFixed16u(@) { | 
| 5129 | 0 |  |  | 0 | 0 | 0 | my $val = int(shift() * 0x100 + 0.5); | 
| 5130 | 0 |  |  |  |  | 0 | return Set16u($val, @_); | 
| 5131 |  |  |  |  |  |  | } | 
| 5132 |  |  |  |  |  |  | sub SetFixed16s(@) { | 
| 5133 | 0 |  |  | 0 | 0 | 0 | my $val = shift; | 
| 5134 | 0 | 0 |  |  |  | 0 | return Set16s(int($val * 0x100 + ($val < 0 ? -0.5 : 0.5)), @_); | 
| 5135 |  |  |  |  |  |  | } | 
| 5136 |  |  |  |  |  |  | sub SetFixed32u(@) { | 
| 5137 | 0 |  |  | 0 | 0 | 0 | my $val = int(shift() * 0x10000 + 0.5); | 
| 5138 | 0 |  |  |  |  | 0 | return Set32u($val, @_); | 
| 5139 |  |  |  |  |  |  | } | 
| 5140 |  |  |  |  |  |  | sub SetFixed32s(@) { | 
| 5141 | 12 |  |  | 12 | 0 | 24 | my $val = shift; | 
| 5142 | 12 | 100 |  |  |  | 55 | return Set32s(int($val * 0x10000 + ($val < 0 ? -0.5 : 0.5)), @_); | 
| 5143 |  |  |  |  |  |  | } | 
| 5144 |  |  |  |  |  |  | sub SetFloat(@) { | 
| 5145 | 62 |  |  | 62 | 0 | 485 | my $val = SwapBytes(pack('f',$_[0]), 4); | 
| 5146 | 62 | 50 |  |  |  | 360 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5147 | 62 |  |  |  |  | 416 | return $val; | 
| 5148 |  |  |  |  |  |  | } | 
| 5149 |  |  |  |  |  |  | sub SetDouble(@) { | 
| 5150 |  |  |  |  |  |  | # swap 32-bit words (ARM quirk) and bytes if necessary | 
| 5151 | 64 |  |  | 64 | 0 | 500 | my $val = SwapBytes(SwapWords(pack('d',$_[0])), 8); | 
| 5152 | 64 | 50 |  |  |  | 339 | $_[1] and substr(${$_[1]}, $_[2], length($val)) = $val; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5153 | 64 |  |  |  |  | 435 | return $val; | 
| 5154 |  |  |  |  |  |  | } | 
| 5155 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5156 |  |  |  |  |  |  | # hash lookups for writing binary data values | 
| 5157 |  |  |  |  |  |  | my %writeValueProc = ( | 
| 5158 |  |  |  |  |  |  | int8s => \&Set8s, | 
| 5159 |  |  |  |  |  |  | int8u => \&Set8u, | 
| 5160 |  |  |  |  |  |  | int16s => \&Set16s, | 
| 5161 |  |  |  |  |  |  | int16u => \&Set16u, | 
| 5162 |  |  |  |  |  |  | int16uRev => \&Set16uRev, | 
| 5163 |  |  |  |  |  |  | int32s => \&Set32s, | 
| 5164 |  |  |  |  |  |  | int32u => \&Set32u, | 
| 5165 |  |  |  |  |  |  | int64s => \&Set64s, | 
| 5166 |  |  |  |  |  |  | int64u => \&Set64u, | 
| 5167 |  |  |  |  |  |  | rational32s => \&SetRational32s, | 
| 5168 |  |  |  |  |  |  | rational32u => \&SetRational32u, | 
| 5169 |  |  |  |  |  |  | rational64s => \&SetRational64s, | 
| 5170 |  |  |  |  |  |  | rational64u => \&SetRational64u, | 
| 5171 |  |  |  |  |  |  | fixed16u => \&SetFixed16u, | 
| 5172 |  |  |  |  |  |  | fixed16s => \&SetFixed16s, | 
| 5173 |  |  |  |  |  |  | fixed32u => \&SetFixed32u, | 
| 5174 |  |  |  |  |  |  | fixed32s => \&SetFixed32s, | 
| 5175 |  |  |  |  |  |  | float => \&SetFloat, | 
| 5176 |  |  |  |  |  |  | double => \&SetDouble, | 
| 5177 |  |  |  |  |  |  | ifd => \&Set32u, | 
| 5178 |  |  |  |  |  |  | ); | 
| 5179 |  |  |  |  |  |  | # verify that we can write floats on this platform | 
| 5180 |  |  |  |  |  |  | { | 
| 5181 |  |  |  |  |  |  | my %writeTest = ( | 
| 5182 |  |  |  |  |  |  | float =>  [ -3.14159, 'c0490fd0' ], | 
| 5183 |  |  |  |  |  |  | double => [ -3.14159, 'c00921f9f01b866e' ], | 
| 5184 |  |  |  |  |  |  | ); | 
| 5185 |  |  |  |  |  |  | my $format; | 
| 5186 |  |  |  |  |  |  | my $oldOrder = GetByteOrder(); | 
| 5187 |  |  |  |  |  |  | SetByteOrder('MM'); | 
| 5188 |  |  |  |  |  |  | foreach $format (keys %writeTest) { | 
| 5189 |  |  |  |  |  |  | my ($val, $hex) = @{$writeTest{$format}}; | 
| 5190 |  |  |  |  |  |  | # add floating point entries if we can write them | 
| 5191 |  |  |  |  |  |  | next if unpack('H*', &{$writeValueProc{$format}}($val)) eq $hex; | 
| 5192 |  |  |  |  |  |  | delete $writeValueProc{$format};    # we can't write them | 
| 5193 |  |  |  |  |  |  | } | 
| 5194 |  |  |  |  |  |  | SetByteOrder($oldOrder); | 
| 5195 |  |  |  |  |  |  | } | 
| 5196 |  |  |  |  |  |  |  | 
| 5197 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5198 |  |  |  |  |  |  | # write binary data value (with current byte ordering) | 
| 5199 |  |  |  |  |  |  | # Inputs: 0) value, 1) format string | 
| 5200 |  |  |  |  |  |  | #         2) number of values: | 
| 5201 |  |  |  |  |  |  | #               undef = 1 for numerical types, or data length for string/undef types | 
| 5202 |  |  |  |  |  |  | #                  -1 = number of space-delimited values in the input string | 
| 5203 |  |  |  |  |  |  | #         3) optional data reference, 4) value offset (may be negative for bytes from end) | 
| 5204 |  |  |  |  |  |  | # Returns: packed value (and sets value in data) or undef on error | 
| 5205 |  |  |  |  |  |  | # Notes: May modify input value to round for integer formats | 
| 5206 |  |  |  |  |  |  | sub WriteValue($$;$$$$) | 
| 5207 |  |  |  |  |  |  | { | 
| 5208 | 1598 |  |  | 1598 | 0 | 3871 | my ($val, $format, $count, $dataPt, $offset) = @_; | 
| 5209 | 1598 |  |  |  |  | 3930 | my $proc = $writeValueProc{$format}; | 
| 5210 | 1598 |  |  |  |  | 2470 | my $packed; | 
| 5211 |  |  |  |  |  |  |  | 
| 5212 | 1598 | 100 | 66 |  |  | 4211 | if ($proc) { | 
|  |  | 50 |  |  |  |  |  | 
| 5213 | 1238 |  |  |  |  | 4307 | my @vals = split(' ',$val); | 
| 5214 | 1238 | 100 |  |  |  | 2719 | if ($count) { | 
| 5215 | 662 | 100 |  |  |  | 1738 | $count = @vals if $count < 0; | 
| 5216 |  |  |  |  |  |  | } else { | 
| 5217 | 576 |  |  |  |  | 1076 | $count = 1;   # assume 1 if count not specified | 
| 5218 |  |  |  |  |  |  | } | 
| 5219 | 1238 |  |  |  |  | 2166 | $packed = ''; | 
| 5220 | 1238 |  |  |  |  | 2746 | while ($count--) { | 
| 5221 | 1721 |  |  |  |  | 2951 | $val = shift @vals; | 
| 5222 | 1721 | 50 |  |  |  | 3687 | return undef unless defined $val; | 
| 5223 |  |  |  |  |  |  | # validate numerical formats | 
| 5224 | 1721 | 100 |  |  |  | 6647 | if ($format =~ /^int/) { | 
|  |  | 100 |  |  |  |  |  | 
| 5225 | 1232 | 50 | 33 |  |  | 3455 | unless (IsInt($val) or IsHex($val)) { | 
| 5226 | 0 | 0 |  |  |  | 0 | return undef unless IsFloat($val); | 
| 5227 |  |  |  |  |  |  | # round to nearest integer | 
| 5228 | 0 | 0 |  |  |  | 0 | $val = int($val + ($val < 0 ? -0.5 : 0.5)); | 
| 5229 | 0 |  |  |  |  | 0 | $_[0] = $val; | 
| 5230 |  |  |  |  |  |  | } | 
| 5231 |  |  |  |  |  |  | } elsif (not IsFloat($val)) { | 
| 5232 | 7 | 50 | 33 |  |  | 132 | return undef unless $format =~ /^rational/ and ($val eq 'inf' or | 
|  |  |  | 33 |  |  |  |  | 
| 5233 |  |  |  |  |  |  | $val eq 'undef' or IsRational($val)); | 
| 5234 |  |  |  |  |  |  | } | 
| 5235 | 1721 |  |  |  |  | 4804 | $packed .= &$proc($val); | 
| 5236 |  |  |  |  |  |  | } | 
| 5237 |  |  |  |  |  |  | } elsif ($format eq 'string' or $format eq 'undef') { | 
| 5238 | 360 | 100 |  |  |  | 1052 | $format eq 'string' and $val .= "\0";   # null-terminate strings | 
| 5239 | 360 | 100 | 66 |  |  | 1178 | if ($count and $count > 0) { | 
| 5240 | 61 |  |  |  |  | 174 | my $diff = $count - length($val); | 
| 5241 | 61 | 100 |  |  |  | 224 | if ($diff) { | 
| 5242 |  |  |  |  |  |  | #warn "wrong string length!\n"; | 
| 5243 |  |  |  |  |  |  | # adjust length of string to match specified count | 
| 5244 | 29 | 100 |  |  |  | 77 | if ($diff < 0) { | 
| 5245 | 22 | 50 |  |  |  | 63 | if ($format eq 'string') { | 
| 5246 | 22 | 50 |  |  |  | 58 | return undef unless $count; | 
| 5247 | 22 |  |  |  |  | 64 | $val = substr($val, 0, $count - 1) . "\0"; | 
| 5248 |  |  |  |  |  |  | } else { | 
| 5249 | 0 |  |  |  |  | 0 | $val = substr($val, 0, $count); | 
| 5250 |  |  |  |  |  |  | } | 
| 5251 |  |  |  |  |  |  | } else { | 
| 5252 | 7 |  |  |  |  | 25 | $val .= "\0" x $diff; | 
| 5253 |  |  |  |  |  |  | } | 
| 5254 |  |  |  |  |  |  | } | 
| 5255 |  |  |  |  |  |  | } else { | 
| 5256 | 299 |  |  |  |  | 541 | $count = length($val); | 
| 5257 |  |  |  |  |  |  | } | 
| 5258 | 360 | 100 |  |  |  | 855 | $dataPt and substr($$dataPt, $offset, $count) = $val; | 
| 5259 | 360 |  |  |  |  | 1154 | return $val; | 
| 5260 |  |  |  |  |  |  | } else { | 
| 5261 | 0 |  |  |  |  | 0 | warn "Sorry, Can't write $format values on this platform\n"; | 
| 5262 | 0 |  |  |  |  | 0 | return undef; | 
| 5263 |  |  |  |  |  |  | } | 
| 5264 | 1238 | 100 |  |  |  | 3234 | $dataPt and substr($$dataPt, $offset, length($packed)) = $packed; | 
| 5265 | 1238 |  |  |  |  | 3427 | return $packed; | 
| 5266 |  |  |  |  |  |  | } | 
| 5267 |  |  |  |  |  |  |  | 
| 5268 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5269 |  |  |  |  |  |  | # Encode bit mask (the inverse of DecodeBits()) | 
| 5270 |  |  |  |  |  |  | # Inputs: 0) value to encode, 1) Reference to hash for encoding (or undef) | 
| 5271 |  |  |  |  |  |  | #         2) optional number of bits per word (defaults to 32), 3) total bits | 
| 5272 |  |  |  |  |  |  | # Returns: bit mask or undef on error (plus error string in list context) | 
| 5273 |  |  |  |  |  |  | sub EncodeBits($$;$$) | 
| 5274 |  |  |  |  |  |  | { | 
| 5275 | 104 |  |  | 104 | 0 | 309 | my ($val, $lookup, $bits, $num) = @_; | 
| 5276 | 104 | 100 |  |  |  | 328 | $bits or $bits = 32; | 
| 5277 | 104 | 100 |  |  |  | 352 | $num or $num = $bits; | 
| 5278 | 104 |  |  |  |  | 396 | my $words = int(($num + $bits - 1) / $bits); | 
| 5279 | 104 |  |  |  |  | 333 | my @outVal = (0) x $words; | 
| 5280 | 104 | 100 |  |  |  | 318 | if ($val ne '(none)') { | 
| 5281 | 85 |  |  |  |  | 370 | my @vals = split /\s*,\s*/, $val; | 
| 5282 | 85 |  |  |  |  | 256 | foreach $val (@vals) { | 
| 5283 | 42 |  |  |  |  | 160 | my $bit; | 
| 5284 | 42 | 50 |  |  |  | 113 | if ($lookup) { | 
| 5285 | 42 |  |  |  |  | 135 | $bit = ReverseLookup($val, $lookup); | 
| 5286 |  |  |  |  |  |  | # (Note: may get non-numerical $bit values from Unknown() tags) | 
| 5287 | 42 | 100 |  |  |  | 163 | unless (defined $bit) { | 
| 5288 | 33 | 50 |  |  |  | 130 | if ($val =~ /\[(\d+)\]/) { # numerical bit specification | 
| 5289 | 0 |  |  |  |  | 0 | $bit = $1; | 
| 5290 |  |  |  |  |  |  | } else { | 
| 5291 |  |  |  |  |  |  | # don't return error string unless more than one value | 
| 5292 | 33 | 100 | 66 |  |  | 249 | return undef unless @vals > 1 and wantarray; | 
| 5293 | 2 |  |  |  |  | 23 | return (undef, "no match for '${val}'"); | 
| 5294 |  |  |  |  |  |  | } | 
| 5295 |  |  |  |  |  |  | } | 
| 5296 |  |  |  |  |  |  | } else { | 
| 5297 | 0 |  |  |  |  | 0 | $bit = $val; | 
| 5298 |  |  |  |  |  |  | } | 
| 5299 | 9 | 50 | 33 |  |  | 61 | unless (IsInt($bit) and $bit < $num) { | 
| 5300 | 0 | 0 |  |  |  | 0 | return undef unless wantarray; | 
| 5301 | 0 | 0 |  |  |  | 0 | return (undef, IsInt($bit) ? 'bit number too high' : 'not an integer'); | 
| 5302 |  |  |  |  |  |  | } | 
| 5303 | 9 |  |  |  |  | 46 | my $word = int($bit / $bits); | 
| 5304 | 9 |  |  |  |  | 50 | $outVal[$word] |= (1 << ($bit - $word * $bits)); | 
| 5305 |  |  |  |  |  |  | } | 
| 5306 |  |  |  |  |  |  | } | 
| 5307 | 71 |  |  |  |  | 381 | return "@outVal"; | 
| 5308 |  |  |  |  |  |  | } | 
| 5309 |  |  |  |  |  |  |  | 
| 5310 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5311 |  |  |  |  |  |  | # get current position in output file (or end of file if a scalar reference) | 
| 5312 |  |  |  |  |  |  | # Inputs: 0) file or scalar reference | 
| 5313 |  |  |  |  |  |  | # Returns: Current position or -1 on error | 
| 5314 |  |  |  |  |  |  | sub Tell($) | 
| 5315 |  |  |  |  |  |  | { | 
| 5316 | 325 |  |  | 325 | 0 | 757 | my $outfile = shift; | 
| 5317 | 325 | 100 |  |  |  | 1482 | if (UNIVERSAL::isa($outfile,'GLOB')) { | 
| 5318 | 296 |  |  |  |  | 2106 | return tell($outfile); | 
| 5319 |  |  |  |  |  |  | } else { | 
| 5320 | 29 |  |  |  |  | 168 | return length($$outfile); | 
| 5321 |  |  |  |  |  |  | } | 
| 5322 |  |  |  |  |  |  | } | 
| 5323 |  |  |  |  |  |  |  | 
| 5324 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5325 |  |  |  |  |  |  | # write to file or memory | 
| 5326 |  |  |  |  |  |  | # Inputs: 0) file or scalar reference, 1-N) list of stuff to write | 
| 5327 |  |  |  |  |  |  | # Returns: true on success | 
| 5328 |  |  |  |  |  |  | sub Write($@) | 
| 5329 |  |  |  |  |  |  | { | 
| 5330 | 3736 |  |  | 3736 | 0 | 6592 | my $outfile = shift; | 
| 5331 | 3736 | 100 |  |  |  | 12327 | if (UNIVERSAL::isa($outfile,'GLOB')) { | 
|  |  | 50 |  |  |  |  |  | 
| 5332 | 2311 |  |  |  |  | 19059 | return print $outfile @_; | 
| 5333 |  |  |  |  |  |  | } elsif (ref $outfile eq 'SCALAR') { | 
| 5334 | 1425 |  |  |  |  | 6291 | $$outfile .= join('', @_); | 
| 5335 | 1425 |  |  |  |  | 5360 | return 1; | 
| 5336 |  |  |  |  |  |  | } | 
| 5337 | 0 |  |  |  |  | 0 | return 0; | 
| 5338 |  |  |  |  |  |  | } | 
| 5339 |  |  |  |  |  |  |  | 
| 5340 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5341 |  |  |  |  |  |  | # Write trailer buffer to file (applying fixups if necessary) | 
| 5342 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) trailer dirInfo ref, 2) output file ref | 
| 5343 |  |  |  |  |  |  | # Returns: 1 on success | 
| 5344 |  |  |  |  |  |  | sub WriteTrailerBuffer($$$) | 
| 5345 |  |  |  |  |  |  | { | 
| 5346 | 12 |  |  | 12 | 0 | 48 | my ($self, $trailInfo, $outfile) = @_; | 
| 5347 | 12 | 50 |  |  |  | 67 | if ($$self{DEL_GROUP}{Trailer}) { | 
| 5348 | 0 |  |  |  |  | 0 | $self->VPrint(0, "  Deleting trailer ($$trailInfo{Offset} bytes)\n"); | 
| 5349 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 5350 | 0 |  |  |  |  | 0 | return 1; | 
| 5351 |  |  |  |  |  |  | } | 
| 5352 | 12 |  |  |  |  | 50 | my $pos = Tell($outfile); | 
| 5353 | 12 |  |  |  |  | 41 | my $trailPt = $$trailInfo{OutFile}; | 
| 5354 |  |  |  |  |  |  | # apply fixup if necessary (AFCP requires this) | 
| 5355 | 12 | 100 |  |  |  | 70 | if ($$trailInfo{Fixup}) { | 
| 5356 | 8 | 50 |  |  |  | 31 | if ($pos > 0) { | 
| 5357 |  |  |  |  |  |  | # shift offsets to final AFCP location and write it out | 
| 5358 | 8 |  |  |  |  | 23 | $$trailInfo{Fixup}{Shift} += $pos; | 
| 5359 | 8 |  |  |  |  | 40 | $$trailInfo{Fixup}->ApplyFixup($trailPt); | 
| 5360 |  |  |  |  |  |  | } else { | 
| 5361 | 0 |  |  |  |  | 0 | $self->Error("Can't get file position for trailer offset fixup",1); | 
| 5362 |  |  |  |  |  |  | } | 
| 5363 |  |  |  |  |  |  | } | 
| 5364 | 12 |  |  |  |  | 82 | return Write($outfile, $$trailPt); | 
| 5365 |  |  |  |  |  |  | } | 
| 5366 |  |  |  |  |  |  |  | 
| 5367 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5368 |  |  |  |  |  |  | # Add trailers as a block | 
| 5369 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) [optional] trailer data raf, | 
| 5370 |  |  |  |  |  |  | #         1 or 2-N) trailer types to add (or none to add all) | 
| 5371 |  |  |  |  |  |  | # Returns: new trailer ref, or undef | 
| 5372 |  |  |  |  |  |  | # - increments CHANGED if trailer was added | 
| 5373 |  |  |  |  |  |  | sub AddNewTrailers($;@) | 
| 5374 |  |  |  |  |  |  | { | 
| 5375 | 130 |  |  | 130 | 0 | 485 | my ($self, @types) = @_; | 
| 5376 | 130 |  |  |  |  | 265 | my $trailPt; | 
| 5377 | 130 | 100 |  |  |  | 486 | ref $types[0] and $trailPt = shift @types; | 
| 5378 | 130 | 100 |  |  |  | 505 | $types[0] or shift @types; # (in case undef data ref is passed) | 
| 5379 |  |  |  |  |  |  | # add all possible trailers if none specified (currently only CanonVRD) | 
| 5380 | 130 | 100 |  |  |  | 661 | @types or @types = qw(CanonVRD CanonDR4); | 
| 5381 |  |  |  |  |  |  | # add trailers as a block (if not done already) | 
| 5382 | 130 |  |  |  |  | 317 | my $type; | 
| 5383 | 130 |  |  |  |  | 473 | foreach $type (@types) { | 
| 5384 | 253 | 100 |  |  |  | 1406 | next unless $$self{NEW_VALUE}{$Image::ExifTool::Extra{$type}}; | 
| 5385 | 10 | 100 |  |  |  | 64 | next if $$self{"Did$type"}; | 
| 5386 | 9 | 100 |  |  |  | 78 | my $val = $self->GetNewValue($type) or next; | 
| 5387 |  |  |  |  |  |  | # DR4 record must be wrapped in VRD trailer package | 
| 5388 | 8 | 100 |  |  |  | 46 | if ($type eq 'CanonDR4') { | 
| 5389 | 3 | 100 |  |  |  | 17 | next if $$self{DidCanonVRD};    # (only allow one VRD trailer) | 
| 5390 | 2 |  |  |  |  | 23 | require Image::ExifTool::CanonVRD; | 
| 5391 | 2 |  |  |  |  | 380 | $val = Image::ExifTool::CanonVRD::WrapDR4($val); | 
| 5392 | 2 |  |  |  |  | 8 | $$self{DidCanonVRD} = 1; | 
| 5393 |  |  |  |  |  |  | } | 
| 5394 | 7 | 50 |  |  |  | 45 | my $verb = $trailPt ? 'Writing' : 'Adding'; | 
| 5395 | 7 |  |  |  |  | 62 | $self->VPrint(0, "  $verb $type as a block\n"); | 
| 5396 | 7 | 50 |  |  |  | 42 | if ($trailPt) { | 
| 5397 | 0 |  |  |  |  | 0 | $$trailPt .= $val; | 
| 5398 |  |  |  |  |  |  | } else { | 
| 5399 | 7 |  |  |  |  | 21 | $trailPt = \$val; | 
| 5400 |  |  |  |  |  |  | } | 
| 5401 | 7 |  |  |  |  | 30 | $$self{"Did$type"} = 1; | 
| 5402 | 7 |  |  |  |  | 26 | ++$$self{CHANGED}; | 
| 5403 |  |  |  |  |  |  | } | 
| 5404 | 130 |  |  |  |  | 501 | return $trailPt; | 
| 5405 |  |  |  |  |  |  | } | 
| 5406 |  |  |  |  |  |  |  | 
| 5407 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5408 |  |  |  |  |  |  | # Write segment, splitting up into multiple segments if necessary | 
| 5409 |  |  |  |  |  |  | # Inputs: 0) file or scalar reference, 1) segment marker | 
| 5410 |  |  |  |  |  |  | #         2) segment header, 3) segment data ref, 4) segment type | 
| 5411 |  |  |  |  |  |  | # Returns: number of segments written, or 0 on error | 
| 5412 |  |  |  |  |  |  | # Notes: Writes a single empty segment if data is empty | 
| 5413 |  |  |  |  |  |  | sub WriteMultiSegment($$$$;$) | 
| 5414 |  |  |  |  |  |  | { | 
| 5415 | 110 |  |  | 110 | 0 | 487 | my ($outfile, $marker, $header, $dataPt, $type) = @_; | 
| 5416 | 110 | 100 |  |  |  | 474 | $type or $type = ''; | 
| 5417 | 110 |  |  |  |  | 349 | my $len = length($$dataPt); | 
| 5418 | 110 |  |  |  |  | 439 | my $hdr = "\xff" . chr($marker); | 
| 5419 | 110 |  |  |  |  | 230 | my $count = 0; | 
| 5420 | 110 |  |  |  |  | 296 | my $maxLen = $maxSegmentLen - length($header); | 
| 5421 | 110 | 100 |  |  |  | 442 | $maxLen -= 2 if $type eq 'ICC'; # leave room for segment counters | 
| 5422 | 110 |  |  |  |  | 475 | my $num = int(($len + $maxLen - 1) / $maxLen);  # number of segments to write | 
| 5423 | 110 |  |  |  |  | 233 | my $n = 0; | 
| 5424 |  |  |  |  |  |  | # write data, splitting into multiple segments if necessary | 
| 5425 |  |  |  |  |  |  | # (each segment gets its own header) | 
| 5426 | 110 |  |  |  |  | 275 | for (;;) { | 
| 5427 | 110 |  |  |  |  | 236 | ++$count; | 
| 5428 | 110 |  |  |  |  | 291 | my $size = $len - $n; | 
| 5429 | 110 | 50 |  |  |  | 431 | if ($size > $maxLen) { | 
| 5430 | 0 |  |  |  |  | 0 | $size = $maxLen; | 
| 5431 |  |  |  |  |  |  | # avoid starting an Extended EXIF segment with a valid TIFF header | 
| 5432 |  |  |  |  |  |  | # (because we would interpret that as a separate EXIF segment) | 
| 5433 | 0 | 0 | 0 |  |  | 0 | --$size if $type eq 'EXIF' and $n+$maxLen <= $len-4 and | 
|  |  |  | 0 |  |  |  |  | 
| 5434 |  |  |  |  |  |  | substr($$dataPt, $n+$maxLen, 4) =~ /^(MM\0\x2a|II\x2a\0)/; | 
| 5435 |  |  |  |  |  |  | } | 
| 5436 | 110 |  |  |  |  | 662 | my $buff = substr($$dataPt,$n,$size); | 
| 5437 | 110 |  |  |  |  | 253 | $n += $size; | 
| 5438 | 110 |  |  |  |  | 286 | $size += length($header); | 
| 5439 | 110 | 100 |  |  |  | 409 | if ($type eq 'ICC') { | 
| 5440 | 3 |  |  |  |  | 22 | $buff = pack('CC', $count, $num) . $buff; | 
| 5441 | 3 |  |  |  |  | 10 | $size += 2; | 
| 5442 |  |  |  |  |  |  | } | 
| 5443 |  |  |  |  |  |  | # write the new segment with appropriate header | 
| 5444 | 110 |  |  |  |  | 557 | my $segHdr = $hdr . pack('n', $size + 2); | 
| 5445 | 110 | 50 |  |  |  | 431 | Write($outfile, $segHdr, $header, $buff) or return 0; | 
| 5446 | 110 | 50 |  |  |  | 539 | last if $n >= $len; | 
| 5447 |  |  |  |  |  |  | } | 
| 5448 | 110 |  |  |  |  | 464 | return $count; | 
| 5449 |  |  |  |  |  |  | } | 
| 5450 |  |  |  |  |  |  |  | 
| 5451 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5452 |  |  |  |  |  |  | # Write XMP segment(s) to JPEG file | 
| 5453 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) outfile ref, 2) XMP data ref, | 
| 5454 |  |  |  |  |  |  | #         3) extended XMP data ref, 4) 32-char extended XMP GUID (or undef if no extended data) | 
| 5455 |  |  |  |  |  |  | # Returns: true on success, false on write error | 
| 5456 |  |  |  |  |  |  | sub WriteMultiXMP($$$$$) | 
| 5457 |  |  |  |  |  |  | { | 
| 5458 | 34 |  |  | 34 | 0 | 180 | my ($self, $outfile, $dataPt, $extPt, $guid) = @_; | 
| 5459 | 34 |  |  |  |  | 94 | my $success = 1; | 
| 5460 |  |  |  |  |  |  |  | 
| 5461 |  |  |  |  |  |  | # write main XMP segment | 
| 5462 | 34 |  |  |  |  | 131 | my $size = length($$dataPt) + length($xmpAPP1hdr); | 
| 5463 | 34 | 50 |  |  |  | 141 | if ($size > $maxXMPLen) { | 
| 5464 | 0 |  |  |  |  | 0 | $self->Error("XMP block too large for JPEG segment! ($size bytes)", 1); | 
| 5465 | 0 |  |  |  |  | 0 | return 1; | 
| 5466 |  |  |  |  |  |  | } | 
| 5467 | 34 |  |  |  |  | 210 | my $app1hdr = "\xff\xe1" . pack('n', $size + 2); | 
| 5468 | 34 | 50 |  |  |  | 169 | Write($outfile, $app1hdr, $xmpAPP1hdr, $$dataPt) or $success = 0; | 
| 5469 |  |  |  |  |  |  | # write extended XMP segment(s) if necessary | 
| 5470 | 34 | 50 |  |  |  | 206 | if (defined $guid) { | 
| 5471 | 0 |  |  |  |  | 0 | $size = length($$extPt); | 
| 5472 | 0 |  |  |  |  | 0 | my $maxLen = $maxXMPLen - 75; # maximum size without 75-byte header | 
| 5473 | 0 |  |  |  |  | 0 | my $off; | 
| 5474 | 0 |  |  |  |  | 0 | for ($off=0; $off<$size; $off+=$maxLen) { | 
| 5475 |  |  |  |  |  |  | # header(75) = signature(35) + guid(32) + size(4) + offset(4) | 
| 5476 | 0 |  |  |  |  | 0 | my $len = $size - $off; | 
| 5477 | 0 | 0 |  |  |  | 0 | $len = $maxLen if $len > $maxLen; | 
| 5478 | 0 |  |  |  |  | 0 | $app1hdr = "\xff\xe1" . pack('n', $len + 75 + 2); | 
| 5479 | 0 |  |  |  |  | 0 | $self->VPrint(0, "Writing extended XMP segment ($len bytes)\n"); | 
| 5480 | 0 | 0 |  |  |  | 0 | Write($outfile, $app1hdr, $xmpExtAPP1hdr, $guid, pack('N2', $size, $off), | 
| 5481 |  |  |  |  |  |  | substr($$extPt, $off, $len)) or $success = 0; | 
| 5482 |  |  |  |  |  |  | } | 
| 5483 |  |  |  |  |  |  | } | 
| 5484 | 34 |  |  |  |  | 194 | return $success; | 
| 5485 |  |  |  |  |  |  | } | 
| 5486 |  |  |  |  |  |  |  | 
| 5487 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 5488 |  |  |  |  |  |  | # WriteJPEG : Write JPEG image | 
| 5489 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) dirInfo reference | 
| 5490 |  |  |  |  |  |  | # Returns: 1 on success, 0 if this wasn't a valid JPEG file, or -1 if | 
| 5491 |  |  |  |  |  |  | #          an output file was specified and a write error occurred | 
| 5492 |  |  |  |  |  |  | sub WriteJPEG($$) | 
| 5493 |  |  |  |  |  |  | { | 
| 5494 | 111 |  |  | 111 | 0 | 462 | my ($self, $dirInfo) = @_; | 
| 5495 | 111 |  |  |  |  | 403 | my $outfile = $$dirInfo{OutFile}; | 
| 5496 | 111 |  |  |  |  | 429 | my $raf = $$dirInfo{RAF}; | 
| 5497 | 111 |  |  |  |  | 356 | my ($ch, $s, $length,$err, %doneDir, $isEXV, $creatingEXV); | 
| 5498 | 111 |  |  |  |  | 318 | my $verbose = $$self{OPTIONS}{Verbose}; | 
| 5499 | 111 |  |  |  |  | 398 | my $out = $$self{OPTIONS}{TextOut}; | 
| 5500 | 111 |  |  |  |  | 302 | my $rtnVal = 0; | 
| 5501 | 111 |  |  |  |  | 377 | my ($writeBuffer, $oldOutfile); # used to buffer writing until PreviewImage position is known | 
| 5502 |  |  |  |  |  |  |  | 
| 5503 |  |  |  |  |  |  | # check to be sure this is a valid JPG or EXV file | 
| 5504 | 111 | 100 | 100 |  |  | 696 | unless ($raf->Read($s,2) == 2 and $s eq "\xff\xd8") { | 
| 5505 | 2 | 100 | 66 |  |  | 15 | if (defined $s and length $s) { | 
| 5506 | 1 | 50 | 33 |  |  | 14 | return 0 unless $s eq "\xff\x01" and $raf->Read($s,5) == 5 and $s eq 'Exiv2'; | 
|  |  |  | 33 |  |  |  |  | 
| 5507 |  |  |  |  |  |  | } else { | 
| 5508 | 1 | 50 |  |  |  | 9 | return 0 unless $$self{FILE_TYPE} eq 'EXV'; | 
| 5509 | 1 |  |  |  |  | 5 | $s = 'Exiv2'; | 
| 5510 | 1 |  |  |  |  | 4 | $creatingEXV = 1; | 
| 5511 |  |  |  |  |  |  | } | 
| 5512 | 2 | 50 |  |  |  | 8 | Write($outfile,"\xff\x01") or $err = 1; | 
| 5513 | 2 |  |  |  |  | 10 | $isEXV = 1; | 
| 5514 |  |  |  |  |  |  | } | 
| 5515 |  |  |  |  |  |  |  | 
| 5516 | 111 |  |  |  |  | 445 | delete $$self{PREVIEW_INFO};   # reset preview information | 
| 5517 | 111 |  |  |  |  | 323 | delete $$self{DEL_PREVIEW};    # reset flag to delete preview | 
| 5518 |  |  |  |  |  |  |  | 
| 5519 | 111 | 50 |  |  |  | 612 | Write($outfile, $s) or $err = 1; | 
| 5520 |  |  |  |  |  |  | # figure out what segments we need to write for the tags we have set | 
| 5521 | 111 |  |  |  |  | 579 | my $addDirs = $$self{ADD_DIRS}; | 
| 5522 | 111 |  |  |  |  | 336 | my $editDirs = $$self{EDIT_DIRS}; | 
| 5523 | 111 |  |  |  |  | 436 | my $delGroup = $$self{DEL_GROUP}; | 
| 5524 | 111 |  |  |  |  | 341 | my $path = $$self{PATH}; | 
| 5525 | 111 |  |  |  |  | 314 | my $pn = scalar @$path; | 
| 5526 |  |  |  |  |  |  |  | 
| 5527 |  |  |  |  |  |  | # set input record separator to 0xff (the JPEG marker) to make reading quicker | 
| 5528 | 111 |  |  |  |  | 802 | local $/ = "\xff"; | 
| 5529 |  |  |  |  |  |  | # | 
| 5530 |  |  |  |  |  |  | # pre-scan image to determine if any create-able segment already exists | 
| 5531 |  |  |  |  |  |  | # | 
| 5532 | 111 |  |  |  |  | 597 | my $pos = $raf->Tell(); | 
| 5533 | 111 |  |  |  |  | 452 | my ($marker, @dirOrder, %dirCount); | 
| 5534 | 111 |  |  |  |  | 330 | Prescan: for (;;) { | 
| 5535 |  |  |  |  |  |  | # read up to next marker (JPEG markers begin with 0xff) | 
| 5536 | 796 | 100 |  |  |  | 2740 | $raf->ReadLine($s) or last; | 
| 5537 |  |  |  |  |  |  | # JPEG markers can be padded with unlimited 0xff's | 
| 5538 | 795 |  |  |  |  | 1607 | for (;;) { | 
| 5539 | 795 | 50 |  |  |  | 2563 | $raf->Read($ch, 1) or last Prescan; | 
| 5540 | 795 |  |  |  |  | 1609 | $marker = ord($ch); | 
| 5541 | 795 | 50 |  |  |  | 2031 | last unless $marker == 0xff; | 
| 5542 |  |  |  |  |  |  | } | 
| 5543 | 795 |  |  |  |  | 1815 | my $dirName; | 
| 5544 |  |  |  |  |  |  | # stop pre-scan at SOS (end of meta information) or EOI (end of image) | 
| 5545 | 795 | 100 | 100 |  |  | 3143 | if ($marker == 0xda or $marker == 0xd9) { | 
| 5546 | 110 |  |  |  |  | 688 | $dirName = $jpegMarker{$marker}; | 
| 5547 | 110 |  |  |  |  | 420 | push(@dirOrder, $dirName); | 
| 5548 | 110 |  |  |  |  | 365 | $dirCount{$dirName} = 1; | 
| 5549 | 110 |  |  |  |  | 342 | last; | 
| 5550 |  |  |  |  |  |  | } | 
| 5551 |  |  |  |  |  |  | # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc) | 
| 5552 | 685 | 100 | 66 |  |  | 5855 | if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) { | 
|  |  | 50 | 100 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 5553 | 109 | 50 |  |  |  | 483 | last unless $raf->Seek(7, 1); | 
| 5554 |  |  |  |  |  |  | # read data for all markers except stand-alone | 
| 5555 |  |  |  |  |  |  | # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7) | 
| 5556 |  |  |  |  |  |  | } elsif ($marker!=0x00 and $marker!=0x01 and ($marker<0xd0 or $marker>0xd7)) { | 
| 5557 |  |  |  |  |  |  | # read record length word | 
| 5558 | 576 | 50 |  |  |  | 1563 | last unless $raf->Read($s, 2) == 2; | 
| 5559 | 576 |  |  |  |  | 2104 | my $len = unpack('n',$s);   # get data length | 
| 5560 | 576 | 50 | 33 |  |  | 2491 | last unless defined($len) and $len >= 2; | 
| 5561 | 576 |  |  |  |  | 998 | $len -= 2;  # subtract size of length word | 
| 5562 | 576 | 100 |  |  |  | 1406 | if (($marker & 0xf0) == 0xe0) {  # is this an APP segment? | 
| 5563 | 347 | 100 |  |  |  | 1230 | my $n = $len < 64 ? $len : 64; | 
| 5564 | 347 | 50 |  |  |  | 995 | $raf->Read($s, $n) == $n or last; | 
| 5565 | 347 |  |  |  |  | 1263 | $len -= $n; | 
| 5566 |  |  |  |  |  |  | # Note: only necessary to recognize APP segments that we can create, | 
| 5567 |  |  |  |  |  |  | # or delete as a group (and the names below should match @delGroups) | 
| 5568 | 347 | 100 |  |  |  | 1941 | if ($marker == 0xe0) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 5569 | 45 | 100 |  |  |  | 268 | $s =~ /^JFIF\0/         and $dirName = 'JFIF'; | 
| 5570 | 45 | 100 |  |  |  | 224 | $s =~ /^JFXX\0\x10/     and $dirName = 'JFXX'; | 
| 5571 | 45 | 100 |  |  |  | 237 | $s =~ /^(II|MM).{4}HEAPJPGM/s and $dirName = 'CIFF'; | 
| 5572 |  |  |  |  |  |  | } elsif ($marker == 0xe1) { | 
| 5573 | 84 | 100 |  |  |  | 688 | if ($s =~ /^(.{0,4})Exif\0.(.{1,4})/is) { | 
| 5574 | 60 |  |  |  |  | 220 | $dirName = 'IFD0'; | 
| 5575 | 60 |  |  |  |  | 381 | my ($junk, $bytes) = ($1, $2); | 
| 5576 |  |  |  |  |  |  | # support multi-segment EXIF | 
| 5577 | 60 | 0 | 66 |  |  | 929 | if (@dirOrder and $dirOrder[-1] =~ /^(IFD0|ExtendedEXIF)$/ and | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 5578 |  |  |  |  |  |  | not length $junk and $bytes !~ /^(MM\0\x2a|II\x2a\0)/) | 
| 5579 |  |  |  |  |  |  | { | 
| 5580 | 0 |  |  |  |  | 0 | $dirName = 'ExtendedEXIF'; | 
| 5581 |  |  |  |  |  |  | } | 
| 5582 |  |  |  |  |  |  | } | 
| 5583 | 84 | 100 |  |  |  | 1082 | $s =~ /^$xmpAPP1hdr/    and $dirName = 'XMP'; | 
| 5584 | 84 | 100 |  |  |  | 886 | $s =~ /^$xmpExtAPP1hdr/ and $dirName = 'XMP'; | 
| 5585 |  |  |  |  |  |  | } elsif ($marker == 0xe2) { | 
| 5586 | 55 | 100 |  |  |  | 298 | $s =~ /^ICC_PROFILE\0/  and $dirName = 'ICC_Profile'; | 
| 5587 | 55 | 100 |  |  |  | 258 | $s =~ /^FPXR\0/         and $dirName = 'FlashPix'; | 
| 5588 | 55 | 100 |  |  |  | 204 | $s =~ /^MPF\0/          and $dirName = 'MPF'; | 
| 5589 |  |  |  |  |  |  | } elsif ($marker == 0xe3) { | 
| 5590 | 9 | 50 |  |  |  | 146 | $s =~ /^(Meta|META|Exif)\0\0/ and $dirName = 'Meta'; | 
| 5591 |  |  |  |  |  |  | } elsif ($marker == 0xe5) { | 
| 5592 | 9 | 50 |  |  |  | 97 | $s =~ /^RMETA\0/        and $dirName = 'RMETA'; | 
| 5593 |  |  |  |  |  |  | } elsif ($marker == 0xec) { | 
| 5594 | 19 | 100 |  |  |  | 204 | $s =~ /^Ducky/          and $dirName = 'Ducky'; | 
| 5595 |  |  |  |  |  |  | } elsif ($marker == 0xed) { | 
| 5596 | 29 | 100 |  |  |  | 454 | $s =~ /^$psAPP13hdr/    and $dirName = 'Photoshop'; | 
| 5597 |  |  |  |  |  |  | } elsif ($marker == 0xee) { | 
| 5598 | 16 | 50 |  |  |  | 139 | $s =~ /^Adobe/          and $dirName = 'Adobe'; | 
| 5599 |  |  |  |  |  |  | } | 
| 5600 |  |  |  |  |  |  | # initialize doneDir as a flag that the directory exists | 
| 5601 |  |  |  |  |  |  | # (unless we are deleting it anyway) | 
| 5602 | 347 | 100 | 100 |  |  | 2734 | $doneDir{$dirName} = 0 if defined $dirName and not $$delGroup{$dirName}; | 
| 5603 |  |  |  |  |  |  | } | 
| 5604 | 576 | 50 |  |  |  | 1788 | $raf->Seek($len, 1) or last; | 
| 5605 |  |  |  |  |  |  | } | 
| 5606 | 685 | 100 |  |  |  | 3251 | $dirName or $dirName = JpegMarkerName($marker); | 
| 5607 | 685 |  | 100 |  |  | 3463 | $dirCount{$dirName} = ($dirCount{$dirName} || 0) + 1; | 
| 5608 | 685 |  |  |  |  | 1807 | push @dirOrder, $dirName; | 
| 5609 |  |  |  |  |  |  | } | 
| 5610 | 111 | 100 | 100 |  |  | 814 | unless ($marker and $marker == 0xda) { | 
| 5611 | 2 | 50 |  |  |  | 10 | $isEXV or $self->Error('Corrupted JPEG image'), return 1; | 
| 5612 | 2 | 50 | 66 |  |  | 16 | $marker and $marker != 0xd9 and $self->Error('Corrupted EXV file'), return 1; | 
| 5613 |  |  |  |  |  |  | } | 
| 5614 | 111 | 50 |  |  |  | 1004 | $raf->Seek($pos, 0) or $self->Error('Seek error'), return 1; | 
| 5615 |  |  |  |  |  |  | # | 
| 5616 |  |  |  |  |  |  | # re-write the image | 
| 5617 |  |  |  |  |  |  | # | 
| 5618 | 111 |  |  |  |  | 1075 | my ($combinedSegData, $segPos, $firstSegPos, %extendedXMP); | 
| 5619 | 111 |  |  |  |  | 0 | my (@iccChunk, $iccChunkCount, $iccChunksTotal); | 
| 5620 |  |  |  |  |  |  | # read through each segment in the JPEG file | 
| 5621 | 111 |  |  |  |  | 255 | Marker: for (;;) { | 
| 5622 |  |  |  |  |  |  |  | 
| 5623 |  |  |  |  |  |  | # read up to next marker (JPEG markers begin with 0xff) | 
| 5624 | 796 |  |  |  |  | 1736 | my $segJunk; | 
| 5625 | 796 | 100 |  |  |  | 2930 | $raf->ReadLine($segJunk) or $segJunk = ''; | 
| 5626 |  |  |  |  |  |  | # remove the 0xff but write the rest of the junk up to this point | 
| 5627 |  |  |  |  |  |  | # (this will handle the data after the first 7 bytes of SOF segments) | 
| 5628 | 796 |  |  |  |  | 2147 | chomp($segJunk); | 
| 5629 | 796 | 100 |  |  |  | 2232 | Write($outfile, $segJunk) if length $segJunk; | 
| 5630 |  |  |  |  |  |  | # JPEG markers can be padded with unlimited 0xff's | 
| 5631 | 796 |  |  |  |  | 1295 | for (;;) { | 
| 5632 | 796 | 100 |  |  |  | 2460 | if ($raf->Read($ch, 1)) { | 
|  |  | 50 |  |  |  |  |  | 
| 5633 | 795 |  |  |  |  | 1657 | $marker = ord($ch); | 
| 5634 | 795 | 50 |  |  |  | 2189 | last unless $marker == 0xff; | 
| 5635 |  |  |  |  |  |  | } elsif ($creatingEXV) { | 
| 5636 |  |  |  |  |  |  | # create EXV from scratch | 
| 5637 | 1 |  |  |  |  | 4 | $marker = 0xd9; # EOI | 
| 5638 | 1 |  |  |  |  | 4 | push @dirOrder, 'EOI'; | 
| 5639 | 1 |  |  |  |  | 7 | $dirCount{EOI} = 1; | 
| 5640 | 1 |  |  |  |  | 4 | last; | 
| 5641 |  |  |  |  |  |  | } else { | 
| 5642 | 0 |  |  |  |  | 0 | $self->Error('Format error'); | 
| 5643 | 0 |  |  |  |  | 0 | return 1; | 
| 5644 |  |  |  |  |  |  | } | 
| 5645 |  |  |  |  |  |  | } | 
| 5646 |  |  |  |  |  |  | # read the segment data | 
| 5647 | 796 |  |  |  |  | 1442 | my $segData; | 
| 5648 |  |  |  |  |  |  | # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc) | 
| 5649 | 796 | 100 | 66 |  |  | 8631 | if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 5650 | 109 | 50 |  |  |  | 479 | last unless $raf->Read($segData, 7) == 7; | 
| 5651 |  |  |  |  |  |  | # read data for all markers except stand-alone | 
| 5652 |  |  |  |  |  |  | # markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, EOI, RST0-RST7) | 
| 5653 |  |  |  |  |  |  | } elsif ($marker!=0x00 and $marker!=0x01 and $marker!=0xd9 and | 
| 5654 |  |  |  |  |  |  | ($marker<0xd0 or $marker>0xd7)) | 
| 5655 |  |  |  |  |  |  | { | 
| 5656 |  |  |  |  |  |  | # read record length word | 
| 5657 | 685 | 50 |  |  |  | 1912 | last unless $raf->Read($s, 2) == 2; | 
| 5658 | 685 |  |  |  |  | 2184 | my $len = unpack('n',$s);   # get data length | 
| 5659 | 685 | 50 | 33 |  |  | 3152 | last unless defined($len) and $len >= 2; | 
| 5660 | 685 |  |  |  |  | 2032 | $segPos = $raf->Tell(); | 
| 5661 | 685 |  |  |  |  | 1392 | $len -= 2;  # subtract size of length word | 
| 5662 | 685 | 50 |  |  |  | 1765 | last unless $raf->Read($segData, $len) == $len; | 
| 5663 |  |  |  |  |  |  | } | 
| 5664 |  |  |  |  |  |  | # initialize variables for this segment | 
| 5665 | 796 |  |  |  |  | 2656 | my $hdr = "\xff" . chr($marker);    # segment header | 
| 5666 | 796 |  |  |  |  | 2355 | my $markerName = JpegMarkerName($marker); | 
| 5667 | 796 |  |  |  |  | 2074 | my $dirName = shift @dirOrder;      # get directory name | 
| 5668 |  |  |  |  |  |  | # | 
| 5669 |  |  |  |  |  |  | # create all segments that must come before this one | 
| 5670 |  |  |  |  |  |  | # (nothing comes before SOI or after SOS) | 
| 5671 |  |  |  |  |  |  | # | 
| 5672 | 796 |  |  |  |  | 2512 | while ($markerName ne 'SOI') { | 
| 5673 | 796 | 100 | 100 |  |  | 2783 | if (exists $$addDirs{JFIF} and not defined $doneDir{JFIF}) { | 
| 5674 | 1 |  |  |  |  | 4 | $doneDir{JFIF} = 1; | 
| 5675 | 1 | 50 |  |  |  | 4 | if (defined $doneDir{Adobe}) { | 
| 5676 |  |  |  |  |  |  | # JFIF overrides Adobe APP14 colour components, so don't allow this | 
| 5677 |  |  |  |  |  |  | # (ref https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html) | 
| 5678 | 1 |  |  |  |  | 6 | $self->Warn('Not creating JFIF in JPEG with Adobe APP14'); | 
| 5679 |  |  |  |  |  |  | } else { | 
| 5680 | 0 | 0 |  |  |  | 0 | if ($verbose) { | 
| 5681 | 0 |  |  |  |  | 0 | print $out "Creating APP0:\n"; | 
| 5682 | 0 |  |  |  |  | 0 | print $out "  Creating JFIF with default values\n"; | 
| 5683 |  |  |  |  |  |  | } | 
| 5684 | 0 |  |  |  |  | 0 | my $jfif = "\x01\x02\x01\0\x48\0\x48\0\0"; | 
| 5685 | 0 |  |  |  |  | 0 | SetByteOrder('MM'); | 
| 5686 | 0 |  |  |  |  | 0 | my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main'); | 
| 5687 | 0 |  |  |  |  | 0 | my %dirInfo = ( | 
| 5688 |  |  |  |  |  |  | DataPt   => \$jfif, | 
| 5689 |  |  |  |  |  |  | DirStart => 0, | 
| 5690 |  |  |  |  |  |  | DirLen   => length $jfif, | 
| 5691 |  |  |  |  |  |  | Parent   => 'JFIF', | 
| 5692 |  |  |  |  |  |  | ); | 
| 5693 |  |  |  |  |  |  | # must temporarily remove JFIF from DEL_GROUP so we can | 
| 5694 |  |  |  |  |  |  | # delete JFIF and add it back again in a single step | 
| 5695 | 0 |  |  |  |  | 0 | my $delJFIF = $$delGroup{JFIF}; | 
| 5696 | 0 |  |  |  |  | 0 | delete $$delGroup{JFIF}; | 
| 5697 | 0 |  |  |  |  | 0 | $$path[$pn] = 'JFIF'; | 
| 5698 | 0 |  |  |  |  | 0 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 5699 | 0 | 0 |  |  |  | 0 | $$delGroup{JFIF} = $delJFIF if defined $delJFIF; | 
| 5700 | 0 | 0 | 0 |  |  | 0 | if (defined $newData and length $newData) { | 
| 5701 | 0 |  |  |  |  | 0 | my $app0hdr = "\xff\xe0" . pack('n', length($newData) + 7); | 
| 5702 | 0 | 0 |  |  |  | 0 | Write($outfile,$app0hdr,"JFIF\0",$newData) or $err = 1; | 
| 5703 |  |  |  |  |  |  | } | 
| 5704 |  |  |  |  |  |  | } | 
| 5705 |  |  |  |  |  |  | } | 
| 5706 |  |  |  |  |  |  | # don't create anything before APP0 or APP1 EXIF (containing IFD0) | 
| 5707 | 796 | 100 | 100 |  |  | 5169 | last if $markerName eq 'APP0' or $dirCount{IFD0} or $dirCount{ExtendedEXIF}; | 
|  |  |  | 66 |  |  |  |  | 
| 5708 |  |  |  |  |  |  | # EXIF information must come immediately after APP0 | 
| 5709 | 691 | 100 | 100 |  |  | 2612 | if (exists $$addDirs{IFD0} and not defined $doneDir{IFD0}) { | 
| 5710 | 31 |  |  |  |  | 120 | $doneDir{IFD0} = 1; | 
| 5711 | 31 | 100 |  |  |  | 136 | $verbose and print $out "Creating APP1:\n"; | 
| 5712 |  |  |  |  |  |  | # write new EXIF data | 
| 5713 | 31 |  |  |  |  | 112 | $$self{TIFF_TYPE} = 'APP1'; | 
| 5714 | 31 |  |  |  |  | 154 | my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main'); | 
| 5715 | 31 |  |  |  |  | 234 | my %dirInfo = ( | 
| 5716 |  |  |  |  |  |  | DirName => 'IFD0', | 
| 5717 |  |  |  |  |  |  | Parent  => 'APP1', | 
| 5718 |  |  |  |  |  |  | ); | 
| 5719 | 31 |  |  |  |  | 133 | $$path[$pn] = 'APP1'; | 
| 5720 | 31 |  |  |  |  | 255 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF); | 
| 5721 | 31 | 100 | 66 |  |  | 319 | if (defined $buff and length $buff) { | 
| 5722 | 29 | 50 |  |  |  | 191 | if (length($buff) + length($exifAPP1hdr) > $maxSegmentLen) { | 
| 5723 | 0 | 0 |  |  |  | 0 | if ($self->Options('NoMultiExif')) { | 
| 5724 | 0 |  |  |  |  | 0 | $self->Error('EXIF is too large for JPEG segment'); | 
| 5725 |  |  |  |  |  |  | } else { | 
| 5726 | 0 |  |  |  |  | 0 | $self->Warn('Creating multi-segment EXIF',1); | 
| 5727 |  |  |  |  |  |  | } | 
| 5728 |  |  |  |  |  |  | } | 
| 5729 |  |  |  |  |  |  | # switch to buffered output if required | 
| 5730 | 29 | 50 | 33 |  |  | 320 | if (($$self{PREVIEW_INFO} or $$self{LeicaTrailer}) and not $oldOutfile) { | 
|  |  |  | 33 |  |  |  |  | 
| 5731 | 0 |  |  |  |  | 0 | $writeBuffer = ''; | 
| 5732 | 0 |  |  |  |  | 0 | $oldOutfile = $outfile; | 
| 5733 | 0 |  |  |  |  | 0 | $outfile = \$writeBuffer; | 
| 5734 |  |  |  |  |  |  | # account for segment, EXIF and TIFF headers | 
| 5735 | 0 | 0 |  |  |  | 0 | $$self{PREVIEW_INFO}{Fixup}{Start} += 18 if $$self{PREVIEW_INFO}; | 
| 5736 | 0 | 0 |  |  |  | 0 | $$self{LeicaTrailer}{Fixup}{Start} += 18 if $$self{LeicaTrailer}; | 
| 5737 |  |  |  |  |  |  | } | 
| 5738 |  |  |  |  |  |  | # write as multi-segment | 
| 5739 | 29 |  |  |  |  | 174 | my $n = WriteMultiSegment($outfile, 0xe1, $exifAPP1hdr, \$buff, 'EXIF'); | 
| 5740 | 29 | 50 | 33 |  |  | 230 | if (not $n) { | 
|  |  | 50 |  |  |  |  |  | 
| 5741 | 0 |  |  |  |  | 0 | $err = 1; | 
| 5742 |  |  |  |  |  |  | } elsif ($n > 1 and $oldOutfile) { | 
| 5743 |  |  |  |  |  |  | # (punt on this because updating the pointers would be a real pain) | 
| 5744 | 0 |  |  |  |  | 0 | $self->Error("Can't write multi-segment EXIF with external pointers"); | 
| 5745 |  |  |  |  |  |  | } | 
| 5746 | 29 |  |  |  |  | 191 | ++$$self{CHANGED}; | 
| 5747 |  |  |  |  |  |  | } | 
| 5748 |  |  |  |  |  |  | } | 
| 5749 |  |  |  |  |  |  | # APP13 Photoshop segment next | 
| 5750 | 691 | 100 |  |  |  | 1804 | last if $dirCount{Photoshop}; | 
| 5751 | 509 | 100 | 100 |  |  | 5537 | if (exists $$addDirs{Photoshop} and not defined $doneDir{Photoshop}) { | 
| 5752 | 19 |  |  |  |  | 75 | $doneDir{Photoshop} = 1; | 
| 5753 | 19 | 50 |  |  |  | 87 | $verbose and print $out "Creating APP13:\n"; | 
| 5754 |  |  |  |  |  |  | # write new APP13 Photoshop record to memory | 
| 5755 | 19 |  |  |  |  | 88 | my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main'); | 
| 5756 | 19 |  |  |  |  | 110 | my %dirInfo = ( | 
| 5757 |  |  |  |  |  |  | Parent => 'APP13', | 
| 5758 |  |  |  |  |  |  | ); | 
| 5759 | 19 |  |  |  |  | 73 | $$path[$pn] = 'APP13'; | 
| 5760 | 19 |  |  |  |  | 113 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 5761 | 19 | 50 | 33 |  |  | 156 | if (defined $buff and length $buff) { | 
| 5762 | 19 | 50 |  |  |  | 447 | WriteMultiSegment($outfile, 0xed, $psAPP13hdr, \$buff) or $err = 1; | 
| 5763 | 19 |  |  |  |  | 100 | ++$$self{CHANGED}; | 
| 5764 |  |  |  |  |  |  | } | 
| 5765 |  |  |  |  |  |  | } | 
| 5766 |  |  |  |  |  |  | # then APP1 XMP segment | 
| 5767 | 509 | 100 |  |  |  | 1353 | last if $dirCount{XMP}; | 
| 5768 | 494 | 100 | 100 |  |  | 1787 | if (exists $$addDirs{XMP} and not defined $doneDir{XMP}) { | 
| 5769 | 27 |  |  |  |  | 92 | $doneDir{XMP} = 1; | 
| 5770 | 27 | 50 |  |  |  | 130 | $verbose and print $out "Creating APP1:\n"; | 
| 5771 |  |  |  |  |  |  | # write new XMP data | 
| 5772 | 27 |  |  |  |  | 165 | my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main'); | 
| 5773 | 27 |  |  |  |  | 250 | my %dirInfo = ( | 
| 5774 |  |  |  |  |  |  | Parent      => 'APP1', | 
| 5775 |  |  |  |  |  |  | # specify MaxDataLen so XMP is split if required | 
| 5776 |  |  |  |  |  |  | MaxDataLen  => $maxXMPLen - length($xmpAPP1hdr), | 
| 5777 |  |  |  |  |  |  | ); | 
| 5778 | 27 |  |  |  |  | 108 | $$path[$pn] = 'APP1'; | 
| 5779 | 27 |  |  |  |  | 165 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 5780 | 27 | 50 | 33 |  |  | 300 | if (defined $buff and length $buff) { | 
| 5781 |  |  |  |  |  |  | WriteMultiXMP($self, $outfile, \$buff, $dirInfo{ExtendedXMP}, | 
| 5782 | 27 | 50 |  |  |  | 209 | $dirInfo{ExtendedGUID}) or $err = 1; | 
| 5783 |  |  |  |  |  |  | } | 
| 5784 |  |  |  |  |  |  | } | 
| 5785 |  |  |  |  |  |  | # then APP2 ICC_Profile segment | 
| 5786 | 494 | 100 |  |  |  | 1478 | last if $dirCount{ICC_Profile}; | 
| 5787 | 489 | 100 | 100 |  |  | 1476 | if (exists $$addDirs{ICC_Profile} and not defined $doneDir{ICC_Profile}) { | 
| 5788 | 3 |  |  |  |  | 11 | $doneDir{ICC_Profile} = 1; | 
| 5789 | 3 | 50 | 66 |  |  | 23 | next if $$delGroup{ICC_Profile} and $$delGroup{ICC_Profile} != 2; | 
| 5790 | 3 | 50 |  |  |  | 13 | $verbose and print $out "Creating APP2:\n"; | 
| 5791 |  |  |  |  |  |  | # write new ICC_Profile data | 
| 5792 | 3 |  |  |  |  | 16 | my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main'); | 
| 5793 | 3 |  |  |  |  | 23 | my %dirInfo = ( | 
| 5794 |  |  |  |  |  |  | Parent   => 'APP2', | 
| 5795 |  |  |  |  |  |  | ); | 
| 5796 | 3 |  |  |  |  | 14 | $$path[$pn] = 'APP2'; | 
| 5797 | 3 |  |  |  |  | 50 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 5798 | 3 | 50 | 33 |  |  | 23 | if (defined $buff and length $buff) { | 
| 5799 | 3 | 50 |  |  |  | 16 | WriteMultiSegment($outfile, 0xe2, "ICC_PROFILE\0", \$buff, 'ICC') or $err = 1; | 
| 5800 | 3 |  |  |  |  | 24 | ++$$self{CHANGED}; | 
| 5801 |  |  |  |  |  |  | } | 
| 5802 |  |  |  |  |  |  | } | 
| 5803 |  |  |  |  |  |  | # then APP12 Ducky segment | 
| 5804 | 489 | 100 |  |  |  | 1210 | last if $dirCount{Ducky}; | 
| 5805 | 488 | 100 | 100 |  |  | 1458 | if (exists $$addDirs{Ducky} and not defined $doneDir{Ducky}) { | 
| 5806 | 2 |  |  |  |  | 10 | $doneDir{Ducky} = 1; | 
| 5807 | 2 | 50 |  |  |  | 14 | $verbose and print $out "Creating APP12 Ducky:\n"; | 
| 5808 |  |  |  |  |  |  | # write new Ducky segment data | 
| 5809 | 2 |  |  |  |  | 15 | my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky'); | 
| 5810 | 2 |  |  |  |  | 17 | my %dirInfo = ( | 
| 5811 |  |  |  |  |  |  | Parent   => 'APP12', | 
| 5812 |  |  |  |  |  |  | ); | 
| 5813 | 2 |  |  |  |  | 11 | $$path[$pn] = 'APP12'; | 
| 5814 | 2 |  |  |  |  | 12 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 5815 | 2 | 50 | 33 |  |  | 25 | if (defined $buff and length $buff) { | 
| 5816 | 2 |  |  |  |  | 7 | my $size = length($buff) + 5; | 
| 5817 | 2 | 50 |  |  |  | 8 | if ($size <= $maxSegmentLen) { | 
| 5818 |  |  |  |  |  |  | # write the new segment with appropriate header | 
| 5819 | 2 |  |  |  |  | 11 | my $app12hdr = "\xff\xec" . pack('n', $size + 2); | 
| 5820 | 2 | 50 |  |  |  | 10 | Write($outfile, $app12hdr, 'Ducky', $buff) or $err = 1; | 
| 5821 |  |  |  |  |  |  | } else { | 
| 5822 | 0 |  |  |  |  | 0 | $self->Warn("APP12 Ducky segment too large! ($size bytes)"); | 
| 5823 |  |  |  |  |  |  | } | 
| 5824 |  |  |  |  |  |  | } | 
| 5825 |  |  |  |  |  |  | } | 
| 5826 |  |  |  |  |  |  | # then APP14 Adobe segment | 
| 5827 | 488 | 100 |  |  |  | 1331 | last if $dirCount{Adobe}; | 
| 5828 | 463 | 50 | 33 |  |  | 1596 | if (exists $$addDirs{Adobe} and not defined $doneDir{Adobe}) { | 
| 5829 | 0 |  |  |  |  | 0 | $doneDir{Adobe} = 1; | 
| 5830 | 0 |  |  |  |  | 0 | my $buff = $self->GetNewValue('Adobe'); | 
| 5831 | 0 | 0 |  |  |  | 0 | if ($buff) { | 
| 5832 | 0 | 0 |  |  |  | 0 | $verbose and print $out "Creating APP14:\n  Creating Adobe segment\n"; | 
| 5833 | 0 |  |  |  |  | 0 | my $size = length($buff); | 
| 5834 | 0 | 0 |  |  |  | 0 | if ($size <= $maxSegmentLen) { | 
| 5835 |  |  |  |  |  |  | # write the new segment with appropriate header | 
| 5836 | 0 |  |  |  |  | 0 | my $app14hdr = "\xff\xee" . pack('n', $size + 2); | 
| 5837 | 0 | 0 |  |  |  | 0 | Write($outfile, $app14hdr, $buff) or $err = 1; | 
| 5838 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 5839 |  |  |  |  |  |  | } else { | 
| 5840 | 0 |  |  |  |  | 0 | $self->Warn("APP14 Adobe segment too large! ($size bytes)"); | 
| 5841 |  |  |  |  |  |  | } | 
| 5842 |  |  |  |  |  |  | } | 
| 5843 |  |  |  |  |  |  | } | 
| 5844 |  |  |  |  |  |  | # finally, COM segment | 
| 5845 | 463 | 100 |  |  |  | 1166 | last if $dirCount{COM}; | 
| 5846 | 443 | 100 | 100 |  |  | 1388 | if (exists $$addDirs{COM} and not defined $doneDir{COM}) { | 
| 5847 | 5 |  |  |  |  | 16 | $doneDir{COM} = 1; | 
| 5848 | 5 | 50 | 33 |  |  | 22 | next if $$delGroup{File} and $$delGroup{File} != 2; | 
| 5849 | 5 |  |  |  |  | 24 | my $newComment = $self->GetNewValue('Comment'); | 
| 5850 | 5 | 50 |  |  |  | 25 | if (defined $newComment) { | 
| 5851 | 5 | 50 |  |  |  | 29 | if ($verbose) { | 
| 5852 | 0 |  |  |  |  | 0 | print $out "Creating COM:\n"; | 
| 5853 | 0 |  |  |  |  | 0 | $self->VerboseValue('+ Comment', $newComment); | 
| 5854 |  |  |  |  |  |  | } | 
| 5855 | 5 | 50 |  |  |  | 29 | WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1; | 
| 5856 | 5 |  |  |  |  | 27 | ++$$self{CHANGED}; | 
| 5857 |  |  |  |  |  |  | } | 
| 5858 |  |  |  |  |  |  | } | 
| 5859 | 443 |  |  |  |  | 758 | last;   # didn't want to loop anyway | 
| 5860 |  |  |  |  |  |  | } | 
| 5861 | 796 |  |  |  |  | 1936 | $$path[$pn] = $markerName; | 
| 5862 |  |  |  |  |  |  | # decrement counter for this directory since we are about to process it | 
| 5863 | 796 |  |  |  |  | 1878 | --$dirCount{$dirName}; | 
| 5864 |  |  |  |  |  |  | # | 
| 5865 |  |  |  |  |  |  | # rewrite existing segments | 
| 5866 |  |  |  |  |  |  | # | 
| 5867 |  |  |  |  |  |  | # handle SOF markers: SOF0-SOF15, except DHT(0xc4), JPGA(0xc8) and DAC(0xcc) | 
| 5868 | 796 | 100 | 66 |  |  | 8273 | if (($marker & 0xf0) == 0xc0 and ($marker == 0xc0 or $marker & 0x03)) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 5869 | 109 | 100 |  |  |  | 464 | $verbose and print $out "JPEG $markerName:\n"; | 
| 5870 | 109 | 50 |  |  |  | 404 | Write($outfile, $hdr, $segData) or $err = 1; | 
| 5871 | 109 |  |  |  |  | 411 | next; | 
| 5872 |  |  |  |  |  |  | } elsif ($marker == 0xda) {             # SOS | 
| 5873 | 109 |  |  |  |  | 511 | pop @$path; | 
| 5874 | 109 | 100 |  |  |  | 571 | $verbose and print $out "JPEG SOS\n"; | 
| 5875 |  |  |  |  |  |  | # write SOS segment | 
| 5876 | 109 |  |  |  |  | 491 | $s = pack('n', length($segData) + 2); | 
| 5877 | 109 | 50 |  |  |  | 498 | Write($outfile, $hdr, $s, $segData) or $err = 1; | 
| 5878 | 109 |  |  |  |  | 479 | my ($buff, $endPos, $trailInfo); | 
| 5879 | 109 |  |  |  |  | 389 | my $delPreview = $$self{DEL_PREVIEW}; | 
| 5880 | 109 | 100 |  |  |  | 775 | $trailInfo = IdentifyTrailer($raf) unless $$delGroup{Trailer}; | 
| 5881 | 109 |  |  |  |  | 961 | my $nvTrail = $self->GetNewValueHash($Image::ExifTool::Extra{Trailer}); | 
| 5882 | 109 | 100 | 66 |  |  | 1953 | unless ($oldOutfile or $delPreview or $trailInfo or $$delGroup{Trailer} or $nvTrail) { | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 5883 |  |  |  |  |  |  | # blindly copy the rest of the file | 
| 5884 | 93 |  |  |  |  | 485 | while ($raf->Read($buff, 65536)) { | 
| 5885 | 93 | 50 |  |  |  | 4372 | Write($outfile, $buff) or $err = 1, last; | 
| 5886 |  |  |  |  |  |  | } | 
| 5887 | 93 |  |  |  |  | 378 | $rtnVal = 1;  # success unless we have a file write error | 
| 5888 | 93 |  |  |  |  | 307 | last;         # all done | 
| 5889 |  |  |  |  |  |  | } | 
| 5890 |  |  |  |  |  |  | # write the rest of the image (as quickly as possible) up to the EOI | 
| 5891 | 16 |  |  |  |  | 58 | my $endedWithFF; | 
| 5892 | 16 |  |  |  |  | 45 | for (;;) { | 
| 5893 | 16 | 50 |  |  |  | 82 | my $n = $raf->Read($buff, 65536) or last Marker; | 
| 5894 | 16 | 50 | 33 |  |  | 298 | if (($endedWithFF and $buff =~ m/^\xd9/sg) or | 
|  |  |  | 33 |  |  |  |  | 
| 5895 |  |  |  |  |  |  | $buff =~ m/\xff\xd9/sg) | 
| 5896 |  |  |  |  |  |  | { | 
| 5897 | 16 |  |  |  |  | 47 | $rtnVal = 1; # the JPEG is OK | 
| 5898 |  |  |  |  |  |  | # write up to the EOI | 
| 5899 | 16 |  |  |  |  | 51 | my $pos = pos($buff); | 
| 5900 | 16 | 50 |  |  |  | 92 | Write($outfile, substr($buff, 0, $pos)) or $err = 1; | 
| 5901 | 16 |  |  |  |  | 141 | $buff = substr($buff, $pos); | 
| 5902 | 16 |  |  |  |  | 47 | last; | 
| 5903 |  |  |  |  |  |  | } | 
| 5904 | 0 | 0 |  |  |  | 0 | unless ($n == 65536) { | 
| 5905 | 0 |  |  |  |  | 0 | $self->Error('JPEG EOI marker not found'); | 
| 5906 | 0 |  |  |  |  | 0 | last Marker; | 
| 5907 |  |  |  |  |  |  | } | 
| 5908 | 0 | 0 |  |  |  | 0 | Write($outfile, $buff) or $err = 1; | 
| 5909 | 0 | 0 |  |  |  | 0 | $endedWithFF = substr($buff, 65535, 1) eq "\xff" ? 1 : 0; | 
| 5910 |  |  |  |  |  |  | } | 
| 5911 |  |  |  |  |  |  | # remember position of last data copied | 
| 5912 | 16 |  |  |  |  | 112 | $endPos = $raf->Tell() - length($buff); | 
| 5913 |  |  |  |  |  |  | # write new trailer if specified | 
| 5914 | 16 | 50 |  |  |  | 102 | if ($nvTrail) { | 
| 5915 |  |  |  |  |  |  | # access new value directly to avoid copying a potentially very large data block | 
| 5916 | 0 | 0 | 0 |  |  | 0 | if ($$nvTrail{Value} and $$nvTrail{Value}[0]) { # (note: "0" will also delete the trailer) | 
|  |  | 0 | 0 |  |  |  |  | 
| 5917 | 0 |  |  |  |  | 0 | $self->VPrint(0, '  Writing new trailer'); | 
| 5918 | 0 | 0 |  |  |  | 0 | Write($outfile, $$nvTrail{Value}[0]) or $err = 1; | 
| 5919 | 0 |  |  |  |  | 0 | ++$$self{CHANGED}; | 
| 5920 |  |  |  |  |  |  | } elsif ($raf->Seek(0, 2) and $raf->Tell() != $endPos) { | 
| 5921 | 0 |  |  |  |  | 0 | $self->VPrint(0, '  Deleting trailer (', $raf->Tell() - $endPos, ' bytes)'); | 
| 5922 | 0 |  |  |  |  | 0 | ++$$self{CHANGED};  # changed if there was previously a trailer | 
| 5923 |  |  |  |  |  |  | } | 
| 5924 | 0 |  |  |  |  | 0 | last;   # all done | 
| 5925 |  |  |  |  |  |  | } | 
| 5926 |  |  |  |  |  |  | # rewrite existing trailers | 
| 5927 | 16 | 100 |  |  |  | 108 | if ($trailInfo) { | 
| 5928 | 11 |  |  |  |  | 39 | my $tbuf = ''; | 
| 5929 | 11 |  |  |  |  | 66 | $raf->Seek(-length($buff), 1);  # seek back to just after EOI | 
| 5930 | 11 |  |  |  |  | 110 | $$trailInfo{OutFile} = \$tbuf;  # rewrite the trailer | 
| 5931 | 11 |  |  |  |  | 68 | $$trailInfo{ScanForAFCP} = 1;   # scan if necessary | 
| 5932 | 11 | 50 |  |  |  | 76 | $self->ProcessTrailers($trailInfo) or undef $trailInfo; | 
| 5933 |  |  |  |  |  |  | } | 
| 5934 | 16 | 100 |  |  |  | 77 | if (not $oldOutfile) { | 
|  |  | 50 |  |  |  |  |  | 
| 5935 |  |  |  |  |  |  | # do nothing special | 
| 5936 |  |  |  |  |  |  | } elsif ($$self{LeicaTrailer}) { | 
| 5937 | 0 |  |  |  |  | 0 | my $trailLen; | 
| 5938 | 0 | 0 |  |  |  | 0 | if ($trailInfo) { | 
| 5939 | 0 |  |  |  |  | 0 | $trailLen = $$trailInfo{DataPos} - $endPos; | 
| 5940 |  |  |  |  |  |  | } else { | 
| 5941 | 0 | 0 |  |  |  | 0 | $raf->Seek(0, 2) or $err = 1; | 
| 5942 | 0 |  |  |  |  | 0 | $trailLen = $raf->Tell() - $endPos; | 
| 5943 |  |  |  |  |  |  | } | 
| 5944 | 0 |  |  |  |  | 0 | my $fixup = $$self{LeicaTrailer}{Fixup}; | 
| 5945 | 0 |  |  |  |  | 0 | $$self{LeicaTrailer}{TrailPos} = $endPos; | 
| 5946 | 0 |  |  |  |  | 0 | $$self{LeicaTrailer}{TrailLen} = $trailLen; | 
| 5947 |  |  |  |  |  |  | # get _absolute_ position of new Leica trailer | 
| 5948 | 0 |  |  |  |  | 0 | my $absPos = Tell($oldOutfile) + length($$outfile); | 
| 5949 | 0 |  |  |  |  | 0 | require Image::ExifTool::Panasonic; | 
| 5950 | 0 |  |  |  |  | 0 | my $dat = Image::ExifTool::Panasonic::ProcessLeicaTrailer($self, $absPos); | 
| 5951 |  |  |  |  |  |  | # allow some junk before Leica trailer (just in case) | 
| 5952 | 0 |  |  |  |  | 0 | my $junk = $$self{LeicaTrailerPos} - $endPos; | 
| 5953 |  |  |  |  |  |  | # set MakerNote pointer and size (subtract 10 for segment and EXIF headers) | 
| 5954 | 0 |  |  |  |  | 0 | $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', length($$outfile) - 10 + $junk); | 
| 5955 |  |  |  |  |  |  | # use this fixup to set the size too (sneaky) | 
| 5956 | 0 | 0 |  |  |  | 0 | my $trailSize = defined($dat) ? length($dat) - $junk : $$self{LeicaTrailer}{Size}; | 
| 5957 | 0 |  |  |  |  | 0 | $$fixup{Start} -= 4;  $$fixup{Shift} += 4; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5958 | 0 | 0 |  |  |  | 0 | $fixup->SetMarkerPointers($outfile, 'LeicaTrailer', $trailSize) if defined $trailSize; | 
| 5959 | 0 |  |  |  |  | 0 | $$fixup{Start} += 4;  $$fixup{Shift} -= 4; | 
|  | 0 |  |  |  |  | 0 |  | 
| 5960 |  |  |  |  |  |  | # clean up and write the buffered data | 
| 5961 | 0 |  |  |  |  | 0 | $outfile = $oldOutfile; | 
| 5962 | 0 |  |  |  |  | 0 | undef $oldOutfile; | 
| 5963 | 0 | 0 |  |  |  | 0 | Write($outfile, $writeBuffer) or $err = 1; | 
| 5964 | 0 |  |  |  |  | 0 | undef $writeBuffer; | 
| 5965 | 0 | 0 |  |  |  | 0 | if (defined $dat) { | 
| 5966 | 0 | 0 |  |  |  | 0 | Write($outfile, $dat) or $err = 1;  # write new Leica trailer | 
| 5967 | 0 |  |  |  |  | 0 | $delPreview = 1;                    # delete existing Leica trailer | 
| 5968 |  |  |  |  |  |  | } | 
| 5969 |  |  |  |  |  |  | } else { | 
| 5970 |  |  |  |  |  |  | # locate preview image and fix up preview offsets | 
| 5971 | 1 | 50 |  |  |  | 8 | my $scanLen = $$self{Make} =~ /^SONY/i ? 65536 : 1024; | 
| 5972 | 1 | 50 |  |  |  | 4 | if (length($buff) < $scanLen) { # make sure we have enough trailer to scan | 
| 5973 | 1 |  |  |  |  | 2 | my $buf2; | 
| 5974 | 1 | 50 |  |  |  | 5 | $buff .= $buf2 if $raf->Read($buf2, $scanLen - length($buff)); | 
| 5975 |  |  |  |  |  |  | } | 
| 5976 |  |  |  |  |  |  | # get new preview image position, relative to EXIF base | 
| 5977 | 1 |  |  |  |  | 16 | my $newPos = length($$outfile) - 10; # (subtract 10 for segment and EXIF headers) | 
| 5978 | 1 |  |  |  |  | 4 | my $junkLen; | 
| 5979 |  |  |  |  |  |  | # adjust position if image isn't at the start (eg. Olympus E-1/E-300) | 
| 5980 | 1 | 50 |  |  |  | 4 | if ($buff =~ /(\xff\xd8\xff.|.\xd8\xff\xdb)(..)/sg) { | 
| 5981 | 0 |  |  |  |  | 0 | my ($jpegHdr, $segLen) = ($1, $2); | 
| 5982 | 0 |  |  |  |  | 0 | $junkLen = pos($buff) - 6; | 
| 5983 |  |  |  |  |  |  | # Sony previewimage trailer has a 32 byte header | 
| 5984 | 0 | 0 | 0 |  |  | 0 | if ($$self{Make} =~ /^SONY/i and $junkLen > 32) { | 
| 5985 |  |  |  |  |  |  | # with some newer Sony models, the makernotes preview pointer | 
| 5986 |  |  |  |  |  |  | # points to JPEG at end of EXIF inside MPImage preview (what a pain!) | 
| 5987 | 0 | 0 |  |  |  | 0 | if ($jpegHdr eq "\xff\xd8\xff\xe1") {   # is the first segment EXIF? | 
| 5988 | 0 |  |  |  |  | 0 | $segLen = unpack('n', $segLen);     # the EXIF segment length | 
| 5989 |  |  |  |  |  |  | # Sony PreviewImage starts with last 2 bytes of EXIF segment | 
| 5990 |  |  |  |  |  |  | # (and first byte is usually "\0", not "\xff", so don't check this) | 
| 5991 | 0 | 0 | 0 |  |  | 0 | if (length($buff) > $junkLen + $segLen + 6 and | 
| 5992 |  |  |  |  |  |  | substr($buff, $junkLen + $segLen + 3, 3) eq "\xd8\xff\xdb") | 
| 5993 |  |  |  |  |  |  | { | 
| 5994 | 0 |  |  |  |  | 0 | $junkLen += $segLen + 2; | 
| 5995 |  |  |  |  |  |  | # (note: this will not copy the trailer after PreviewImage, | 
| 5996 |  |  |  |  |  |  | #  which is a 14kB block full of zeros for the A77) | 
| 5997 |  |  |  |  |  |  | } | 
| 5998 |  |  |  |  |  |  | } | 
| 5999 | 0 |  |  |  |  | 0 | $junkLen -= 32; | 
| 6000 |  |  |  |  |  |  | } | 
| 6001 | 0 |  |  |  |  | 0 | $newPos += $junkLen; | 
| 6002 |  |  |  |  |  |  | } | 
| 6003 |  |  |  |  |  |  | # fix up the preview offsets to point to the start of the new image | 
| 6004 | 1 |  |  |  |  | 4 | my $previewInfo = $$self{PREVIEW_INFO}; | 
| 6005 | 1 |  |  |  |  | 2 | delete $$self{PREVIEW_INFO}; | 
| 6006 | 1 |  |  |  |  | 4 | my $fixup = $$previewInfo{Fixup}; | 
| 6007 | 1 |  | 50 |  |  | 7 | $newPos += ($$previewInfo{BaseShift} || 0); | 
| 6008 |  |  |  |  |  |  | # adjust to absolute file offset if necessary (Samsung STMN) | 
| 6009 | 1 | 50 |  |  |  | 4 | $newPos += Tell($oldOutfile) + 10 if $$previewInfo{Absolute}; | 
| 6010 | 1 | 50 |  |  |  | 4 | if ($$previewInfo{Relative}) { | 
|  |  | 0 |  |  |  |  |  | 
| 6011 |  |  |  |  |  |  | # adjust for our base by looking at how far the pointer got shifted | 
| 6012 | 1 |  | 50 |  |  | 7 | $newPos -= ($fixup->GetMarkerPointers($outfile, 'PreviewImage') || 0); | 
| 6013 |  |  |  |  |  |  | } elsif ($$previewInfo{ChangeBase}) { | 
| 6014 |  |  |  |  |  |  | # Leica S2 uses relative offsets for the preview only (leica sucks) | 
| 6015 | 0 |  |  |  |  | 0 | my $makerOffset = $fixup->GetMarkerPointers($outfile, 'LeicaTrailer'); | 
| 6016 | 0 | 0 |  |  |  | 0 | $newPos -= $makerOffset if $makerOffset; | 
| 6017 |  |  |  |  |  |  | } | 
| 6018 | 1 |  |  |  |  | 5 | $fixup->SetMarkerPointers($outfile, 'PreviewImage', $newPos); | 
| 6019 |  |  |  |  |  |  | # clean up and write the buffered data | 
| 6020 | 1 |  |  |  |  | 2 | $outfile = $oldOutfile; | 
| 6021 | 1 |  |  |  |  | 4 | undef $oldOutfile; | 
| 6022 | 1 | 50 |  |  |  | 4 | Write($outfile, $writeBuffer) or $err = 1; | 
| 6023 | 1 |  |  |  |  | 3 | undef $writeBuffer; | 
| 6024 |  |  |  |  |  |  | # write preview image | 
| 6025 | 1 | 50 |  |  |  | 15 | if ($$previewInfo{Data} ne 'LOAD_PREVIEW') { | 
| 6026 |  |  |  |  |  |  | # write any junk that existed before the preview image | 
| 6027 | 0 | 0 | 0 |  |  | 0 | Write($outfile, substr($buff,0,$junkLen)) or $err = 1 if $junkLen; | 
| 6028 |  |  |  |  |  |  | # write the saved preview image | 
| 6029 | 0 | 0 |  |  |  | 0 | Write($outfile, $$previewInfo{Data}) or $err = 1; | 
| 6030 | 0 |  |  |  |  | 0 | delete $$previewInfo{Data}; | 
| 6031 |  |  |  |  |  |  | # (don't increment CHANGED because we could be rewriting existing preview) | 
| 6032 | 0 |  |  |  |  | 0 | $delPreview = 1;    # remove old preview | 
| 6033 |  |  |  |  |  |  | } | 
| 6034 |  |  |  |  |  |  | } | 
| 6035 |  |  |  |  |  |  | # copy over preview image if necessary | 
| 6036 | 16 | 50 |  |  |  | 72 | unless ($delPreview) { | 
| 6037 | 16 |  |  |  |  | 56 | my $extra; | 
| 6038 | 16 | 100 |  |  |  | 69 | if ($trailInfo) { | 
| 6039 |  |  |  |  |  |  | # copy everything up to start of first processed trailer | 
| 6040 | 11 |  |  |  |  | 32 | $extra = $$trailInfo{DataPos} - $endPos; | 
| 6041 |  |  |  |  |  |  | } else { | 
| 6042 |  |  |  |  |  |  | # copy everything up to end of file | 
| 6043 | 5 | 50 |  |  |  | 28 | $raf->Seek(0, 2) or $err = 1; | 
| 6044 | 5 |  |  |  |  | 36 | $extra = $raf->Tell() - $endPos; | 
| 6045 |  |  |  |  |  |  | } | 
| 6046 | 16 | 100 |  |  |  | 104 | if ($extra > 0) { | 
| 6047 | 3 | 100 |  |  |  | 16 | if ($$delGroup{Trailer}) { | 
| 6048 | 2 | 50 |  |  |  | 12 | $verbose and print $out "  Deleting unknown trailer ($extra bytes)\n"; | 
| 6049 | 2 |  |  |  |  | 8 | ++$$self{CHANGED}; | 
| 6050 |  |  |  |  |  |  | } else { | 
| 6051 |  |  |  |  |  |  | # copy over unknown trailer | 
| 6052 | 1 | 50 |  |  |  | 12 | $verbose and print $out "  Preserving unknown trailer ($extra bytes)\n"; | 
| 6053 | 1 | 50 |  |  |  | 10 | $raf->Seek($endPos, 0) or $err = 1; | 
| 6054 | 1 | 50 |  |  |  | 6 | CopyBlock($raf, $outfile, $extra) or $err = 1; | 
| 6055 |  |  |  |  |  |  | } | 
| 6056 |  |  |  |  |  |  | } | 
| 6057 |  |  |  |  |  |  | } | 
| 6058 |  |  |  |  |  |  | # write trailer if necessary | 
| 6059 | 16 | 100 |  |  |  | 69 | if ($trailInfo) { | 
| 6060 | 11 | 50 |  |  |  | 95 | $self->WriteTrailerBuffer($trailInfo, $outfile) or $err = 1; | 
| 6061 | 11 |  |  |  |  | 98 | undef $trailInfo; | 
| 6062 |  |  |  |  |  |  | } | 
| 6063 | 16 |  |  |  |  | 67 | last;   # all done parsing file | 
| 6064 |  |  |  |  |  |  |  | 
| 6065 |  |  |  |  |  |  | } elsif ($marker==0xd9 and $isEXV) { | 
| 6066 |  |  |  |  |  |  | # write EXV EOI (any trailer will be lost) | 
| 6067 | 2 | 50 |  |  |  | 6 | Write($outfile, "\xff\xd9") or $err = 1; | 
| 6068 | 2 |  |  |  |  | 5 | $rtnVal = 1; | 
| 6069 | 2 |  |  |  |  | 11 | last; | 
| 6070 |  |  |  |  |  |  |  | 
| 6071 |  |  |  |  |  |  | } elsif ($marker==0x00 or $marker==0x01 or ($marker>=0xd0 and $marker<=0xd7)) { | 
| 6072 | 0 | 0 | 0 |  |  | 0 | $verbose and $marker and print $out "JPEG $markerName:\n"; | 
| 6073 |  |  |  |  |  |  | # handle stand-alone markers 0x00, 0x01 and 0xd0-0xd7 (NULL, TEM, RST0-RST7) | 
| 6074 | 0 | 0 |  |  |  | 0 | Write($outfile, $hdr) or $err = 1; | 
| 6075 | 0 |  |  |  |  | 0 | next; | 
| 6076 |  |  |  |  |  |  | } | 
| 6077 |  |  |  |  |  |  | # | 
| 6078 |  |  |  |  |  |  | # NOTE: A 'next' statement after this point will cause $$segDataPt | 
| 6079 |  |  |  |  |  |  | #       not to be written if there is an output file, so in this case | 
| 6080 |  |  |  |  |  |  | #       the $$self{CHANGED} flags must be updated | 
| 6081 |  |  |  |  |  |  | # | 
| 6082 | 576 |  |  |  |  | 1198 | my $segDataPt = \$segData; | 
| 6083 | 576 |  |  |  |  | 1043 | $length = length($segData); | 
| 6084 | 576 | 100 |  |  |  | 1598 | print $out "JPEG $markerName ($length bytes)\n" if $verbose; | 
| 6085 |  |  |  |  |  |  | # group delete of APP segments | 
| 6086 | 576 | 100 |  |  |  | 1708 | if ($$delGroup{$dirName}) { | 
| 6087 | 55 | 50 |  |  |  | 122 | $verbose and print $out "  Deleting $dirName segment\n"; | 
| 6088 | 55 | 100 |  |  |  | 149 | $self->Warn('ICC_Profile deleted. Image colors may be affected') if $dirName eq 'ICC_Profile'; | 
| 6089 | 55 |  |  |  |  | 117 | ++$$self{CHANGED}; | 
| 6090 | 55 |  |  |  |  | 128 | next Marker; | 
| 6091 |  |  |  |  |  |  | } | 
| 6092 | 521 |  |  |  |  | 1082 | my ($segType, $del); | 
| 6093 |  |  |  |  |  |  | # rewrite this segment only if we are changing a tag which is contained in its | 
| 6094 |  |  |  |  |  |  | # directory (or deleting '*', in which case we need to identify the segment type) | 
| 6095 | 521 |  | 100 |  |  | 2679 | while (exists $$editDirs{$markerName} or $$delGroup{'*'}) { | 
| 6096 | 131 | 100 |  |  |  | 1442 | if ($marker == 0xe0) {              # APP0 (JFIF, CIFF) | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 6097 | 31 | 100 |  |  |  | 313 | if ($$segDataPt =~ /^JFIF\0/) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 6098 | 11 |  |  |  |  | 35 | $segType = 'JFIF'; | 
| 6099 | 11 | 50 |  |  |  | 62 | $$delGroup{JFIF} and $del = 1, last; | 
| 6100 | 11 | 50 |  |  |  | 49 | last unless $$editDirs{JFIF}; | 
| 6101 | 11 |  |  |  |  | 63 | SetByteOrder('MM'); | 
| 6102 | 11 |  |  |  |  | 90 | my $tagTablePtr = GetTagTable('Image::ExifTool::JFIF::Main'); | 
| 6103 | 11 |  |  |  |  | 105 | my %dirInfo = ( | 
| 6104 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6105 |  |  |  |  |  |  | DataPos  => $segPos, | 
| 6106 |  |  |  |  |  |  | DataLen  => $length, | 
| 6107 |  |  |  |  |  |  | DirStart => 5,     # directory starts after identifier | 
| 6108 |  |  |  |  |  |  | DirLen   => $length-5, | 
| 6109 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6110 |  |  |  |  |  |  | ); | 
| 6111 | 11 |  |  |  |  | 74 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 6112 | 11 | 50 | 33 |  |  | 104 | if (defined $newData and length $newData) { | 
| 6113 | 11 |  |  |  |  | 73 | $$segDataPt = "JFIF\0" . $newData; | 
| 6114 |  |  |  |  |  |  | } | 
| 6115 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^JFXX\0\x10/) { | 
| 6116 | 8 |  |  |  |  | 30 | $segType = 'JFXX'; | 
| 6117 | 8 | 100 |  |  |  | 47 | $$delGroup{JFIF} and $del = 1; | 
| 6118 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^(II|MM).{4}HEAPJPGM/s) { | 
| 6119 | 6 |  |  |  |  | 20 | $segType = 'CIFF'; | 
| 6120 | 6 | 50 |  |  |  | 44 | $$delGroup{CIFF} and $del = 1, last; | 
| 6121 | 6 | 100 |  |  |  | 29 | last unless $$editDirs{CIFF}; | 
| 6122 | 4 |  |  |  |  | 20 | my $newData = ''; | 
| 6123 | 4 |  |  |  |  | 28 | my %dirInfo = ( | 
| 6124 |  |  |  |  |  |  | RAF => new File::RandomAccess($segDataPt), | 
| 6125 |  |  |  |  |  |  | OutFile => \$newData, | 
| 6126 |  |  |  |  |  |  | ); | 
| 6127 | 4 |  |  |  |  | 47 | require Image::ExifTool::CanonRaw; | 
| 6128 | 4 | 50 |  |  |  | 44 | if (Image::ExifTool::CanonRaw::WriteCRW($self, \%dirInfo) > 0) { | 
| 6129 | 4 | 50 |  |  |  | 19 | if (length $newData) { | 
| 6130 | 4 |  |  |  |  | 13 | $$segDataPt = $newData; | 
| 6131 |  |  |  |  |  |  | } else { | 
| 6132 | 0 |  |  |  |  | 0 | undef $segDataPt; | 
| 6133 | 0 |  |  |  |  | 0 | $del = 1;   # delete this segment | 
| 6134 |  |  |  |  |  |  | } | 
| 6135 |  |  |  |  |  |  | } | 
| 6136 |  |  |  |  |  |  | } | 
| 6137 |  |  |  |  |  |  | } elsif ($marker == 0xe1) {         # APP1 (EXIF, XMP) | 
| 6138 |  |  |  |  |  |  | # check for EXIF data | 
| 6139 | 73 | 100 | 0 |  |  | 942 | if ($$segDataPt =~ /^(.{0,4})Exif\0./is) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 6140 | 52 |  |  |  |  | 196 | my $hdrLen = length $exifAPP1hdr; | 
| 6141 | 52 | 50 |  |  |  | 475 | if (length $1) { | 
|  |  | 50 |  |  |  |  |  | 
| 6142 | 0 |  |  |  |  | 0 | $hdrLen += length $1; | 
| 6143 | 0 |  |  |  |  | 0 | $self->Error('Unknown garbage at start of EXIF segment',1); | 
| 6144 |  |  |  |  |  |  | } elsif ($$segDataPt !~ /^Exif\0/) { | 
| 6145 | 0 |  |  |  |  | 0 | $self->Error('Incorrect EXIF segment identifier',1); | 
| 6146 |  |  |  |  |  |  | } | 
| 6147 | 52 |  |  |  |  | 176 | $segType = 'EXIF'; | 
| 6148 | 52 | 100 |  |  |  | 716 | last unless $$editDirs{IFD0}; | 
| 6149 |  |  |  |  |  |  | # add this data to the combined data if it exists | 
| 6150 | 51 | 50 |  |  |  | 222 | if (defined $combinedSegData) { | 
| 6151 | 0 |  |  |  |  | 0 | $combinedSegData .= substr($$segDataPt,$hdrLen); | 
| 6152 | 0 |  |  |  |  | 0 | $segDataPt = \$combinedSegData; | 
| 6153 | 0 |  |  |  |  | 0 | $segPos = $firstSegPos; | 
| 6154 | 0 |  |  |  |  | 0 | $length = length $combinedSegData;  # update length | 
| 6155 |  |  |  |  |  |  | } | 
| 6156 |  |  |  |  |  |  | # peek ahead to see if the next segment is extended EXIF | 
| 6157 | 51 | 50 |  |  |  | 245 | if ($dirOrder[0] eq 'ExtendedEXIF') { | 
| 6158 |  |  |  |  |  |  | # initialize combined data if necessary | 
| 6159 | 0 | 0 |  |  |  | 0 | unless (defined $combinedSegData) { | 
| 6160 | 0 |  |  |  |  | 0 | $combinedSegData = $$segDataPt; | 
| 6161 | 0 |  |  |  |  | 0 | $firstSegPos = $segPos; | 
| 6162 | 0 |  |  |  |  | 0 | $self->Warn('File contains multi-segment EXIF',1); | 
| 6163 |  |  |  |  |  |  | } | 
| 6164 | 0 |  |  |  |  | 0 | next Marker;    # get the next segment to combine | 
| 6165 |  |  |  |  |  |  | } | 
| 6166 | 51 | 50 |  |  |  | 233 | $doneDir{IFD0} and $self->Warn('Multiple APP1 EXIF records'); | 
| 6167 | 51 |  |  |  |  | 164 | $doneDir{IFD0} = 1; | 
| 6168 |  |  |  |  |  |  | # check del groups now so we can change byte order in one step | 
| 6169 | 51 | 100 | 66 |  |  | 845 | if ($$delGroup{IFD0} or $$delGroup{EXIF}) { | 
| 6170 | 1 |  |  |  |  | 3 | delete $doneDir{IFD0};  # delete so we will create a new one | 
| 6171 | 1 |  |  |  |  | 4 | $del = 1; | 
| 6172 | 1 |  |  |  |  | 2 | last; | 
| 6173 |  |  |  |  |  |  | } | 
| 6174 |  |  |  |  |  |  | # rewrite EXIF as if this were a TIFF file in memory | 
| 6175 | 50 |  |  |  |  | 631 | my %dirInfo = ( | 
| 6176 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6177 |  |  |  |  |  |  | DataPos  => -$hdrLen, # (remember: relative to Base!) | 
| 6178 |  |  |  |  |  |  | DirStart => $hdrLen, | 
| 6179 |  |  |  |  |  |  | Base     => $segPos + $hdrLen, | 
| 6180 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6181 |  |  |  |  |  |  | DirName  => 'IFD0', | 
| 6182 |  |  |  |  |  |  | ); | 
| 6183 |  |  |  |  |  |  | # write new EXIF data to memory | 
| 6184 | 50 |  |  |  |  | 309 | my $tagTablePtr = GetTagTable('Image::ExifTool::Exif::Main'); | 
| 6185 | 50 |  |  |  |  | 473 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF); | 
| 6186 | 50 | 50 |  |  |  | 303 | if (defined $buff) { | 
| 6187 | 50 |  |  |  |  | 182 | undef $$segDataPt;  # free the old buffer | 
| 6188 | 50 |  |  |  |  | 150 | $segDataPt = \$buff; | 
| 6189 |  |  |  |  |  |  | } else { | 
| 6190 | 0 | 0 |  |  |  | 0 | last Marker unless $self->Options('IgnoreMinorErrors'); | 
| 6191 |  |  |  |  |  |  | } | 
| 6192 |  |  |  |  |  |  | # delete segment if IFD contains no entries | 
| 6193 | 50 | 100 |  |  |  | 256 | length $$segDataPt or $del = 1, last; | 
| 6194 | 46 | 50 |  |  |  | 261 | if (length($$segDataPt) + length($exifAPP1hdr) > $maxSegmentLen) { | 
| 6195 | 0 | 0 |  |  |  | 0 | if ($self->Options('NoMultiExif')) { | 
| 6196 | 0 |  |  |  |  | 0 | $self->Error('EXIF is too large for JPEG segment'); | 
| 6197 |  |  |  |  |  |  | } else { | 
| 6198 | 0 |  |  |  |  | 0 | $self->Warn('Writing multi-segment EXIF',1); | 
| 6199 |  |  |  |  |  |  | } | 
| 6200 |  |  |  |  |  |  | } | 
| 6201 |  |  |  |  |  |  | # switch to buffered output if required | 
| 6202 | 46 | 100 | 66 |  |  | 438 | if (($$self{PREVIEW_INFO} or $$self{LeicaTrailer}) and not $oldOutfile) { | 
|  |  |  | 66 |  |  |  |  | 
| 6203 | 1 |  |  |  |  | 3 | $writeBuffer = ''; | 
| 6204 | 1 |  |  |  |  | 3 | $oldOutfile = $outfile; | 
| 6205 | 1 |  |  |  |  | 2 | $outfile = \$writeBuffer; | 
| 6206 |  |  |  |  |  |  | # must account for segment, EXIF and TIFF headers | 
| 6207 | 1 | 50 |  |  |  | 5 | $$self{PREVIEW_INFO}{Fixup}{Start} += 18 if $$self{PREVIEW_INFO}; | 
| 6208 | 1 | 50 |  |  |  | 6 | $$self{LeicaTrailer}{Fixup}{Start} += 18 if $$self{LeicaTrailer}; | 
| 6209 |  |  |  |  |  |  | } | 
| 6210 |  |  |  |  |  |  | # write as multi-segment | 
| 6211 | 46 |  |  |  |  | 308 | my $n = WriteMultiSegment($outfile, $marker, $exifAPP1hdr, $segDataPt, 'EXIF'); | 
| 6212 | 46 | 50 | 33 |  |  | 370 | if (not $n) { | 
|  |  | 50 |  |  |  |  |  | 
| 6213 | 0 |  |  |  |  | 0 | $err = 1; | 
| 6214 |  |  |  |  |  |  | } elsif ($n > 1 and $oldOutfile) { | 
| 6215 |  |  |  |  |  |  | # (punt on this because updating the pointers would be a real pain) | 
| 6216 | 0 |  |  |  |  | 0 | $self->Error("Can't write multi-segment EXIF with external pointers"); | 
| 6217 |  |  |  |  |  |  | } | 
| 6218 | 46 |  |  |  |  | 160 | undef $combinedSegData; | 
| 6219 | 46 |  |  |  |  | 156 | undef $$segDataPt; | 
| 6220 | 46 |  |  |  |  | 431 | next Marker; | 
| 6221 |  |  |  |  |  |  | # check for XMP data | 
| 6222 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^($xmpAPP1hdr|$xmpExtAPP1hdr)/) { | 
| 6223 | 21 |  |  |  |  | 71 | $segType = 'XMP'; | 
| 6224 | 21 | 50 |  |  |  | 86 | $$delGroup{XMP} and $del = 1, last; | 
| 6225 | 21 |  | 100 |  |  | 130 | $doneDir{XMP} = ($doneDir{XMP} || 0) + 1; | 
| 6226 | 21 | 100 |  |  |  | 87 | last unless $$editDirs{XMP}; | 
| 6227 | 14 | 100 |  |  |  | 71 | if ($doneDir{XMP} + $dirCount{XMP} > 1) { | 
| 6228 |  |  |  |  |  |  | # must assemble all XMP segments before writing | 
| 6229 | 3 |  |  |  |  | 8 | my ($guid, $extXMP); | 
| 6230 | 3 | 100 |  |  |  | 31 | if ($$segDataPt =~ /^$xmpExtAPP1hdr/) { | 
| 6231 |  |  |  |  |  |  | # save extended XMP data | 
| 6232 | 2 | 50 |  |  |  | 6 | if (length $$segDataPt < 75) { | 
| 6233 | 0 |  |  |  |  | 0 | $extendedXMP{Error} = 'Truncated data'; | 
| 6234 |  |  |  |  |  |  | } else { | 
| 6235 | 2 |  |  |  |  | 10 | my ($size, $off) = unpack('x67N2', $$segDataPt); | 
| 6236 | 2 |  |  |  |  | 7 | $guid = substr($$segDataPt, 35, 32); | 
| 6237 | 2 | 50 |  |  |  | 9 | if ($guid =~ /[^A-Za-z0-9]/) { # (technically, should be uppercase) | 
| 6238 | 0 |  |  |  |  | 0 | $extendedXMP{Error} = 'Invalid GUID'; | 
| 6239 |  |  |  |  |  |  | } else { | 
| 6240 |  |  |  |  |  |  | # remember extended data for each GUID | 
| 6241 | 2 |  |  |  |  | 5 | $extXMP = $extendedXMP{$guid}; | 
| 6242 | 2 | 100 |  |  |  | 8 | if ($extXMP) { | 
| 6243 | 1 | 50 |  |  |  | 5 | $size == $$extXMP{Size} or $extendedXMP{Error} = 'Inconsistent size'; | 
| 6244 |  |  |  |  |  |  | } else { | 
| 6245 | 1 |  |  |  |  | 4 | $extXMP = $extendedXMP{$guid} = { }; | 
| 6246 |  |  |  |  |  |  | } | 
| 6247 | 2 |  |  |  |  | 7 | $$extXMP{Size} = $size; | 
| 6248 | 2 |  |  |  |  | 8 | $$extXMP{$off} = substr($$segDataPt, 75); | 
| 6249 |  |  |  |  |  |  | } | 
| 6250 |  |  |  |  |  |  | } | 
| 6251 |  |  |  |  |  |  | } else { | 
| 6252 |  |  |  |  |  |  | # save all main XMP segments (should normally be only one) | 
| 6253 | 1 | 50 |  |  |  | 7 | $extendedXMP{Main} = [] unless $extendedXMP{Main}; | 
| 6254 | 1 |  |  |  |  | 5 | push @{$extendedXMP{Main}}, substr($$segDataPt, length $xmpAPP1hdr); | 
|  | 1 |  |  |  |  | 5 |  | 
| 6255 |  |  |  |  |  |  | } | 
| 6256 |  |  |  |  |  |  | # continue processing only if we have read all the segments | 
| 6257 | 3 | 100 |  |  |  | 15 | next Marker if $dirCount{XMP}; | 
| 6258 |  |  |  |  |  |  | # reconstruct an XMP super-segment | 
| 6259 | 1 |  |  |  |  | 3 | $$segDataPt = $xmpAPP1hdr; | 
| 6260 | 1 |  |  |  |  | 3 | my $goodGuid = ''; | 
| 6261 | 1 |  |  |  |  | 3 | foreach (@{$extendedXMP{Main}}) { | 
|  | 1 |  |  |  |  | 4 |  | 
| 6262 |  |  |  |  |  |  | # get the HasExtendedXMP GUID if it exists | 
| 6263 | 1 | 50 |  |  |  | 9 | if (/:HasExtendedXMP\s*(=\s*['"]|>)(\w{32})/) { | 
| 6264 |  |  |  |  |  |  | # warn of subsequent XMP blocks specifying a different | 
| 6265 |  |  |  |  |  |  | # HasExtendedXMP (have never seen this) | 
| 6266 | 1 | 50 | 33 |  |  | 5 | if ($goodGuid and $goodGuid ne $2) { | 
| 6267 | 0 |  |  |  |  | 0 | $self->WarnOnce('Multiple XMP segments specifying different extended XMP GUID'); | 
| 6268 |  |  |  |  |  |  | } | 
| 6269 | 1 |  |  |  |  | 4 | $goodGuid = $2; # GUID for the standard extended XMP | 
| 6270 |  |  |  |  |  |  | } | 
| 6271 | 1 |  |  |  |  | 4 | $$segDataPt .= $_; | 
| 6272 |  |  |  |  |  |  | } | 
| 6273 |  |  |  |  |  |  | # GUID of the extended XMP that we want to read | 
| 6274 | 1 |  | 50 |  |  | 7 | my $readGuid = $$self{OPTIONS}{ExtendedXMP} || 0; | 
| 6275 | 1 | 50 |  |  |  | 5 | $readGuid = $goodGuid if $readGuid eq '1'; | 
| 6276 | 1 |  |  |  |  | 7 | foreach $guid (sort keys %extendedXMP) { | 
| 6277 | 2 | 100 |  |  |  | 8 | next unless length $guid == 32;     # ignore other (internal) keys | 
| 6278 | 1 | 50 | 33 |  |  | 4 | if ($guid ne $readGuid and $readGuid ne '2') { | 
| 6279 | 0 | 0 |  |  |  | 0 | my $non = $guid eq $goodGuid ? '' : 'non-'; | 
| 6280 | 0 |  |  |  |  | 0 | $self->Warn("Ignored ${non}standard extended XMP (GUID $guid)"); | 
| 6281 | 0 |  |  |  |  | 0 | next; | 
| 6282 |  |  |  |  |  |  | } | 
| 6283 | 1 | 50 |  |  |  | 5 | if ($guid ne $goodGuid) { | 
| 6284 | 0 |  |  |  |  | 0 | $self->Warn("Reading non-standard extended XMP (GUID $guid)"); | 
| 6285 |  |  |  |  |  |  | } | 
| 6286 | 1 |  |  |  |  | 2 | $extXMP = $extendedXMP{$guid}; | 
| 6287 | 1 | 50 |  |  |  | 6 | next unless ref $extXMP eq 'HASH';  # (just to be safe) | 
| 6288 | 1 |  |  |  |  | 3 | my $size = $$extXMP{Size}; | 
| 6289 | 1 |  |  |  |  | 4 | my (@offsets, $off); | 
| 6290 | 1 |  |  |  |  | 5 | for ($off=0; $off<$size; ) { | 
| 6291 | 2 | 50 |  |  |  | 8 | last unless defined $$extXMP{$off}; | 
| 6292 | 2 |  |  |  |  | 4 | push @offsets, $off; | 
| 6293 | 2 |  |  |  |  | 7 | $off += length $$extXMP{$off}; | 
| 6294 |  |  |  |  |  |  | } | 
| 6295 | 1 | 50 |  |  |  | 5 | if ($off == $size) { | 
| 6296 |  |  |  |  |  |  | # add all XMP to super-segment | 
| 6297 | 1 |  |  |  |  | 7 | $$segDataPt .= $$extXMP{$_} foreach @offsets; | 
| 6298 |  |  |  |  |  |  | } else { | 
| 6299 | 0 |  |  |  |  | 0 | $self->Error("Incomplete extended XMP (GUID $guid)", 1); | 
| 6300 |  |  |  |  |  |  | } | 
| 6301 |  |  |  |  |  |  | } | 
| 6302 | 1 | 50 |  |  |  | 6 | $self->Error("$extendedXMP{Error} in extended XMP", 1) if $extendedXMP{Error}; | 
| 6303 |  |  |  |  |  |  | } | 
| 6304 | 12 |  |  |  |  | 43 | my $start = length $xmpAPP1hdr; | 
| 6305 | 12 |  |  |  |  | 66 | my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main'); | 
| 6306 | 12 |  |  |  |  | 111 | my %dirInfo = ( | 
| 6307 |  |  |  |  |  |  | DataPt     => $segDataPt, | 
| 6308 |  |  |  |  |  |  | DirStart   => $start, | 
| 6309 |  |  |  |  |  |  | Parent     => $markerName, | 
| 6310 |  |  |  |  |  |  | # limit XMP size and create extended XMP if necessary | 
| 6311 |  |  |  |  |  |  | MaxDataLen => $maxXMPLen - length($xmpAPP1hdr), | 
| 6312 |  |  |  |  |  |  | ); | 
| 6313 | 12 |  |  |  |  | 97 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 6314 | 12 | 100 |  |  |  | 62 | if (defined $newData) { | 
| 6315 | 9 |  |  |  |  | 32 | undef %extendedXMP; | 
| 6316 | 9 | 100 |  |  |  | 48 | if (length $newData) { | 
| 6317 |  |  |  |  |  |  | # write multi-segment XMP (XMP plus extended XMP if necessary) | 
| 6318 |  |  |  |  |  |  | WriteMultiXMP($self, $outfile, \$newData, $dirInfo{ExtendedXMP}, | 
| 6319 | 7 | 50 |  |  |  | 59 | $dirInfo{ExtendedGUID}) or $err = 1; | 
| 6320 | 7 |  |  |  |  | 29 | undef $$segDataPt;  # free the old buffer | 
| 6321 | 7 |  |  |  |  | 58 | next Marker; | 
| 6322 |  |  |  |  |  |  | } else { | 
| 6323 | 2 |  |  |  |  | 7 | $$segDataPt = '';   # delete the XMP | 
| 6324 |  |  |  |  |  |  | } | 
| 6325 |  |  |  |  |  |  | } else { | 
| 6326 | 3 | 50 |  |  |  | 15 | $verbose and print $out "    [XMP rewritten with no changes]\n"; | 
| 6327 | 3 | 50 |  |  |  | 16 | if ($doneDir{XMP} > 1) { | 
| 6328 |  |  |  |  |  |  | # re-write original multi-segment XMP | 
| 6329 | 0 |  |  |  |  | 0 | my ($dat, $guid, $extXMP, $off); | 
| 6330 | 0 |  |  |  |  | 0 | foreach $dat (@{$extendedXMP{Main}}) {      # main XMP | 
|  | 0 |  |  |  |  | 0 |  | 
| 6331 | 0 | 0 |  |  |  | 0 | next unless length $dat; | 
| 6332 | 0 |  |  |  |  | 0 | $s = pack('n', length($xmpAPP1hdr) + length($dat) + 2); | 
| 6333 | 0 | 0 |  |  |  | 0 | Write($outfile, $hdr, $s, $xmpAPP1hdr, $dat) or $err = 1; | 
| 6334 |  |  |  |  |  |  | } | 
| 6335 | 0 |  |  |  |  | 0 | foreach $guid (sort keys %extendedXMP) {    # extended XMP | 
| 6336 | 0 | 0 |  |  |  | 0 | next unless length $guid == 32; | 
| 6337 | 0 |  |  |  |  | 0 | $extXMP = $extendedXMP{$guid}; | 
| 6338 | 0 | 0 |  |  |  | 0 | next unless ref $extXMP eq 'HASH'; | 
| 6339 | 0 | 0 |  |  |  | 0 | my $size = $$extXMP{Size} or next; | 
| 6340 | 0 |  |  |  |  | 0 | for ($off=0; defined $$extXMP{$off}; $off += length $$extXMP{$off}) { | 
| 6341 | 0 |  |  |  |  | 0 | $s = pack('n', length($xmpExtAPP1hdr) + length($$extXMP{$off}) + 42); | 
| 6342 |  |  |  |  |  |  | Write($outfile, $hdr, $s, $xmpExtAPP1hdr, $guid, | 
| 6343 | 0 | 0 |  |  |  | 0 | pack('N2', $size, $off), $$extXMP{$off}) or $err = 1; | 
| 6344 |  |  |  |  |  |  | } | 
| 6345 |  |  |  |  |  |  | } | 
| 6346 | 0 |  |  |  |  | 0 | undef $$segDataPt;  # free the old buffer | 
| 6347 | 0 |  |  |  |  | 0 | undef %extendedXMP; | 
| 6348 | 0 |  |  |  |  | 0 | next Marker; | 
| 6349 |  |  |  |  |  |  | } | 
| 6350 |  |  |  |  |  |  | # continue on to re-write original single-segment XMP | 
| 6351 |  |  |  |  |  |  | } | 
| 6352 | 5 | 100 |  |  |  | 34 | $del = 1 unless length $$segDataPt; | 
| 6353 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^http/ or $$segDataPt =~ / | 
| 6354 | 0 |  |  |  |  | 0 | $self->Warn('Ignored APP1 XMP segment with non-standard header', 1); | 
| 6355 |  |  |  |  |  |  | } | 
| 6356 |  |  |  |  |  |  | } elsif ($marker == 0xe2) {         # APP2 (ICC Profile, FPXR, MPF) | 
| 6357 | 0 | 0 | 0 |  |  | 0 | if ($$segDataPt =~ /^ICC_PROFILE\0/ and $length >= 14) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 6358 | 0 |  |  |  |  | 0 | $segType = 'ICC_Profile'; | 
| 6359 | 0 | 0 |  |  |  | 0 | $$delGroup{ICC_Profile} and $del = 1, last; | 
| 6360 |  |  |  |  |  |  | # must concatenate blocks of profile | 
| 6361 | 0 |  |  |  |  | 0 | my $chunkNum = Get8u($segDataPt, 12); | 
| 6362 | 0 |  |  |  |  | 0 | my $chunksTot = Get8u($segDataPt, 13); | 
| 6363 | 0 | 0 |  |  |  | 0 | if (defined $iccChunksTotal) { | 
| 6364 |  |  |  |  |  |  | # abort parsing ICC_Profile if the total chunk count is inconsistent | 
| 6365 | 0 | 0 | 0 |  |  | 0 | if ($chunksTot != $iccChunksTotal and defined $iccChunkCount) { | 
| 6366 |  |  |  |  |  |  | # an error because the accumulated profile data will be lost | 
| 6367 | 0 |  |  |  |  | 0 | $self->Error('Inconsistent ICC_Profile chunk count', 1); | 
| 6368 | 0 |  |  |  |  | 0 | undef $iccChunkCount; # abort ICC_Profile parsing | 
| 6369 | 0 |  |  |  |  | 0 | undef $chunkNum;      # avoid 2nd warning below | 
| 6370 | 0 |  |  |  |  | 0 | ++$$self{CHANGED};    # we are deleting the bad chunks before this one | 
| 6371 |  |  |  |  |  |  | } | 
| 6372 |  |  |  |  |  |  | } else { | 
| 6373 | 0 |  |  |  |  | 0 | $iccChunkCount = 0; | 
| 6374 | 0 |  |  |  |  | 0 | $iccChunksTotal = $chunksTot; | 
| 6375 | 0 | 0 |  |  |  | 0 | $self->Warn('ICC_Profile chunk count is zero') if !$chunksTot; | 
| 6376 |  |  |  |  |  |  | } | 
| 6377 | 0 | 0 |  |  |  | 0 | if (defined $iccChunkCount) { | 
|  |  | 0 |  |  |  |  |  | 
| 6378 |  |  |  |  |  |  | # save this chunk | 
| 6379 | 0 | 0 |  |  |  | 0 | if (defined $iccChunk[$chunkNum]) { | 
| 6380 | 0 |  |  |  |  | 0 | $self->Warn("Duplicate ICC_Profile chunk number $chunkNum"); | 
| 6381 | 0 |  |  |  |  | 0 | $iccChunk[$chunkNum] .= substr($$segDataPt, 14); | 
| 6382 |  |  |  |  |  |  | } else { | 
| 6383 | 0 |  |  |  |  | 0 | $iccChunk[$chunkNum] = substr($$segDataPt, 14); | 
| 6384 |  |  |  |  |  |  | } | 
| 6385 |  |  |  |  |  |  | # continue accumulating chunks unless we have all of them | 
| 6386 | 0 | 0 |  |  |  | 0 | next Marker unless ++$iccChunkCount >= $iccChunksTotal; | 
| 6387 | 0 |  |  |  |  | 0 | undef $iccChunkCount;   # prevent reprocessing | 
| 6388 | 0 |  |  |  |  | 0 | $doneDir{ICC_Profile} = 1; | 
| 6389 |  |  |  |  |  |  | # combine the ICC_Profile chunks | 
| 6390 | 0 |  |  |  |  | 0 | my $icc_profile = ''; | 
| 6391 | 0 |  | 0 |  |  | 0 | defined $_ and $icc_profile .= $_ foreach @iccChunk; | 
| 6392 | 0 |  |  |  |  | 0 | undef @iccChunk;   # free memory | 
| 6393 | 0 |  |  |  |  | 0 | $segDataPt = \$icc_profile; | 
| 6394 | 0 |  |  |  |  | 0 | $length = length $icc_profile; | 
| 6395 | 0 |  |  |  |  | 0 | my $tagTablePtr = GetTagTable('Image::ExifTool::ICC_Profile::Main'); | 
| 6396 | 0 |  |  |  |  | 0 | my %dirInfo = ( | 
| 6397 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6398 |  |  |  |  |  |  | DataPos  => $segPos + 14, | 
| 6399 |  |  |  |  |  |  | DataLen  => $length, | 
| 6400 |  |  |  |  |  |  | DirStart => 0, | 
| 6401 |  |  |  |  |  |  | DirLen   => $length, | 
| 6402 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6403 |  |  |  |  |  |  | ); | 
| 6404 | 0 |  |  |  |  | 0 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 6405 | 0 | 0 |  |  |  | 0 | if (defined $newData) { | 
| 6406 | 0 |  |  |  |  | 0 | undef $$segDataPt;  # free the old buffer | 
| 6407 | 0 |  |  |  |  | 0 | $segDataPt = \$newData; | 
| 6408 |  |  |  |  |  |  | } | 
| 6409 | 0 | 0 |  |  |  | 0 | length $$segDataPt or $del = 1, last; | 
| 6410 |  |  |  |  |  |  | # write as ICC multi-segment | 
| 6411 | 0 | 0 |  |  |  | 0 | WriteMultiSegment($outfile, $marker, "ICC_PROFILE\0", $segDataPt, 'ICC') or $err = 1; | 
| 6412 | 0 |  |  |  |  | 0 | undef $$segDataPt; | 
| 6413 | 0 |  |  |  |  | 0 | next Marker; | 
| 6414 |  |  |  |  |  |  | } elsif (defined $chunkNum) { | 
| 6415 | 0 |  |  |  |  | 0 | $self->WarnOnce('Invalid or extraneous ICC_Profile chunk(s)'); | 
| 6416 |  |  |  |  |  |  | # fall through to preserve this extra profile... | 
| 6417 |  |  |  |  |  |  | } | 
| 6418 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^FPXR\0/) { | 
| 6419 | 0 |  |  |  |  | 0 | $segType = 'FPXR'; | 
| 6420 | 0 | 0 |  |  |  | 0 | $$delGroup{FlashPix} and $del = 1; | 
| 6421 |  |  |  |  |  |  | } elsif ($$segDataPt =~ /^MPF\0/) { | 
| 6422 | 0 |  |  |  |  | 0 | $segType = 'MPF'; | 
| 6423 | 0 | 0 |  |  |  | 0 | $$delGroup{MPF} and $del = 1; | 
| 6424 |  |  |  |  |  |  | } | 
| 6425 |  |  |  |  |  |  | } elsif ($marker == 0xe3) {         # APP3 (Kodak Meta) | 
| 6426 | 1 | 50 |  |  |  | 13 | if ($$segDataPt =~ /^(Meta|META|Exif)\0\0/) { | 
| 6427 | 1 |  |  |  |  | 5 | $segType = 'Kodak Meta'; | 
| 6428 | 1 | 50 |  |  |  | 7 | $$delGroup{Meta} and $del = 1, last; | 
| 6429 | 1 | 50 |  |  |  | 6 | $doneDir{Meta} and $self->Warn('Multiple APP3 Meta segments'); | 
| 6430 | 1 |  |  |  |  | 3 | $doneDir{Meta} = 1; | 
| 6431 | 1 | 50 |  |  |  | 5 | last unless $$editDirs{Meta}; | 
| 6432 |  |  |  |  |  |  | # rewrite Meta IFD as if this were a TIFF file in memory | 
| 6433 | 1 |  |  |  |  | 11 | my %dirInfo = ( | 
| 6434 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6435 |  |  |  |  |  |  | DataPos  => -6, # (remember: relative to Base!) | 
| 6436 |  |  |  |  |  |  | DirStart => 6, | 
| 6437 |  |  |  |  |  |  | Base     => $segPos + 6, | 
| 6438 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6439 |  |  |  |  |  |  | DirName  => 'Meta', | 
| 6440 |  |  |  |  |  |  | ); | 
| 6441 |  |  |  |  |  |  | # write new data to memory | 
| 6442 | 1 |  |  |  |  | 6 | my $tagTablePtr = GetTagTable('Image::ExifTool::Kodak::Meta'); | 
| 6443 | 1 |  |  |  |  | 10 | my $buff = $self->WriteDirectory(\%dirInfo, $tagTablePtr, \&WriteTIFF); | 
| 6444 | 1 | 50 |  |  |  | 8 | if (defined $buff) { | 
| 6445 |  |  |  |  |  |  | # update segment with new data | 
| 6446 | 1 |  |  |  |  | 7 | $$segDataPt = substr($$segDataPt,0,6) . $buff; | 
| 6447 |  |  |  |  |  |  | } else { | 
| 6448 | 0 | 0 |  |  |  | 0 | last Marker unless $self->Options('IgnoreMinorErrors'); | 
| 6449 |  |  |  |  |  |  | } | 
| 6450 |  |  |  |  |  |  | # delete segment if IFD contains no entries | 
| 6451 | 1 | 50 |  |  |  | 9 | $del = 1 unless length($$segDataPt) > 6; | 
| 6452 |  |  |  |  |  |  | } | 
| 6453 |  |  |  |  |  |  | } elsif ($marker == 0xe5) {         # APP5 (Ricoh RMETA) | 
| 6454 | 0 | 0 |  |  |  | 0 | if ($$segDataPt =~ /^RMETA\0/) { | 
| 6455 | 0 |  |  |  |  | 0 | $segType = 'Ricoh RMETA'; | 
| 6456 | 0 | 0 |  |  |  | 0 | $$delGroup{RMETA} and $del = 1; | 
| 6457 |  |  |  |  |  |  | } | 
| 6458 |  |  |  |  |  |  | } elsif ($marker == 0xec) {         # APP12 (Ducky) | 
| 6459 | 1 | 50 |  |  |  | 16 | if ($$segDataPt =~ /^Ducky/) { | 
| 6460 | 1 |  |  |  |  | 6 | $segType = 'Ducky'; | 
| 6461 | 1 | 50 |  |  |  | 10 | $$delGroup{Ducky} and $del = 1, last; | 
| 6462 | 1 | 50 |  |  |  | 6 | $doneDir{Ducky} and $self->Warn('Multiple APP12 Ducky segments'); | 
| 6463 | 1 |  |  |  |  | 4 | $doneDir{Ducky} = 1; | 
| 6464 | 1 | 50 |  |  |  | 9 | last unless $$editDirs{Ducky}; | 
| 6465 | 1 |  |  |  |  | 6 | my $tagTablePtr = GetTagTable('Image::ExifTool::APP12::Ducky'); | 
| 6466 | 1 |  |  |  |  | 16 | my %dirInfo = ( | 
| 6467 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6468 |  |  |  |  |  |  | DataPos  => $segPos, | 
| 6469 |  |  |  |  |  |  | DataLen  => $length, | 
| 6470 |  |  |  |  |  |  | DirStart => 5,     # directory starts after identifier | 
| 6471 |  |  |  |  |  |  | DirLen   => $length-5, | 
| 6472 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6473 |  |  |  |  |  |  | ); | 
| 6474 | 1 |  |  |  |  | 6 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 6475 | 1 | 50 |  |  |  | 7 | if (defined $newData) { | 
| 6476 | 1 |  |  |  |  | 2 | undef $$segDataPt;  # free the old buffer | 
| 6477 |  |  |  |  |  |  | # add header to new segment unless empty | 
| 6478 | 1 | 50 |  |  |  | 9 | $newData = 'Ducky' . $newData if length $newData; | 
| 6479 | 1 |  |  |  |  | 4 | $segDataPt = \$newData; | 
| 6480 |  |  |  |  |  |  | } | 
| 6481 | 1 | 50 |  |  |  | 6 | $del = 1 unless length $$segDataPt; | 
| 6482 |  |  |  |  |  |  | } | 
| 6483 |  |  |  |  |  |  | } elsif ($marker == 0xed) {         # APP13 (Photoshop) | 
| 6484 | 9 | 100 |  |  |  | 153 | if ($$segDataPt =~ /^$psAPP13hdr/) { | 
| 6485 | 8 |  |  |  |  | 34 | $segType = 'Photoshop'; | 
| 6486 |  |  |  |  |  |  | # add this data to the combined data if it exists | 
| 6487 | 8 | 50 |  |  |  | 44 | if (defined $combinedSegData) { | 
| 6488 | 0 |  |  |  |  | 0 | $combinedSegData .= substr($$segDataPt,length($psAPP13hdr)); | 
| 6489 | 0 |  |  |  |  | 0 | $segDataPt = \$combinedSegData; | 
| 6490 | 0 |  |  |  |  | 0 | $length = length $combinedSegData;  # update length | 
| 6491 |  |  |  |  |  |  | } | 
| 6492 |  |  |  |  |  |  | # peek ahead to see if the next segment is photoshop data too | 
| 6493 | 8 | 50 |  |  |  | 46 | if ($dirOrder[0] eq 'Photoshop') { | 
| 6494 |  |  |  |  |  |  | # initialize combined data if necessary | 
| 6495 | 0 | 0 |  |  |  | 0 | $combinedSegData = $$segDataPt unless defined $combinedSegData; | 
| 6496 | 0 |  |  |  |  | 0 | next Marker;    # get the next segment to combine | 
| 6497 |  |  |  |  |  |  | } | 
| 6498 | 8 | 50 |  |  |  | 61 | if ($doneDir{Photoshop}) { | 
| 6499 | 0 |  |  |  |  | 0 | $self->Warn('Multiple Photoshop records'); | 
| 6500 |  |  |  |  |  |  | # only rewrite the first Photoshop segment when deleting this group | 
| 6501 |  |  |  |  |  |  | # (to remove multiples when deleting and adding back in one step) | 
| 6502 | 0 | 0 |  |  |  | 0 | $$delGroup{Photoshop} and $del = 1, last; | 
| 6503 |  |  |  |  |  |  | } | 
| 6504 | 8 |  |  |  |  | 41 | $doneDir{Photoshop} = 1; | 
| 6505 |  |  |  |  |  |  | # process APP13 Photoshop record | 
| 6506 | 8 |  |  |  |  | 34 | my $tagTablePtr = GetTagTable('Image::ExifTool::Photoshop::Main'); | 
| 6507 | 8 |  |  |  |  | 110 | my %dirInfo = ( | 
| 6508 |  |  |  |  |  |  | DataPt   => $segDataPt, | 
| 6509 |  |  |  |  |  |  | DataPos  => $segPos, | 
| 6510 |  |  |  |  |  |  | DataLen  => $length, | 
| 6511 |  |  |  |  |  |  | DirStart => 14,     # directory starts after identifier | 
| 6512 |  |  |  |  |  |  | DirLen   => $length-14, | 
| 6513 |  |  |  |  |  |  | Parent   => $markerName, | 
| 6514 |  |  |  |  |  |  | ); | 
| 6515 | 8 |  |  |  |  | 49 | my $newData = $self->WriteDirectory(\%dirInfo, $tagTablePtr); | 
| 6516 | 8 | 50 |  |  |  | 47 | if (defined $newData) { | 
| 6517 | 8 |  |  |  |  | 29 | undef $$segDataPt;  # free the old buffer | 
| 6518 | 8 |  |  |  |  | 28 | $segDataPt = \$newData; | 
| 6519 |  |  |  |  |  |  | } | 
| 6520 | 8 | 100 |  |  |  | 75 | length $$segDataPt or $del = 1, last; | 
| 6521 |  |  |  |  |  |  | # write as multi-segment | 
| 6522 | 6 | 50 |  |  |  | 33 | WriteMultiSegment($outfile, $marker, $psAPP13hdr, $segDataPt) or $err = 1; | 
| 6523 | 6 |  |  |  |  | 17 | undef $combinedSegData; | 
| 6524 | 6 |  |  |  |  | 23 | undef $$segDataPt; | 
| 6525 | 6 |  |  |  |  | 46 | next Marker; | 
| 6526 |  |  |  |  |  |  | } | 
| 6527 |  |  |  |  |  |  | } elsif ($marker == 0xee) {         # APP14 (Adobe) | 
| 6528 | 4 | 50 |  |  |  | 34 | if ($$segDataPt =~ /^Adobe/) { | 
| 6529 | 4 |  |  |  |  | 14 | $segType = 'Adobe'; | 
| 6530 |  |  |  |  |  |  | # delete it and replace it later if editing | 
| 6531 | 4 | 50 | 33 |  |  | 34 | if ($$delGroup{Adobe} or $$editDirs{Adobe}) { | 
| 6532 | 0 |  |  |  |  | 0 | $del = 1; | 
| 6533 | 0 |  |  |  |  | 0 | undef $doneDir{Adobe};  # so we can add it back again above | 
| 6534 |  |  |  |  |  |  | } | 
| 6535 |  |  |  |  |  |  | } | 
| 6536 |  |  |  |  |  |  | } elsif ($marker == 0xfe) {         # COM (JPEG comment) | 
| 6537 | 4 |  |  |  |  | 14 | my $newComment; | 
| 6538 | 4 | 50 |  |  |  | 26 | unless ($doneDir{COM}) { | 
| 6539 | 4 |  |  |  |  | 15 | $doneDir{COM} = 1; | 
| 6540 | 4 | 100 | 100 |  |  | 29 | unless ($$delGroup{File} and $$delGroup{File} != 2) { | 
| 6541 | 3 |  |  |  |  | 14 | my $tagInfo = $Image::ExifTool::Extra{Comment}; | 
| 6542 | 3 |  |  |  |  | 18 | my $nvHash = $self->GetNewValueHash($tagInfo); | 
| 6543 | 3 |  |  |  |  | 21 | my $val = $segData; | 
| 6544 | 3 |  |  |  |  | 14 | $val =~ s/\0+$//;   # allow for stupid software that adds NULL terminator | 
| 6545 | 3 | 50 | 33 |  |  | 16 | if ($self->IsOverwriting($nvHash, $val) or $$delGroup{File}) { | 
| 6546 | 3 |  |  |  |  | 12 | $newComment = $self->GetNewValue($nvHash); | 
| 6547 |  |  |  |  |  |  | } else { | 
| 6548 | 0 |  |  |  |  | 0 | delete $$editDirs{COM}; # we aren't editing COM after all | 
| 6549 | 0 |  |  |  |  | 0 | last; | 
| 6550 |  |  |  |  |  |  | } | 
| 6551 |  |  |  |  |  |  | } | 
| 6552 |  |  |  |  |  |  | } | 
| 6553 | 4 |  |  |  |  | 36 | $self->VerboseValue('- Comment', $$segDataPt); | 
| 6554 | 4 | 100 |  |  |  | 21 | if (defined $newComment) { | 
| 6555 |  |  |  |  |  |  | # write out the comments | 
| 6556 | 2 |  |  |  |  | 12 | $self->VerboseValue('+ Comment', $newComment); | 
| 6557 | 2 | 50 |  |  |  | 15 | WriteMultiSegment($outfile, 0xfe, '', \$newComment) or $err = 1; | 
| 6558 |  |  |  |  |  |  | } else { | 
| 6559 | 2 | 50 |  |  |  | 8 | $verbose and print $out "  Deleting COM segment\n"; | 
| 6560 |  |  |  |  |  |  | } | 
| 6561 | 4 |  |  |  |  | 14 | ++$$self{CHANGED};      # increment the changed flag | 
| 6562 | 4 |  |  |  |  | 10 | undef $segDataPt;       # don't write existing comment | 
| 6563 |  |  |  |  |  |  | } | 
| 6564 | 53 |  |  |  |  | 133 | last;   # didn't want to loop anyway | 
| 6565 |  |  |  |  |  |  | } | 
| 6566 |  |  |  |  |  |  |  | 
| 6567 |  |  |  |  |  |  | # delete necessary segments (including unknown segments if deleting all) | 
| 6568 | 460 | 100 | 100 |  |  | 2334 | if ($del or ($$delGroup{'*'} and not $segType and $marker>=0xe0 and $marker<=0xef)) { | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 6569 | 13 | 100 |  |  |  | 45 | $segType = 'unknown' unless $segType; | 
| 6570 | 13 | 50 |  |  |  | 49 | $verbose and print $out "  Deleting $markerName $segType segment\n"; | 
| 6571 | 13 |  |  |  |  | 34 | ++$$self{CHANGED}; | 
| 6572 | 13 |  |  |  |  | 41 | next Marker; | 
| 6573 |  |  |  |  |  |  | } | 
| 6574 |  |  |  |  |  |  | # write out this segment if $segDataPt is still defined | 
| 6575 | 447 | 100 | 66 |  |  | 2063 | if (defined $segDataPt and defined $$segDataPt) { | 
| 6576 |  |  |  |  |  |  | # write the data for this record (the data could have been | 
| 6577 |  |  |  |  |  |  | # modified, so recalculate the length word) | 
| 6578 | 443 |  |  |  |  | 890 | my $size = length($$segDataPt); | 
| 6579 | 443 | 50 |  |  |  | 1075 | if ($size > $maxSegmentLen) { | 
| 6580 | 0 | 0 |  |  |  | 0 | $segType or $segType = 'Unknown'; | 
| 6581 | 0 |  |  |  |  | 0 | $self->Error("$segType $markerName segment too large! ($size bytes)"); | 
| 6582 | 0 |  |  |  |  | 0 | $err = 1; | 
| 6583 |  |  |  |  |  |  | } else { | 
| 6584 | 443 |  |  |  |  | 1402 | $s = pack('n', length($$segDataPt) + 2); | 
| 6585 | 443 | 50 |  |  |  | 1309 | Write($outfile, $hdr, $s, $$segDataPt) or $err = 1; | 
| 6586 |  |  |  |  |  |  | } | 
| 6587 | 443 |  |  |  |  | 1145 | undef $$segDataPt;  # free the buffer | 
| 6588 | 443 |  |  |  |  | 941 | undef $segDataPt; | 
| 6589 |  |  |  |  |  |  | } | 
| 6590 |  |  |  |  |  |  | } | 
| 6591 |  |  |  |  |  |  | # make sure the ICC_Profile was complete | 
| 6592 | 111 | 50 |  |  |  | 588 | $self->Error('Incomplete ICC_Profile record', 1) if defined $iccChunkCount; | 
| 6593 | 111 | 100 |  |  |  | 475 | pop @$path if @$path > $pn; | 
| 6594 |  |  |  |  |  |  | # if oldOutfile is still set, there was an error copying the JPEG | 
| 6595 | 111 | 50 |  |  |  | 372 | $oldOutfile and return 0; | 
| 6596 | 111 | 50 |  |  |  | 377 | if ($rtnVal) { | 
| 6597 |  |  |  |  |  |  | # add any new trailers we are creating | 
| 6598 | 111 |  |  |  |  | 804 | my $trailPt = $self->AddNewTrailers(); | 
| 6599 | 111 | 100 | 50 |  |  | 491 | Write($outfile, $$trailPt) or $err = 1 if $trailPt; | 
| 6600 |  |  |  |  |  |  | } | 
| 6601 |  |  |  |  |  |  | # set return value to -1 if we only had a write error | 
| 6602 | 111 | 50 | 33 |  |  | 786 | $rtnVal = -1 if $rtnVal and $err; | 
| 6603 | 111 | 50 | 66 |  |  | 522 | if ($creatingEXV and $rtnVal > 0 and not $$self{CHANGED}) { | 
|  |  |  | 66 |  |  |  |  | 
| 6604 | 0 |  |  |  |  | 0 | $self->Error('Nothing written'); | 
| 6605 | 0 |  |  |  |  | 0 | $rtnVal = -1; | 
| 6606 |  |  |  |  |  |  | } | 
| 6607 | 111 |  |  |  |  | 1295 | return $rtnVal; | 
| 6608 |  |  |  |  |  |  | } | 
| 6609 |  |  |  |  |  |  |  | 
| 6610 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6611 |  |  |  |  |  |  | # Validate an image for writing | 
| 6612 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference, 1) raw value reference | 
| 6613 |  |  |  |  |  |  | # Returns: error string or undef on success | 
| 6614 |  |  |  |  |  |  | sub CheckImage($$) | 
| 6615 |  |  |  |  |  |  | { | 
| 6616 | 138 |  |  | 138 | 0 | 550 | my ($self, $valPtr) = @_; | 
| 6617 | 138 | 100 | 100 |  |  | 1206 | if (length($$valPtr) and $$valPtr!~/^\xff\xd8/ and not | 
|  |  |  | 100 |  |  |  |  | 
| 6618 |  |  |  |  |  |  | $self->Options('IgnoreMinorErrors')) | 
| 6619 |  |  |  |  |  |  | { | 
| 6620 | 25 |  |  |  |  | 272 | return '[Minor] Not a valid image'; | 
| 6621 |  |  |  |  |  |  | } | 
| 6622 | 113 |  |  |  |  | 984 | return undef; | 
| 6623 |  |  |  |  |  |  | } | 
| 6624 |  |  |  |  |  |  |  | 
| 6625 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6626 |  |  |  |  |  |  | # check a value for validity | 
| 6627 |  |  |  |  |  |  | # Inputs: 0) value reference, 1) format string, 2) optional count | 
| 6628 |  |  |  |  |  |  | # Returns: error string, or undef on success | 
| 6629 |  |  |  |  |  |  | # Notes: May modify value (if a count is specified for a string, it is null-padded | 
| 6630 |  |  |  |  |  |  | # to the specified length, and floating point values are rounded to integer if required) | 
| 6631 |  |  |  |  |  |  | sub CheckValue($$;$) | 
| 6632 |  |  |  |  |  |  | { | 
| 6633 | 19175 |  |  | 19175 | 0 | 47081 | my ($valPtr, $format, $count) = @_; | 
| 6634 | 19175 |  |  |  |  | 34188 | my (@vals, $val, $n); | 
| 6635 |  |  |  |  |  |  |  | 
| 6636 | 19175 | 100 | 100 |  |  | 72805 | if ($format eq 'string' or $format eq 'undef') { | 
| 6637 | 2487 | 100 | 66 |  |  | 11003 | return undef unless $count and $count > 0; | 
| 6638 | 301 |  |  |  |  | 718 | my $len = length($$valPtr); | 
| 6639 | 301 | 100 |  |  |  | 1052 | if ($format eq 'string') { | 
| 6640 | 198 | 100 |  |  |  | 675 | $len >= $count and return 'String too long'; | 
| 6641 |  |  |  |  |  |  | } else { | 
| 6642 | 103 | 50 |  |  |  | 394 | $len > $count and return 'Data too long'; | 
| 6643 |  |  |  |  |  |  | } | 
| 6644 | 291 | 100 |  |  |  | 811 | if ($len < $count) { | 
| 6645 | 232 |  |  |  |  | 880 | $$valPtr .= "\0" x ($count - $len); | 
| 6646 |  |  |  |  |  |  | } | 
| 6647 | 291 |  |  |  |  | 965 | return undef; | 
| 6648 |  |  |  |  |  |  | } | 
| 6649 | 16688 | 100 | 66 |  |  | 45004 | if ($count and $count != 1) { | 
| 6650 | 1923 |  |  |  |  | 6581 | @vals = split(' ',$$valPtr); | 
| 6651 | 1923 | 100 | 100 |  |  | 5099 | $count < 0 and ($count = @vals or return undef); | 
| 6652 |  |  |  |  |  |  | } else { | 
| 6653 | 14765 |  |  |  |  | 24106 | $count = 1; | 
| 6654 | 14765 |  |  |  |  | 32785 | @vals = ( $$valPtr ); | 
| 6655 |  |  |  |  |  |  | } | 
| 6656 | 16669 | 100 |  |  |  | 37935 | if (@vals != $count) { | 
| 6657 | 913 | 100 |  |  |  | 2394 | my $str = @vals > $count ? 'Too many' : 'Not enough'; | 
| 6658 | 913 |  |  |  |  | 3541 | return "$str values specified ($count required)"; | 
| 6659 |  |  |  |  |  |  | } | 
| 6660 | 15756 |  |  |  |  | 42566 | for ($n=0; $n<$count; ++$n) { | 
| 6661 | 18703 |  |  |  |  | 32790 | $val = shift @vals; | 
| 6662 | 18703 | 100 | 100 |  |  | 70535 | if ($format =~ /^int/) { | 
|  |  | 100 | 100 |  |  |  |  | 
| 6663 |  |  |  |  |  |  | # make sure the value is integer | 
| 6664 | 17349 | 100 |  |  |  | 58462 | unless (IsInt($val)) { | 
| 6665 | 3032 | 100 |  |  |  | 7447 | if (IsHex($val)) { | 
| 6666 | 7 |  |  |  |  | 32 | $val = $$valPtr = hex($val); | 
| 6667 |  |  |  |  |  |  | } else { | 
| 6668 |  |  |  |  |  |  | # round single floating point values to the nearest integer | 
| 6669 | 3025 | 100 | 100 |  |  | 8556 | return 'Not an integer' unless IsFloat($val) and $count == 1; | 
| 6670 | 1267 | 100 |  |  |  | 5635 | $val = $$valPtr = int($val + ($val < 0 ? -0.5 : 0.5)); | 
| 6671 |  |  |  |  |  |  | } | 
| 6672 |  |  |  |  |  |  | } | 
| 6673 | 15591 | 50 |  |  |  | 48271 | my $rng = $intRange{$format} or return "Bad int format: $format"; | 
| 6674 | 15591 | 100 |  |  |  | 38268 | return "Value below $format minimum" if $val < $$rng[0]; | 
| 6675 |  |  |  |  |  |  | # (allow 0xfeedfeed code as value for 16-bit pointers) | 
| 6676 | 15290 | 100 | 66 |  |  | 53280 | return "Value above $format maximum" if $val > $$rng[1] and $val != 0xfeedfeed; | 
| 6677 |  |  |  |  |  |  | } elsif ($format =~ /^rational/ or $format eq 'float' or $format eq 'double') { | 
| 6678 |  |  |  |  |  |  | # make sure the value is a valid floating point number | 
| 6679 | 1351 | 100 |  |  |  | 4577 | unless (IsFloat($val)) { | 
| 6680 |  |  |  |  |  |  | # allow 'inf', 'undef' and fractional rational values | 
| 6681 | 268 | 100 |  |  |  | 1059 | if ($format =~ /^rational/) { | 
| 6682 | 232 | 100 | 66 |  |  | 1187 | next if $val eq 'inf' or $val eq 'undef'; | 
| 6683 | 231 | 100 |  |  |  | 890 | if ($val =~ m{^([-+]?\d+)/(\d+)$}) { | 
| 6684 | 70 | 50 | 66 |  |  | 419 | next unless $1 < 0 and $format =~ /u$/; | 
| 6685 | 0 |  |  |  |  | 0 | return 'Must be an unsigned rational'; | 
| 6686 |  |  |  |  |  |  | } | 
| 6687 |  |  |  |  |  |  | } | 
| 6688 | 197 |  |  |  |  | 720 | return 'Not a floating point number'; | 
| 6689 |  |  |  |  |  |  | } | 
| 6690 | 1083 | 50 | 66 |  |  | 6888 | if ($format =~ /^rational\d+u$/ and $val < 0) { | 
| 6691 | 0 |  |  |  |  | 0 | return 'Must be a positive number'; | 
| 6692 |  |  |  |  |  |  | } | 
| 6693 |  |  |  |  |  |  | } | 
| 6694 |  |  |  |  |  |  | } | 
| 6695 | 13496 |  |  |  |  | 35488 | return undef;   # success! | 
| 6696 |  |  |  |  |  |  | } | 
| 6697 |  |  |  |  |  |  |  | 
| 6698 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6699 |  |  |  |  |  |  | # check new value for binary data block | 
| 6700 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) tagInfo hash ref, 2) raw value ref | 
| 6701 |  |  |  |  |  |  | # Returns: error string or undef (and may modify value) on success | 
| 6702 |  |  |  |  |  |  | sub CheckBinaryData($$$) | 
| 6703 |  |  |  |  |  |  | { | 
| 6704 | 11776 |  |  | 11776 | 0 | 24966 | my ($self, $tagInfo, $valPtr) = @_; | 
| 6705 | 11776 |  |  |  |  | 26981 | my $format = $$tagInfo{Format}; | 
| 6706 | 11776 | 100 |  |  |  | 24889 | unless ($format) { | 
| 6707 | 4426 |  |  |  |  | 7718 | my $table = $$tagInfo{Table}; | 
| 6708 | 4426 | 100 | 66 |  |  | 17820 | if ($table and $$table{FORMAT}) { | 
| 6709 | 3111 |  |  |  |  | 6943 | $format = $$table{FORMAT}; | 
| 6710 |  |  |  |  |  |  | } else { | 
| 6711 |  |  |  |  |  |  | # use default 'int8u' unless specified | 
| 6712 | 1315 |  |  |  |  | 2945 | $format = 'int8u'; | 
| 6713 |  |  |  |  |  |  | } | 
| 6714 |  |  |  |  |  |  | } | 
| 6715 | 11776 |  |  |  |  | 17603 | my $count; | 
| 6716 | 11776 | 100 |  |  |  | 40313 | if ($format =~ /(.*)\[(.*)\]/) { | 
| 6717 | 1637 |  |  |  |  | 4791 | $format = $1; | 
| 6718 | 1637 |  |  |  |  | 3076 | $count = $2; | 
| 6719 |  |  |  |  |  |  | # can't evaluate $count now because we don't know $size yet | 
| 6720 | 1637 | 50 |  |  |  | 3720 | undef $count if $count =~ /\$size/; | 
| 6721 |  |  |  |  |  |  | } | 
| 6722 | 11776 |  |  |  |  | 30517 | return CheckValue($valPtr, $format, $count); | 
| 6723 |  |  |  |  |  |  | } | 
| 6724 |  |  |  |  |  |  |  | 
| 6725 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6726 |  |  |  |  |  |  | # Rename a file (with patch for Windows Unicode file names, and other problem) | 
| 6727 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) old name, 2) new name | 
| 6728 |  |  |  |  |  |  | # Returns: true on success | 
| 6729 |  |  |  |  |  |  | sub Rename($$$) | 
| 6730 |  |  |  |  |  |  | { | 
| 6731 | 3 |  |  | 3 | 0 | 18 | my ($self, $old, $new) = @_; | 
| 6732 | 3 |  |  |  |  | 10 | my ($result, $try, $winUni); | 
| 6733 |  |  |  |  |  |  |  | 
| 6734 | 3 | 50 |  |  |  | 19 | if ($self->EncodeFileName($old)) { | 
|  |  | 50 |  |  |  |  |  | 
| 6735 | 0 |  |  |  |  | 0 | $self->EncodeFileName($new, 1); | 
| 6736 | 0 |  |  |  |  | 0 | $winUni = 1; | 
| 6737 |  |  |  |  |  |  | } elsif ($self->EncodeFileName($new)) { | 
| 6738 | 0 |  |  |  |  | 0 | $old = $_[1]; | 
| 6739 | 0 |  |  |  |  | 0 | $self->EncodeFileName($old, 1); | 
| 6740 | 0 |  |  |  |  | 0 | $winUni = 1; | 
| 6741 |  |  |  |  |  |  | } | 
| 6742 | 3 |  |  |  |  | 14 | for (;;) { | 
| 6743 | 3 | 50 |  |  |  | 19 | if ($winUni) { | 
| 6744 | 0 |  |  |  |  | 0 | $result = eval { Win32API::File::MoveFileExW($old, $new, | 
|  | 0 |  |  |  |  | 0 |  | 
| 6745 |  |  |  |  |  |  | Win32API::File::MOVEFILE_REPLACE_EXISTING() | | 
| 6746 |  |  |  |  |  |  | Win32API::File::MOVEFILE_COPY_ALLOWED()) }; | 
| 6747 |  |  |  |  |  |  | } else { | 
| 6748 | 3 |  |  |  |  | 454 | $result = rename($old, $new); | 
| 6749 |  |  |  |  |  |  | } | 
| 6750 | 3 | 50 | 33 |  |  | 28 | last if $result or $^O ne 'MSWin32'; | 
| 6751 |  |  |  |  |  |  | # keep trying for up to 0.5 seconds | 
| 6752 |  |  |  |  |  |  | # (patch for Windows denial-of-service susceptibility) | 
| 6753 | 0 |  | 0 |  |  | 0 | $try = ($try || 1) + 1; | 
| 6754 | 0 | 0 |  |  |  | 0 | last if $try > 50; | 
| 6755 | 0 |  |  |  |  | 0 | select(undef,undef,undef,0.01); # sleep for 0.01 sec | 
| 6756 |  |  |  |  |  |  | } | 
| 6757 | 3 |  |  |  |  | 24 | return $result; | 
| 6758 |  |  |  |  |  |  | } | 
| 6759 |  |  |  |  |  |  |  | 
| 6760 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6761 |  |  |  |  |  |  | # Delete a file (with patch for Windows Unicode file names) | 
| 6762 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1-N) names of files to delete | 
| 6763 |  |  |  |  |  |  | # Returns: number of files deleted | 
| 6764 |  |  |  |  |  |  | sub Unlink($@) | 
| 6765 |  |  |  |  |  |  | { | 
| 6766 | 0 |  |  | 0 | 0 | 0 | my $self = shift; | 
| 6767 | 0 |  |  |  |  | 0 | my $result = 0; | 
| 6768 | 0 |  |  |  |  | 0 | while (@_) { | 
| 6769 | 0 |  |  |  |  | 0 | my $file = shift; | 
| 6770 | 0 | 0 |  |  |  | 0 | if ($self->EncodeFileName($file)) { | 
| 6771 | 0 | 0 |  |  |  | 0 | ++$result if eval { Win32API::File::DeleteFileW($file) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 6772 |  |  |  |  |  |  | } else { | 
| 6773 | 0 | 0 |  |  |  | 0 | ++$result if unlink $file; | 
| 6774 |  |  |  |  |  |  | } | 
| 6775 |  |  |  |  |  |  | } | 
| 6776 | 0 |  |  |  |  | 0 | return $result; | 
| 6777 |  |  |  |  |  |  | } | 
| 6778 |  |  |  |  |  |  |  | 
| 6779 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6780 |  |  |  |  |  |  | # Set file times (Unix seconds since the epoch) | 
| 6781 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) file name or ref, 2) access time, 3) modification time, | 
| 6782 |  |  |  |  |  |  | #         4) inode change or creation time (or undef for any time to avoid setting) | 
| 6783 |  |  |  |  |  |  | #         5) flag to suppress warning | 
| 6784 |  |  |  |  |  |  | # Returns: 1 on success, 0 on error | 
| 6785 |  |  |  |  |  |  | my $k32SetFileTime; | 
| 6786 |  |  |  |  |  |  | sub SetFileTime($$;$$$$) | 
| 6787 |  |  |  |  |  |  | { | 
| 6788 | 0 |  |  | 0 | 0 | 0 | my ($self, $file, $atime, $mtime, $ctime, $noWarn) = @_; | 
| 6789 | 0 |  |  |  |  | 0 | my $saveFile; | 
| 6790 | 0 |  |  |  |  | 0 | local *FH; | 
| 6791 |  |  |  |  |  |  |  | 
| 6792 |  |  |  |  |  |  | # open file by name if necessary | 
| 6793 | 0 | 0 |  |  |  | 0 | unless (ref $file) { | 
| 6794 |  |  |  |  |  |  | # (file will be automatically closed when *FH goes out of scope) | 
| 6795 | 0 | 0 |  |  |  | 0 | unless ($self->Open(\*FH, $file, '+<')) { | 
| 6796 | 0 |  |  |  |  | 0 | my $success; | 
| 6797 | 0 | 0 | 0 |  |  | 0 | if (defined $atime or defined $mtime) { | 
| 6798 | 0 |  |  |  |  | 0 | my ($a, $m, $c) = $self->GetFileTime($file); | 
| 6799 | 0 | 0 |  |  |  | 0 | $atime = $a unless defined $atime; | 
| 6800 | 0 | 0 |  |  |  | 0 | $mtime = $m unless defined $mtime; | 
| 6801 | 0 | 0 | 0 |  |  | 0 | $success = eval { utime($atime, $mtime, $file) } if defined $atime and defined $mtime; | 
|  | 0 |  |  |  |  | 0 |  | 
| 6802 |  |  |  |  |  |  | } | 
| 6803 | 0 | 0 |  |  |  | 0 | $self->Warn('Error opening file for update') unless $success; | 
| 6804 | 0 |  |  |  |  | 0 | return $success; | 
| 6805 |  |  |  |  |  |  | } | 
| 6806 | 0 |  |  |  |  | 0 | $saveFile = $file; | 
| 6807 | 0 |  |  |  |  | 0 | $file = \*FH; | 
| 6808 |  |  |  |  |  |  | } | 
| 6809 |  |  |  |  |  |  | # on Windows, try to work around incorrect file times when daylight saving time is in effect | 
| 6810 | 0 | 0 |  |  |  | 0 | if ($^O eq 'MSWin32') { | 
| 6811 | 0 | 0 |  |  |  | 0 | if (not eval { require Win32::API }) { | 
|  | 0 | 0 |  |  |  | 0 |  | 
| 6812 | 0 |  |  |  |  | 0 | $self->WarnOnce('Install Win32::API for proper handling of Windows file times'); | 
| 6813 | 0 |  |  |  |  | 0 | } elsif (not eval { require Win32API::File }) { | 
| 6814 | 0 |  |  |  |  | 0 | $self->WarnOnce('Install Win32API::File for proper handling of Windows file times'); | 
| 6815 |  |  |  |  |  |  | } else { | 
| 6816 |  |  |  |  |  |  | # get Win32 handle, needed for SetFileTime | 
| 6817 | 0 |  |  |  |  | 0 | my $win32Handle = eval { Win32API::File::GetOsFHandle($file) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 6818 | 0 | 0 |  |  |  | 0 | unless ($win32Handle) { | 
| 6819 | 0 |  |  |  |  | 0 | $self->Warn('Win32API::File::GetOsFHandle returned invalid handle'); | 
| 6820 | 0 |  |  |  |  | 0 | return 0; | 
| 6821 |  |  |  |  |  |  | } | 
| 6822 |  |  |  |  |  |  | # convert Unix seconds to FILETIME structs | 
| 6823 | 0 |  |  |  |  | 0 | my $time; | 
| 6824 | 0 |  |  |  |  | 0 | foreach $time ($atime, $mtime, $ctime) { | 
| 6825 |  |  |  |  |  |  | # set to NULL if not defined (i.e. do not change) | 
| 6826 | 0 | 0 |  |  |  | 0 | defined $time or $time = 0, next; | 
| 6827 |  |  |  |  |  |  | # convert to 100 ns intervals since 0:00 UTC Jan 1, 1601 | 
| 6828 |  |  |  |  |  |  | # (89 leap years between 1601 and 1970) | 
| 6829 | 0 |  |  |  |  | 0 | my $wt = ($time + (((1970-1601)*365+89)*24*3600)) * 1e7; | 
| 6830 | 0 |  |  |  |  | 0 | my $hi = int($wt / 4294967296); | 
| 6831 | 0 |  |  |  |  | 0 | $time = pack 'LL', int($wt - $hi * 4294967296), $hi; # pack FILETIME struct | 
| 6832 |  |  |  |  |  |  | } | 
| 6833 | 0 | 0 |  |  |  | 0 | unless ($k32SetFileTime) { | 
| 6834 | 0 | 0 |  |  |  | 0 | return 0 if defined $k32SetFileTime; | 
| 6835 | 0 |  |  |  |  | 0 | $k32SetFileTime = new Win32::API('KERNEL32', 'SetFileTime', 'NPPP', 'I'); | 
| 6836 | 0 | 0 |  |  |  | 0 | unless ($k32SetFileTime) { | 
| 6837 | 0 |  |  |  |  | 0 | $self->Warn('Error calling Win32::API::SetFileTime'); | 
| 6838 | 0 |  |  |  |  | 0 | $k32SetFileTime = 0; | 
| 6839 | 0 |  |  |  |  | 0 | return 0; | 
| 6840 |  |  |  |  |  |  | } | 
| 6841 |  |  |  |  |  |  | } | 
| 6842 | 0 | 0 |  |  |  | 0 | unless ($k32SetFileTime->Call($win32Handle, $ctime, $atime, $mtime)) { | 
| 6843 | 0 |  |  |  |  | 0 | $self->Warn('Win32::API::SetFileTime returned ' . Win32::GetLastError()); | 
| 6844 | 0 |  |  |  |  | 0 | return 0; | 
| 6845 |  |  |  |  |  |  | } | 
| 6846 | 0 |  |  |  |  | 0 | return 1; | 
| 6847 |  |  |  |  |  |  | } | 
| 6848 |  |  |  |  |  |  | } | 
| 6849 |  |  |  |  |  |  | # other OS (or Windows fallback) | 
| 6850 | 0 | 0 | 0 |  |  | 0 | if (defined $atime and defined $mtime) { | 
| 6851 | 0 |  |  |  |  | 0 | my $success; | 
| 6852 | 0 |  |  |  |  | 0 | local $SIG{'__WARN__'} = \&SetWarning; # (this may not be necessary) | 
| 6853 | 0 |  |  |  |  | 0 | for (;;) { | 
| 6854 | 0 |  |  |  |  | 0 | undef $evalWarning; | 
| 6855 |  |  |  |  |  |  | # (this may fail on the first try if futimes is not implemented) | 
| 6856 | 0 |  |  |  |  | 0 | $success = eval { utime($atime, $mtime, $file) }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 6857 | 0 | 0 | 0 |  |  | 0 | last if $success or not defined $saveFile; | 
| 6858 | 0 |  |  |  |  | 0 | close $file; | 
| 6859 | 0 |  |  |  |  | 0 | $file = $saveFile; | 
| 6860 | 0 |  |  |  |  | 0 | undef $saveFile; | 
| 6861 |  |  |  |  |  |  | } | 
| 6862 | 0 | 0 |  |  |  | 0 | unless ($noWarn) { | 
| 6863 | 0 | 0 | 0 |  |  | 0 | if ($@ or $evalWarning) { | 
|  |  | 0 |  |  |  |  |  | 
| 6864 | 0 |  | 0 |  |  | 0 | $self->Warn(CleanWarning($@ || $evalWarning)); | 
| 6865 |  |  |  |  |  |  | } elsif (not $success) { | 
| 6866 | 0 |  |  |  |  | 0 | $self->Warn('Error setting file time'); | 
| 6867 |  |  |  |  |  |  | } | 
| 6868 |  |  |  |  |  |  | } | 
| 6869 | 0 |  |  |  |  | 0 | return $success; | 
| 6870 |  |  |  |  |  |  | } | 
| 6871 | 0 |  |  |  |  | 0 | return 1; # (nothing to do) | 
| 6872 |  |  |  |  |  |  | } | 
| 6873 |  |  |  |  |  |  |  | 
| 6874 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6875 |  |  |  |  |  |  | # Add data to MD5 checksum | 
| 6876 |  |  |  |  |  |  | # Inputs: 0) ExifTool ref, 1) RAF ref, 2) data size (or undef to read to end of file), | 
| 6877 |  |  |  |  |  |  | #         3) data name (or undef for no warnings or messages), 4) flag for no verbose message | 
| 6878 |  |  |  |  |  |  | # Returns: number of bytes read and MD5'd | 
| 6879 |  |  |  |  |  |  | sub ImageDataMD5($$$;$$) | 
| 6880 |  |  |  |  |  |  | { | 
| 6881 | 1 |  |  | 1 | 0 | 6 | my ($self, $raf, $size, $type, $noMsg) = @_; | 
| 6882 | 1 | 50 |  |  |  | 6 | my $md5 = $$self{ImageDataMD5} or return; | 
| 6883 | 0 |  |  |  |  | 0 | my ($bytesRead, $n) = (0, 65536); | 
| 6884 | 0 |  |  |  |  | 0 | my $buff; | 
| 6885 | 0 |  |  |  |  | 0 | for (;;) { | 
| 6886 | 0 | 0 |  |  |  | 0 | if (defined $size) { | 
| 6887 | 0 | 0 |  |  |  | 0 | last unless $size; | 
| 6888 | 0 | 0 |  |  |  | 0 | $n = $size > 65536 ? 65536 : $size; | 
| 6889 | 0 |  |  |  |  | 0 | $size -= $n; | 
| 6890 |  |  |  |  |  |  | } | 
| 6891 | 0 | 0 |  |  |  | 0 | unless ($raf->Read($buff, $n)) { | 
| 6892 | 0 | 0 | 0 |  |  | 0 | $self->Warn("Error reading $type data") if $type and defined $size; | 
| 6893 | 0 |  |  |  |  | 0 | last; | 
| 6894 |  |  |  |  |  |  | } | 
| 6895 | 0 |  |  |  |  | 0 | $md5->add($buff); | 
| 6896 | 0 |  |  |  |  | 0 | $bytesRead += length $buff; | 
| 6897 |  |  |  |  |  |  | } | 
| 6898 | 0 | 0 | 0 |  |  | 0 | if ($$self{OPTIONS}{Verbose} and $bytesRead and $type and not $noMsg) { | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 6899 | 0 |  |  |  |  | 0 | $self->VPrint(0, "$$self{INDENT}(ImageDataMD5: $bytesRead bytes of $type data)\n"); | 
| 6900 |  |  |  |  |  |  | } | 
| 6901 | 0 |  |  |  |  | 0 | return $bytesRead; | 
| 6902 |  |  |  |  |  |  | } | 
| 6903 |  |  |  |  |  |  |  | 
| 6904 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6905 |  |  |  |  |  |  | # Copy data block from RAF to output file in max 64kB chunks | 
| 6906 |  |  |  |  |  |  | # Inputs: 0) RAF ref, 1) outfile ref, 2) block size | 
| 6907 |  |  |  |  |  |  | # Returns: 1 on success, 0 on read error, undef on write error | 
| 6908 |  |  |  |  |  |  | sub CopyBlock($$$) | 
| 6909 |  |  |  |  |  |  | { | 
| 6910 | 69 |  |  | 69 | 0 | 248 | my ($raf, $outfile, $size) = @_; | 
| 6911 | 69 |  |  |  |  | 137 | my $buff; | 
| 6912 | 69 |  |  |  |  | 139 | for (;;) { | 
| 6913 | 122 | 100 |  |  |  | 396 | last unless $size > 0; | 
| 6914 | 53 | 50 |  |  |  | 173 | my $n = $size > 65536 ? 65536 : $size; | 
| 6915 | 53 | 50 |  |  |  | 211 | $raf->Read($buff, $n) == $n or return 0; | 
| 6916 | 53 | 50 |  |  |  | 285 | Write($outfile, $buff) or return undef; | 
| 6917 | 53 |  |  |  |  | 195 | $size -= $n; | 
| 6918 |  |  |  |  |  |  | } | 
| 6919 | 69 |  |  |  |  | 249 | return 1; | 
| 6920 |  |  |  |  |  |  | } | 
| 6921 |  |  |  |  |  |  |  | 
| 6922 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6923 |  |  |  |  |  |  | # Copy image data from one file to another | 
| 6924 |  |  |  |  |  |  | # Inputs: 0) ExifTool object reference | 
| 6925 |  |  |  |  |  |  | #         1) reference to list of image data [ position, size, pad bytes ] | 
| 6926 |  |  |  |  |  |  | #         2) output file ref | 
| 6927 |  |  |  |  |  |  | # Returns: true on success | 
| 6928 |  |  |  |  |  |  | sub CopyImageData($$$) | 
| 6929 |  |  |  |  |  |  | { | 
| 6930 | 13 |  |  | 13 | 0 | 59 | my ($self, $imageDataBlocks, $outfile) = @_; | 
| 6931 | 13 |  |  |  |  | 45 | my $raf = $$self{RAF}; | 
| 6932 | 13 |  |  |  |  | 31 | my ($dataBlock, $err); | 
| 6933 | 13 |  |  |  |  | 38 | my $num = @$imageDataBlocks; | 
| 6934 | 13 | 50 |  |  |  | 151 | $self->VPrint(0, "  Copying $num image data blocks\n") if $num; | 
| 6935 | 13 |  |  |  |  | 49 | foreach $dataBlock (@$imageDataBlocks) { | 
| 6936 | 24 |  |  |  |  | 71 | my ($pos, $size, $pad) = @$dataBlock; | 
| 6937 | 24 | 50 |  |  |  | 99 | $raf->Seek($pos, 0) or $err = 'read', last; | 
| 6938 | 24 |  |  |  |  | 136 | my $result = CopyBlock($raf, $outfile, $size); | 
| 6939 | 24 | 0 |  |  |  | 86 | $result or $err = defined $result ? 'read' : 'writ'; | 
|  |  | 50 |  |  |  |  |  | 
| 6940 |  |  |  |  |  |  | # pad if necessary | 
| 6941 | 24 | 100 | 50 |  |  | 80 | Write($outfile, "\0" x $pad) or $err = 'writ' if $pad; | 
| 6942 | 24 | 50 |  |  |  | 89 | last if $err; | 
| 6943 |  |  |  |  |  |  | } | 
| 6944 | 13 | 50 |  |  |  | 72 | if ($err) { | 
| 6945 | 0 |  |  |  |  | 0 | $self->Error("Error ${err}ing image data"); | 
| 6946 | 0 |  |  |  |  | 0 | return 0; | 
| 6947 |  |  |  |  |  |  | } | 
| 6948 | 13 |  |  |  |  | 60 | return 1; | 
| 6949 |  |  |  |  |  |  | } | 
| 6950 |  |  |  |  |  |  |  | 
| 6951 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 6952 |  |  |  |  |  |  | # Write to binary data block | 
| 6953 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) source dirInfo ref, 2) tag table ref | 
| 6954 |  |  |  |  |  |  | # Returns: Binary data block or undefined on error | 
| 6955 |  |  |  |  |  |  | sub WriteBinaryData($$$) | 
| 6956 |  |  |  |  |  |  | { | 
| 6957 | 15010 |  |  | 15010 | 0 | 29670 | my ($self, $dirInfo, $tagTablePtr) = @_; | 
| 6958 | 15010 | 100 |  |  |  | 51294 | $self or return 1;    # allow dummy access to autoload this package | 
| 6959 |  |  |  |  |  |  |  | 
| 6960 |  |  |  |  |  |  | # get default format ('int8u' unless specified) | 
| 6961 | 458 | 50 |  |  |  | 1447 | my $dataPt = $$dirInfo{DataPt} or return undef; | 
| 6962 | 458 |  |  |  |  | 984 | my $dataLen = length $$dataPt; | 
| 6963 | 458 |  | 100 |  |  | 2045 | my $defaultFormat = $$tagTablePtr{FORMAT} || 'int8u'; | 
| 6964 | 458 |  |  |  |  | 1386 | my $increment = FormatSize($defaultFormat); | 
| 6965 | 458 | 50 |  |  |  | 1233 | unless ($increment) { | 
| 6966 | 0 |  |  |  |  | 0 | warn "Unknown format $defaultFormat\n"; | 
| 6967 | 0 |  |  |  |  | 0 | return undef; | 
| 6968 |  |  |  |  |  |  | } | 
| 6969 |  |  |  |  |  |  | # extract data members first if necessary | 
| 6970 | 458 |  |  |  |  | 839 | my @varOffsets; | 
| 6971 | 458 | 100 |  |  |  | 1845 | if ($$tagTablePtr{DATAMEMBER}) { | 
| 6972 | 195 |  |  |  |  | 526 | $$dirInfo{DataMember} = $$tagTablePtr{DATAMEMBER}; | 
| 6973 | 195 |  |  |  |  | 565 | $$dirInfo{VarFormatData} = \@varOffsets; | 
| 6974 | 195 |  |  |  |  | 897 | $self->ProcessBinaryData($dirInfo, $tagTablePtr); | 
| 6975 | 195 |  |  |  |  | 516 | delete $$dirInfo{DataMember}; | 
| 6976 | 195 |  |  |  |  | 393 | delete $$dirInfo{VarFormatData}; | 
| 6977 |  |  |  |  |  |  | } | 
| 6978 | 458 |  | 100 |  |  | 1536 | my $dirStart = $$dirInfo{DirStart} || 0; | 
| 6979 | 458 |  |  |  |  | 840 | my $dirLen = $$dirInfo{DirLen}; | 
| 6980 | 458 | 100 | 66 |  |  | 2074 | $dirLen = $dataLen - $dirStart if not defined $dirLen or $dirLen > $dataLen - $dirStart; | 
| 6981 | 458 | 50 |  |  |  | 1697 | my $newData = substr($$dataPt, $dirStart, $dirLen) or return undef; | 
| 6982 | 458 |  |  |  |  | 920 | my $dirName = $$dirInfo{DirName}; | 
| 6983 | 458 |  |  |  |  | 816 | my $varSize = 0; | 
| 6984 | 458 |  |  |  |  | 961 | my @varInfo = @varOffsets; | 
| 6985 | 458 |  |  |  |  | 648 | my $tagInfo; | 
| 6986 | 458 |  |  |  |  | 951 | $dataPt = \$newData; | 
| 6987 | 458 |  |  |  |  | 1565 | foreach $tagInfo (sort { $$a{TagID} <=> $$b{TagID} } $self->GetNewTagInfoList($tagTablePtr)) { | 
|  | 650 |  |  |  |  | 1160 |  | 
| 6988 | 227 |  |  |  |  | 550 | my $tagID = $$tagInfo{TagID}; | 
| 6989 |  |  |  |  |  |  | # evaluate conditional tags now if necessary | 
| 6990 | 227 | 100 | 100 |  |  | 1194 | if (ref $$tagTablePtr{$tagID} eq 'ARRAY' or $$tagInfo{Condition}) { | 
| 6991 | 22 |  |  |  |  | 89 | my $writeInfo = $self->GetTagInfo($tagTablePtr, $tagID); | 
| 6992 | 22 | 100 | 100 |  |  | 177 | next unless $writeInfo and $writeInfo eq $tagInfo; | 
| 6993 |  |  |  |  |  |  | } | 
| 6994 |  |  |  |  |  |  | # add offsets for variable-sized tags if necessary | 
| 6995 | 218 |  | 100 |  |  | 680 | while (@varInfo and $varInfo[0][0] < $tagID) { | 
| 6996 | 10 |  |  |  |  | 23 | $varSize = $varInfo[0][1];  # get accumulated variable size | 
| 6997 | 10 |  |  |  |  | 30 | shift @varInfo; | 
| 6998 |  |  |  |  |  |  | } | 
| 6999 | 218 |  |  |  |  | 371 | my $count = 1; | 
| 7000 | 218 |  |  |  |  | 473 | my $format = $$tagInfo{Format}; | 
| 7001 | 218 |  |  |  |  | 478 | my $entry = int($tagID) * $increment + $varSize; # relative offset of this entry | 
| 7002 | 218 | 100 |  |  |  | 512 | if ($format) { | 
| 7003 | 87 | 100 |  |  |  | 387 | if ($format =~ /(.*)\[(.*)\]/) { | 
|  |  | 100 |  |  |  |  |  | 
| 7004 | 36 |  |  |  |  | 105 | $format = $1; | 
| 7005 | 36 |  |  |  |  | 77 | $count = $2; | 
| 7006 | 36 |  |  |  |  | 56 | my $size = $dirLen; # used in eval | 
| 7007 |  |  |  |  |  |  | # evaluate count to allow count to be based on previous values | 
| 7008 |  |  |  |  |  |  | #### eval Format size ($size, $self) - NOTE: %val not supported for writing | 
| 7009 | 36 |  |  |  |  | 1716 | $count = eval $count; | 
| 7010 | 36 | 50 |  |  |  | 171 | $@ and warn($@), next; | 
| 7011 |  |  |  |  |  |  | } elsif ($format eq 'string') { | 
| 7012 |  |  |  |  |  |  | # string with no specified count runs to end of block | 
| 7013 | 1 | 50 |  |  |  | 5 | $count = ($dirLen > $entry) ? $dirLen - $entry : 0; | 
| 7014 |  |  |  |  |  |  | } | 
| 7015 |  |  |  |  |  |  | } else { | 
| 7016 | 131 |  |  |  |  | 246 | $format = $defaultFormat; | 
| 7017 |  |  |  |  |  |  | } | 
| 7018 |  |  |  |  |  |  | # read/write using variable format if changed in Hook | 
| 7019 | 218 | 100 | 66 |  |  | 557 | $format = $varInfo[0][2] if @varInfo and $varInfo[0][0] == $tagID; | 
| 7020 | 218 |  |  |  |  | 747 | my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen-$entry); | 
| 7021 | 218 | 100 |  |  |  | 575 | next unless defined $val; | 
| 7022 | 215 |  |  |  |  | 946 | my $nvHash = $self->GetNewValueHash($tagInfo, $$self{CUR_WRITE_GROUP}); | 
| 7023 | 215 | 100 |  |  |  | 710 | next unless $self->IsOverwriting($nvHash, $val) > 0; | 
| 7024 | 214 |  |  |  |  | 578 | my $newVal = $self->GetNewValue($nvHash); | 
| 7025 | 214 | 100 |  |  |  | 514 | next unless defined $newVal;    # can't delete from a binary table | 
| 7026 |  |  |  |  |  |  | # update DataMember with new value if necessary | 
| 7027 | 213 | 100 |  |  |  | 571 | $$self{$$tagInfo{DataMember}} = $newVal if $$tagInfo{DataMember}; | 
| 7028 |  |  |  |  |  |  | # only write masked bits if specified | 
| 7029 | 213 |  |  |  |  | 445 | my $mask = $$tagInfo{Mask}; | 
| 7030 | 213 | 100 |  |  |  | 458 | $newVal = (($newVal << $$tagInfo{BitShift}) & $mask) | ($val & ~$mask) if $mask; | 
| 7031 |  |  |  |  |  |  | # set the size | 
| 7032 | 213 | 50 | 33 |  |  | 643 | if ($$tagInfo{DataTag} and not $$tagInfo{IsOffset}) { | 
| 7033 | 0 | 0 |  |  |  | 0 | warn 'Internal error' unless $newVal == 0xfeedfeed; | 
| 7034 | 0 |  |  |  |  | 0 | my $data = $self->GetNewValue($$tagInfo{DataTag}); | 
| 7035 | 0 | 0 |  |  |  | 0 | $newVal = length($data) if defined $data; | 
| 7036 | 0 |  | 0 |  |  | 0 | my $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT} || 'int32u'; | 
| 7037 | 0 | 0 | 0 |  |  | 0 | if ($format =~ /^int16/ and $newVal > 0xffff) { | 
| 7038 | 0 |  |  |  |  | 0 | $self->Error("$$tagInfo{DataTag} is too large (64 kB max. for this file)"); | 
| 7039 |  |  |  |  |  |  | } | 
| 7040 |  |  |  |  |  |  | } | 
| 7041 | 213 |  |  |  |  | 553 | my $rtnVal = WriteValue($newVal, $format, $count, $dataPt, $entry); | 
| 7042 | 213 | 50 |  |  |  | 552 | if (defined $rtnVal) { | 
| 7043 | 213 |  |  |  |  | 1185 | $self->VerboseValue("- $dirName:$$tagInfo{Name}", $val); | 
| 7044 | 213 |  |  |  |  | 724 | $self->VerboseValue("+ $dirName:$$tagInfo{Name}", $newVal); | 
| 7045 | 213 |  |  |  |  | 641 | ++$$self{CHANGED}; | 
| 7046 |  |  |  |  |  |  | } | 
| 7047 |  |  |  |  |  |  | } | 
| 7048 |  |  |  |  |  |  | # add necessary fixups for any offsets | 
| 7049 | 458 | 50 | 66 |  |  | 1593 | if ($$tagTablePtr{IS_OFFSET} and $$dirInfo{Fixup}) { | 
| 7050 | 1 |  |  |  |  | 2 | $varSize = 0; | 
| 7051 | 1 |  |  |  |  | 3 | @varInfo = @varOffsets; | 
| 7052 | 1 |  |  |  |  | 3 | my $fixup = $$dirInfo{Fixup}; | 
| 7053 | 1 |  |  |  |  | 2 | my $tagID; | 
| 7054 | 1 |  |  |  |  | 2 | foreach $tagID (@{$$tagTablePtr{IS_OFFSET}}) { | 
|  | 1 |  |  |  |  | 6 |  | 
| 7055 | 1 | 50 |  |  |  | 5 | $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID) or next; | 
| 7056 | 1 |  | 33 |  |  | 5 | while (@varInfo and $varInfo[0][0] < $tagID) { | 
| 7057 | 0 |  |  |  |  | 0 | $varSize = $varInfo[0][1]; | 
| 7058 | 0 |  |  |  |  | 0 | shift @varInfo; | 
| 7059 |  |  |  |  |  |  | } | 
| 7060 | 1 |  |  |  |  | 2 | my $entry = $tagID * $increment + $varSize; # (no offset to dirStart for new dir data) | 
| 7061 | 1 | 50 |  |  |  | 4 | next unless $entry <= $dirLen - 4; | 
| 7062 |  |  |  |  |  |  | # (Ricoh has 16-bit preview image offsets, so can't just assume int32u) | 
| 7063 | 0 |  | 0 |  |  | 0 | my $format = $$tagInfo{Format} || $$tagTablePtr{FORMAT} || 'int32u'; | 
| 7064 | 0 |  |  |  |  | 0 | my $offset = ReadValue($dataPt, $entry, $format, 1, $dirLen-$entry); | 
| 7065 |  |  |  |  |  |  | # ignore if offset is zero (eg. Ricoh DNG uses this to indicate no preview) | 
| 7066 | 0 | 0 |  |  |  | 0 | next unless $offset; | 
| 7067 | 0 |  |  |  |  | 0 | $fixup->AddFixup($entry, $$tagInfo{DataTag}, $format); | 
| 7068 |  |  |  |  |  |  | # handle the preview image now if this is a JPEG file | 
| 7069 |  |  |  |  |  |  | next unless $$self{FILE_TYPE} eq 'JPEG' and $$tagInfo{DataTag} and | 
| 7070 | 0 | 0 | 0 |  |  | 0 | $$tagInfo{DataTag} eq 'PreviewImage' and defined $$tagInfo{OffsetPair}; | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 7071 |  |  |  |  |  |  | # NOTE: here we assume there are no var-sized tags between the | 
| 7072 |  |  |  |  |  |  | # OffsetPair tags.  If this ever becomes possible we must recalculate | 
| 7073 |  |  |  |  |  |  | # $varSize for the OffsetPair tag here! | 
| 7074 | 0 |  |  |  |  | 0 | $entry = $$tagInfo{OffsetPair} * $increment + $varSize; | 
| 7075 | 0 |  |  |  |  | 0 | my $size = ReadValue($dataPt, $entry, $format, 1, $dirLen-$entry); | 
| 7076 | 0 |  |  |  |  | 0 | my $previewInfo = $$self{PREVIEW_INFO}; | 
| 7077 |  |  |  |  |  |  | $previewInfo or $previewInfo = $$self{PREVIEW_INFO} = { | 
| 7078 | 0 | 0 |  |  |  | 0 | Fixup => new Image::ExifTool::Fixup, | 
| 7079 |  |  |  |  |  |  | }; | 
| 7080 |  |  |  |  |  |  | # set flag indicating we are using short pointers | 
| 7081 | 0 | 0 |  |  |  | 0 | $$previewInfo{IsShort} = 1 unless $format eq 'int32u'; | 
| 7082 | 0 | 0 | 0 |  |  | 0 | $$previewInfo{Absolute} = 1 if $$tagInfo{IsOffset} and $$tagInfo{IsOffset} eq '3'; | 
| 7083 |  |  |  |  |  |  | # get the value of the Composite::PreviewImage tag | 
| 7084 | 0 |  |  |  |  | 0 | $$previewInfo{Data} = $self->GetNewValue(GetCompositeTagInfo('PreviewImage')); | 
| 7085 | 0 | 0 |  |  |  | 0 | unless (defined $$previewInfo{Data}) { | 
| 7086 | 0 | 0 | 0 |  |  | 0 | if ($offset >= 0 and $offset + $size <= $$dirInfo{DataLen}) { | 
| 7087 | 0 |  |  |  |  | 0 | $$previewInfo{Data} = substr(${$$dirInfo{DataPt}},$offset,$size); | 
|  | 0 |  |  |  |  | 0 |  | 
| 7088 |  |  |  |  |  |  | } else { | 
| 7089 | 0 |  |  |  |  | 0 | $$previewInfo{Data} = 'LOAD_PREVIEW'; # flag to load preview later | 
| 7090 |  |  |  |  |  |  | } | 
| 7091 |  |  |  |  |  |  | } | 
| 7092 |  |  |  |  |  |  | } | 
| 7093 |  |  |  |  |  |  | } | 
| 7094 |  |  |  |  |  |  | # write any necessary SubDirectories | 
| 7095 | 458 | 100 |  |  |  | 1203 | if ($$tagTablePtr{IS_SUBDIR}) { | 
| 7096 | 12 |  |  |  |  | 73 | $varSize = 0; | 
| 7097 | 12 |  |  |  |  | 44 | @varInfo = @varOffsets; | 
| 7098 | 12 |  |  |  |  | 44 | my $tagID; | 
| 7099 | 12 |  |  |  |  | 37 | foreach $tagID (@{$$tagTablePtr{IS_SUBDIR}}) { | 
|  | 12 |  |  |  |  | 55 |  | 
| 7100 | 13 |  |  |  |  | 63 | my $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID); | 
| 7101 | 13 | 100 |  |  |  | 70 | next unless defined $tagInfo; | 
| 7102 | 4 |  | 33 |  |  | 26 | while (@varInfo and $varInfo[0][0] < $tagID) { | 
| 7103 | 0 |  |  |  |  | 0 | $varSize = $varInfo[0][1]; | 
| 7104 | 0 |  |  |  |  | 0 | shift @varInfo; | 
| 7105 |  |  |  |  |  |  | } | 
| 7106 | 4 |  |  |  |  | 18 | my $entry = int($tagID) * $increment + $varSize; | 
| 7107 | 4 | 50 |  |  |  | 16 | last if $entry >= $dirLen; | 
| 7108 |  |  |  |  |  |  | # get value for Condition if necessary | 
| 7109 | 4 | 50 |  |  |  | 19 | unless ($tagInfo) { | 
| 7110 | 0 |  |  |  |  | 0 | my $more = $dirLen - $entry; | 
| 7111 | 0 | 0 |  |  |  | 0 | $more = 128 if $more > 128; | 
| 7112 | 0 |  |  |  |  | 0 | my $v = substr($newData, $entry, $more); | 
| 7113 | 0 |  |  |  |  | 0 | $tagInfo = $self->GetTagInfo($tagTablePtr, $tagID, \$v); | 
| 7114 | 0 | 0 |  |  |  | 0 | next unless $tagInfo; | 
| 7115 |  |  |  |  |  |  | } | 
| 7116 | 4 | 50 |  |  |  | 18 | my $subdir = $$tagInfo{SubDirectory} or next; | 
| 7117 | 4 |  |  |  |  | 10 | my $start = $$subdir{Start}; | 
| 7118 | 4 |  |  |  |  | 8 | my $len; | 
| 7119 | 4 | 50 |  |  |  | 15 | if (not $start) { | 
|  |  | 0 |  |  |  |  |  | 
| 7120 | 4 |  |  |  |  | 9 | $start = $entry; | 
| 7121 | 4 |  |  |  |  | 13 | $len = $dirLen - $start; | 
| 7122 |  |  |  |  |  |  | } elsif ($start =~ /\$/) { | 
| 7123 | 0 |  |  |  |  | 0 | my $count = 1; | 
| 7124 | 0 |  | 0 |  |  | 0 | my $format = $$tagInfo{Format} || $defaultFormat; | 
| 7125 | 0 | 0 |  |  |  | 0 | $format =~ /(.*)\[(.*)\]/ and ($format, $count) = ($1, $2); | 
| 7126 | 0 |  |  |  |  | 0 | my $val = ReadValue($dataPt, $entry, $format, $count, $dirLen - $entry); | 
| 7127 |  |  |  |  |  |  | # ignore directories with a zero offset (ie. missing Nikon ShotInfo entries) | 
| 7128 | 0 | 0 |  |  |  | 0 | next unless $val; | 
| 7129 | 0 |  |  |  |  | 0 | my $dirStart = 0; | 
| 7130 |  |  |  |  |  |  | #### eval Start ($val, $dirStart) | 
| 7131 | 0 |  |  |  |  | 0 | $start = eval($start); | 
| 7132 | 0 | 0 | 0 |  |  | 0 | next if $start < $dirStart or $start > $dataLen; | 
| 7133 | 0 |  |  |  |  | 0 | $len = $$subdir{DirLen}; | 
| 7134 | 0 | 0 | 0 |  |  | 0 | $len = $dataLen - $start unless $len and $len <= $dataLen - $start; | 
| 7135 |  |  |  |  |  |  | } | 
| 7136 | 4 |  |  |  |  | 26 | my %subdirInfo = ( | 
| 7137 |  |  |  |  |  |  | DataPt   => \$newData, | 
| 7138 |  |  |  |  |  |  | DirStart => $start, | 
| 7139 |  |  |  |  |  |  | DirLen   => $len, | 
| 7140 |  |  |  |  |  |  | TagInfo  => $tagInfo, | 
| 7141 |  |  |  |  |  |  | ); | 
| 7142 | 4 |  |  |  |  | 25 | my $dat = $self->WriteDirectory(\%subdirInfo, GetTagTable($$subdir{TagTable})); | 
| 7143 | 4 | 50 | 33 |  |  | 68 | substr($newData, $start, $len) = $dat if defined $dat and length $dat; | 
| 7144 |  |  |  |  |  |  | } | 
| 7145 |  |  |  |  |  |  | } | 
| 7146 | 458 |  |  |  |  | 1760 | return $newData; | 
| 7147 |  |  |  |  |  |  | } | 
| 7148 |  |  |  |  |  |  |  | 
| 7149 |  |  |  |  |  |  | #------------------------------------------------------------------------------ | 
| 7150 |  |  |  |  |  |  | # Write TIFF as a directory | 
| 7151 |  |  |  |  |  |  | # Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref | 
| 7152 |  |  |  |  |  |  | # Returns: New directory data or undefined on error | 
| 7153 |  |  |  |  |  |  | sub WriteTIFF($$$) | 
| 7154 |  |  |  |  |  |  | { | 
| 7155 | 111 |  |  | 111 | 0 | 435 | my ($self, $dirInfo, $tagTablePtr) = @_; | 
| 7156 | 111 | 50 |  |  |  | 985 | $self or return 1;    # allow dummy access | 
| 7157 | 111 |  |  |  |  | 369 | my $buff = ''; | 
| 7158 | 111 |  |  |  |  | 415 | $$dirInfo{OutFile} = \$buff; | 
| 7159 | 111 | 50 |  |  |  | 728 | return $buff if $self->ProcessTIFF($dirInfo, $tagTablePtr) > 0; | 
| 7160 | 0 |  |  |  |  |  | return undef; | 
| 7161 |  |  |  |  |  |  | } | 
| 7162 |  |  |  |  |  |  |  | 
| 7163 |  |  |  |  |  |  | 1; # end | 
| 7164 |  |  |  |  |  |  |  | 
| 7165 |  |  |  |  |  |  | __END__ |