exif.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046 package require Tcl 8.3
00047
00048 package provide exif 1.1.2 ;
00049
00050 namespace ::exif {
00051 namespace export analyze analyzeFile fieldnames
00052 variable debug 0 ;
00053 variable cameraModel ;
00054 variable jpeg_markers ;
00055 variable intel ;
00056 variable cached_fieldnames ;
00057 array jpeg = _markers {
00058 SOF0 \xC0
00059 DHT \xC4
00060 SOI \xD8
00061 EOI \xD9
00062 SOS \xDA
00063 DQT \xDB
00064 DRI \xDD
00065 APP1 \xE1
00066 }
00067 }
00068
00069 ret ::exif::debug (type str) {
00070 variable debug
00071 if {$debug} {puts $str}
00072 }
00073
00074 ret ::exif::streq (type s1 , type s2) {
00075 return [string equal $s1 $s2]
00076 }
00077
00078 ret ::exif::analyzeFile (type file , optional thumbnail ={)} {
00079 set stream [open $file]
00080 set res [analyze $stream $thumbnail]
00081 close $stream
00082 return $res
00083 }
00084
00085 proc ::exif::analyze {stream {thumbnail {}}} {
00086 variable jpeg_markers
00087 array result = {}
00088 fconfigure $stream -translation binary -encoding binary
00089 while {![eof $stream]} {
00090 ch = [read $stream 1]
00091 if {1 != [string length $ch]} {error "End of file reached @1"}
00092 if {![streq "\xFF" $ch]} {break} ;
00093 marker = [read $stream 1]
00094 if {1 != [string length $marker]} {error "End of file reached @2"}
00095 if {[streq $marker $jpeg_markers(SOI)]} {
00096 debug "SOI"
00097 } elseif {[streq $marker $jpeg_markers(EOI)]} {
00098 debug "EOI"
00099 } else {
00100 msb = [read $stream 1]
00101 lsb = [read $stream 1]
00102 if {1 != [string length $msb] || 1 != [string length $lsb]} {
00103 error "File truncated @1"
00104 }
00105 scan $msb %c msb ; scan $lsb %c lsb
00106 size = [expr {256 * $msb + $lsb}]
00107 data = [read $stream [expr {$size-2}]]
00108 debug "read [expr {$size - 2}] bytes of data"
00109 if {[expr {$size-2}] != [string length $data]} {
00110 error "File truncated @2"
00111 }
00112 if {[streq $marker $jpeg_markers(APP1)]} {
00113 debug "APP1\t$size"
00114 array result = [app1 $data $thumbnail]
00115 } elseif {[streq $marker $jpeg_markers(DQT)]} {
00116 debug "DQT\t$size"
00117 } elseif {[streq $marker $jpeg_markers(SOF0)]} {
00118 debug "SOF0\t$size"
00119 } elseif {[streq $marker $jpeg_markers(DHT)]} {
00120 debug "DHT\t$size"
00121 } elseif {[streq $marker $jpeg_markers(SOS)]} {
00122 debug "SOS\t$size"
00123 } else {
00124 binary scan $marker H* x
00125 debug "UNKNOWN MARKER $x"
00126 }
00127 }
00128 }
00129 return [array get result]
00130 }
00131
00132 ret ::exif::app1 (type data , type thumbnail) {
00133 variable intel
00134 variable cameraModel
00135 array set result {}
00136 if {![string equal [string range $data 0 5] "Exif\0\0"]} {
00137 error "APP1 does not contain EXIF"
00138 }
00139 debug "Reading EXIF data"
00140 set data [string range $data 6 end]
00141 set t [string range $data 0 1]
00142 if {[streq $t "II"]} {
00143 set intel 1
00144 debug "Intel byte alignment"
00145 } elseif {[streq $t "MM"]} {
00146 set intel 0
00147 debug "Motorola byte alignment"
00148 } else {
00149 error "Invalid byte alignment: $t"
00150 }
00151 if {[readShort $data 2]!=0x002A} {error "Invalid tag mark"}
00152 set curoffset [readLong $data 4] ; # just called "offset" in the Perl - DNew
00153 debug "Offset to first IFD: $curoffset"
00154 set numEntries [readShort $data $curoffset]
00155 incr curoffset 2
00156 debug "Number of directory entries: $numEntries"
00157 for {set i 0} {$i < $numEntries} {incr i} {
00158 set head [expr {$curoffset + 12 * $i}]
00159 set entry [string range $data $head [expr {$head+11}]]
00160 set tag [readShort $entry 0]
00161 set format [readShort $entry 2]
00162 set components [readLong $entry 4]
00163 set offset [readLong $entry 8]
00164 set value [readIFDEntry $data $format $components $offset]
00165 if {$tag==0x010e} {
00166 set result(ImageDescription) $value
00167 } elseif {$tag==0x010f} {
00168 set result(CameraMake) $value
00169 } elseif {$tag==0x0110} {
00170 set result(CameraModel) $value
00171 set cameraModel $value
00172 } elseif {$tag==0x0112} {
00173 set result(Orientation) $value
00174 } elseif {$tag == 0x011A} {
00175 set result(XResolution) $value
00176 } elseif {$tag == 0x011B} {
00177 set result(YResolution) $value
00178 } elseif {$tag == 0x0128} {
00179 set result(ResolutionUnit) "unknown"
00180 if {$value==2} {set result(ResolutionUnit) "inch"}
00181 if {$value==3} {set result(ResolutionUnit) "centimeter"}
00182 } elseif {$tag==0x0131} {
00183 set result(Software) $value
00184 } elseif {$tag==0x0132} {
00185 set result(DateTime) $value
00186 } elseif {$tag==0x0213} {
00187 set result(YCbCrPositioning) "unknown"
00188 if {$value==1} {set result(YCbCrPositioning) "Center of pixel array"}
00189 if {$value==2} {set result(YCbCrPositioning) "Datum point"}
00190 } elseif {$tag==0x8769} {
00191 # EXIF sub IFD
00192 debug "==CALLING exifSubIFD=="
00193 array set result [exifSubIFD $data $offset]
00194 } else {
00195 debug "Unrecognized entry: Tag=$tag, value=$value"
00196 }
00197 }
00198 set offset [readLong $data [expr {$curoffset + 12 * $numEntries}]]
00199 debug "Offset to next IFD: $offset"
00200 array set thumb_result [exifSubIFD $data $offset]
00201
00202 if {$thumbnail != {}} {
00203 set jpg [string range $data \
00204 $thumb_result(JpegIFOffset) \
00205 [expr {$thumb_result(JpegIFOffset) + $thumb_result(JpegIFByteCount) - 1}]]
00206
00207 set to [open $thumbnail w]
00208 fconfigure $to -translation binary -encoding binary
00209 puts $to $jpg
00210 close $to
00211
00212 #can be used (with a JPG-aware TK) to add the image to the result array
00213 #set result(THUMB) [image create photo -file $thumbnail]
00214 }
00215
00216 return [array get result]
00217 }
00218
00219
00220 ret ::exif::exifSubIFD (type data , type curoffset) {
00221 debug "EXIF: offset=$curoffset"
00222 set numEntries [readShort $data $curoffset]
00223 incr curoffset 2
00224 debug "Number of directory entries: $numEntries"
00225 for {set i 0} {$i < $numEntries} {incr i} {
00226 set head [expr {$curoffset + 12 * $i}]
00227 set entry [string range $data $head [expr {$head+11}]]
00228 set tag [readShort $entry 0]
00229 set format [readShort $entry 2]
00230 set components [readLong $entry 4]
00231 set offset [readLong $entry 8]
00232 if {$tag==0x9000} {
00233 set result(ExifVersion) [string range $entry 8 11]
00234 } elseif {$tag==0x9101} {
00235 set result(ComponentsConfigured) [format 0x%08x $offset]
00236 } elseif {$tag == 0x927C} {
00237 array set result [makerNote $data $offset]
00238 } elseif {$tag == 0x9286} {
00239 # Apparently, this doesn't usually work.
00240 set result(UserComment) "$offset - [string range $data $offset [expr {$offset+8}]]"
00241 set result(UserComment) [string trim $result(UserComment) "\0"]
00242 } elseif {$tag==0xA000} {
00243 set result(FlashPixVersion) [string range $entry 8 11]
00244 } elseif {$tag==0xA300} {
00245 # 3 means digital camera
00246 if {$offset == 3} {
00247 set result(FileSource) "3 - Digital camera"
00248 } else {
00249 set result(FileSource) $offset
00250 }
00251 } else {
00252 set value [readIFDEntry $data $format $components $offset]
00253 if {$tag==0x829A} {
00254 if {0.3 <= $value} {
00255 # In seconds...
00256 set result(ExposureTime) "$value seconds"
00257 } else {
00258 set result(ExposureTime) "1/[expr {1.0/$value}] seconds"
00259 }
00260 } elseif {$tag == 0x829D} {
00261 set result(FNumber) $value
00262 } elseif {$tag == 0x8827} {
00263 # D30 stores ISO here, G1 uses MakerNote Tag 1 field 16
00264 set result(ISOSpeedRatings) $value
00265 } elseif {$tag == 0x9003} {
00266 set result(DateTimeOriginal) $value
00267 } elseif {$tag == 0x9004} {
00268 set result(DateTimeDigitized) $value
00269 } elseif {$tag == 0x9102} {
00270 if {$value == 5} {
00271 set result(ImageQuality) "super fine"
00272 } elseif {$value == 3} {
00273 set result(ImageQuality) "fine"
00274 } elseif {$value == 2} {
00275 set result(ImageQuality) "normal"
00276 } else {
00277 set result(CompressedBitsPerPixel) $value
00278 }
00279 } elseif {$tag == 0x9201} {
00280 # Not very accurate, use Exposure time instead.
00281 # (That's Chris' comment. I don't know what it means.)
00282 set value [expr {pow(2,$value)}]
00283 if {$value < 4} {
00284 set value [expr {1.0 / $value}]
00285 set value [expr {int($value * 10 + 0.5) / 10.0}]
00286 } else {
00287 set value [expr {int($value + 0.49)}]
00288 }
00289 set result(ShutterSpeedValue) "$value Hz"
00290 } elseif {$tag == 0x9202} {
00291 set value [expr {int(pow(sqrt(2.0), $value) * 10 + 0.5) / 10.0}]
00292 set result(AperatureValue) $value
00293 } elseif {$tag == 0x9204} {
00294 set value [compensationFraction $value]
00295 set result(ExposureBiasValue) $value
00296 } elseif {$tag == 0x9205} {
00297 set value [expr {int(pow(sqrt(2.0), $value) * 10 + 0.5) / 10.0}]
00298 } elseif {$tag == 0x9206} {
00299 # May need calibration
00300 set result(SubjectDistance) "$value m"
00301 } elseif {$tag == 0x9207} {
00302 set result(MeteringMode) "other"
00303 if {$value == 0} {set result(MeteringMode) "unknown"}
00304 if {$value == 1} {set result(MeteringMode) "average"}
00305 if {$value == 2} {set result(MeteringMode) "center weighted average"}
00306 if {$value == 3} {set result(MeteringMode) "spot"}
00307 if {$value == 4} {set result(MeteringMode) "multi-spot"}
00308 if {$value == 5} {set result(MeteringMode) "multi-segment"}
00309 if {$value == 6} {set result(MeteringMode) "partial"}
00310 } elseif {$tag == 0x9209} {
00311 if {$value == 0} {
00312 set result(Flash) no
00313 } elseif {$value == 1} {
00314 set result(Flash) yes
00315 } else {
00316 set result(Flash) "unknown: $value"
00317 }
00318 } elseif {$tag == 0x920a} {
00319 set result(FocalLength) "$value mm"
00320 } elseif {$tag == 0xA001} {
00321 set result(ColorSpace) $value
00322 } elseif {$tag == 0xA002} {
00323 set result(ExifImageWidth) $value
00324 } elseif {$tag == 0xA003} {
00325 set result(ExifImageHeight) $value
00326 } elseif {$tag == 0xA005} {
00327 set result(ExifInteroperabilityOffset) $value
00328 } elseif {$tag == 0xA20E} {
00329 set result(FocalPlaneXResolution) $value
00330 } elseif {$tag == 0xA20F} {
00331 set result(FocalPlaneYResolution) $value
00332 } elseif {$tag == 0xA210} {
00333 set result(FocalPlaneResolutionUnit) "none"
00334 if {$value == 2} {set result(FocalPlaneResolutionUnit) "inch"}
00335 if {$value == 3} {set result(FocalPlaneResolutionUnit) "centimeter"}
00336 } elseif {$tag == 0xA217} {
00337 # 2 = 1 chip color area sensor
00338 set result(SensingMethod) $value
00339 } elseif {$tag == 0xA401} {
00340 #TJE
00341 set result(SensingMethod) "normal"
00342 if {$value == 1} {set result(SensingMethod) "custom"}
00343 } elseif {$tag == 0xA402} {
00344 #TJE
00345 set result(ExposureMode) "auto"
00346 if {$value == 1} {set result(ExposureMode) "manual"}
00347 if {$value == 2} {set result(ExposureMode) "auto bracket"}
00348 } elseif {$tag == 0xA403} {
00349 #TJE
00350 set result(WhiteBalance) "auto"
00351 if {$value == 1} {set result(WhiteBalance) "manual"}
00352 } elseif {$tag == 0xA404} {
00353 # digital zoom not used if number is zero
00354 set result(DigitalZoomRatio) "not used"
00355 if {$value != 0} {set result(DigitalZoomRatio) $value}
00356 } elseif {$tag == 0xA405} {
00357 set result(FocalLengthIn35mmFilm) "unknown"
00358 if {$value != 0} {set result(FocalLengthIn35mmFilm) $value}
00359 } elseif {$tag == 0xA406} {
00360 set result(SceneCaptureType) "Standard"
00361 if {$value == 1} {set result(SceneCaptureType) "Landscape"}
00362 if {$value == 2} {set result(SceneCaptureType) "Portrait"}
00363 if {$value == 3} {set result(SceneCaptureType) "Night scene"}
00364 } elseif {$tag == 0xA407} {
00365 set result(GainControl) "none"
00366 if {$value == 1} {set result(GainControl) "Low gain up"}
00367 if {$value == 2} {set result(GainControl) "High gain up"}
00368 if {$value == 3} {set result(GainControl) "Low gain down"}
00369 if {$value == 4} {set result(GainControl) "High gain down"}
00370 } elseif {$tag == 0x0103} {
00371 #TJE
00372 set result(Compression) "unknown"
00373 if {$value == 1} {set result(Compression) "none"}
00374 if {$value == 6} {set result(Compression) "JPEG"}
00375 } elseif {$tag == 0x011A} {
00376 #TJE
00377 set result(XResolution) $value
00378 } elseif {$tag == 0x011B} {
00379 #TJE
00380 set result(YResolution) $value
00381 } elseif {$tag == 0x0128} {
00382 #TJE
00383 set result(ResolutionUnit) "unknown"
00384 if {$value == 1} {set result(ResolutionUnit) "inch"}
00385 if {$value == 6} {set result(ResolutionUnit) "cm"}
00386 } elseif {$tag == 0x0201} {
00387 #TJE
00388 set result(JpegIFOffset) $value
00389 debug "offset = $value"
00390 } elseif {$tag == 0x0202} {
00391 #TJE
00392 set result(JpegIFByteCount) $value
00393 debug "bytecount = $value"
00394 } else {
00395 error "Unrecognized EXIF Tag: $tag (0x[string toupper [format %x $tag]])"
00396 }
00397 }
00398 }
00399 return [array get result]
00400 }
00401
00402
00403 ret ::exif::makerNote (type data , type curoffset) {
00404 variable cameraModel
00405 debug "MakerNote: offset=$curoffset"
00406
00407 array set result {}
00408 set numEntries [readShort $data $curoffset]
00409 incr curoffset 2
00410 debug "Number of directory entries: $numEntries"
00411 for {set i 0} {$i < $numEntries} {incr i} {
00412 set head [expr {$curoffset + 12 * $i}]
00413 set entry [string range $data $head [expr {$head+11}]]
00414 set tag [readShort $entry 0]
00415 set format [readShort $entry 2]
00416 set components [readLong $entry 4]
00417 set offset [readLong $entry 8]
00418 debug "$i)\tTag: $tag, format: $format, components: $components"
00419
00420 if {$tag==6} {
00421 set value [readIFDEntry $data $format $components $offset]
00422 set result(ImageFormat) $value
00423 } elseif {$tag==7} {
00424 set value [readIFDEntry $data $format $components $offset]
00425 set result(FirmwareVersion) $value
00426 } elseif {$tag==8} {
00427 set value [string range $offset 0 2]-[string range $offset 3 end]
00428 set result(ImageNumber) $value
00429 } elseif {$tag==9} {
00430 set value [readIFDEntry $data $format $components $offset]
00431 set result(Owner) $value
00432 } elseif {$tag==0x0C} {
00433 # camera serial number
00434 set msw [expr {($offset >> 16) & 0xFFFF}]
00435 set lsw [expr {$offset & 0xFFFF}]
00436 set result(CameraSerialNumber) [format %04X%05d $msw $lsw]
00437 } elseif {$tag==0x10} {
00438 set result(UnknownTag-0x10) $offset
00439 } else {
00440 if {$format == 3 && 1 < $components} {
00441 debug "MakerNote $i: TAG=$tag"
00442 catch {unset field}
00443 array set field {}
00444 for {set j 0} {$j < $components} {incr j} {
00445 set field($j) [readShort $data [expr {$offset+2*$j}]]
00446 debug "$j : $field($j)"
00447 }
00448 if {$tag == 1} {
00449 if {![string match -nocase "*Pro90*" $cameraModel]} {
00450 if {$field(1)==1} {
00451 set result(MacroMode) macro
00452 } else {
00453 set result(MacroMode) normal
00454 }
00455 }
00456 if {0 < $field(2)} {
00457 set result(SelfTimer) "[expr {$field(2)/10.0}] seconds"
00458 }
00459 set result(ImageQuality) [switch $field(3) {
00460 2 {format Normal}
00461 3 {format Fine}
00462 4 {format "CCD Raw"}
00463 5 {format "Super fine"}
00464 default {format ""}
00465 }]
00466 set result(FlashMode) [switch $field(4) {
00467 0 {format off}
00468 1 {format auto}
00469 2 {format on}
00470 3 {format "red eye reduction"}
00471 4 {format "slow synchro"}
00472 5 {format "auto + red eye reduction"}
00473 6 {format "on + red eye reduction"}
00474 default {format ""}
00475 }]
00476 if {$field(5)} {
00477 set result(ShootingMode) "Continuous"
00478 } else {
00479 set result(ShootingMode) "Single frame"
00480 }
00481 # Field 6 - don't know what it is.
00482 set result(AutoFocusMode) [switch $field(7) {
00483 0 {format "One-shot"}
00484 1 {format "AI servo"}
00485 2 {format "AI focus"}
00486 3 - 6 {format "MF"}
00487 5 {format "Continuous"}
00488 4 {
00489 # G1: uses field 32 to store single/continuous,
00490 # and always sets 7 to 4.
00491 if {[info exists field(32)] && $field(32)} {
00492 format "Continuous"
00493 } else {
00494 format "Single"
00495 }
00496 }
00497 default {format unknown}
00498 }]
00499 # Field 8 and 9 are unknown
00500 set result(ImageSize) [switch $field(10) {
00501 0 {format "large"}
00502 1 {format "medium"}
00503 2 {format "small"}
00504 default {format "unknown"}
00505 }]
00506 # Field 11 - easy shooting - see field 20
00507 # Field 12 - unknown
00508 set NHL {
00509 0 {format "Normal"}
00510 1 {format "High"}
00511 65536 {format "Low"}
00512 default {format "Unknown"}
00513 }
00514 set result(Contrast) [switch $field(13) $NHL]
00515 set result(Saturation) [switch $field(14) $NHL]
00516 set result(Sharpness) [switch $field(15) $NHL]
00517 set result(ISO) [switch $field(16) {
00518 15 {format Auto}
00519 16 {format 50}
00520 17 {format 100}
00521 18 {format 200}
00522 19 {format 400}
00523 default {format "unknown"}
00524 }]
00525 set result(MeteringMode) [switch $field(17) {
00526 3 {format evaluative}
00527 4 {format partial}
00528 5 {format center-weighted}
00529 default {format unknown}
00530 }]
00531 # Field 18 - unknown
00532 if {[info exists field(19)]} {
00533 set result(AFPoint) [switch -- [expr {$field(19)-0x3000}] {
00534 0 {format none}
00535 1 {format auto-selected}
00536 2 {format right}
00537 3 {format center}
00538 4 {format left}
00539 default {format unknown}
00540 }] ; # {}
00541 }
00542 if {[info exists field(20)]} {
00543 if {$field(20) == 0} {
00544 set result(ExposureMode) [switch $field(11) {
00545 0 {format auto}
00546 1 {format manual}
00547 2 {format landscape}
00548 3 {format "fast shutter"}
00549 4 {format "slow shutter"}
00550 5 {format "night scene"}
00551 6 {format "black and white"}
00552 7 {format sepia}
00553 8 {format portrait}
00554 9 {format sports}
00555 10 {format close-up}
00556 11 {format "pan focus"}
00557 default {format unknown}
00558 }] ; # {}
00559 } elseif {$field(20) == 1} {
00560 set result(ExposureMode) program
00561 } elseif {$field(20) == 2} {
00562 set result(ExposureMode) Tv
00563 } elseif {$field(20) == 3} {
00564 set result(ExposureMode) Av
00565 } elseif {$field(20) == 4} {
00566 set result(ExposureMode) manual
00567 } elseif {$field(20) == 5} {
00568 set result(ExposureMode) A-DEP
00569 } else {
00570 set result(ExposureMode) unknown
00571 }
00572 }
00573 # Field 21 and 22 are unknown
00574 # Field 23: max focal len, 24 min focal len, 25 units per mm
00575 if {[info exists field(23)] && [info exists field(25)]} {
00576 set result(MaxFocalLength) \
00577 "[expr {1.0 * $field(23) / $field(25)}] mm"
00578 }
00579 if {[info exists field(24)] && [info exists field(25)]} {
00580 set result(MinFocalLength) \
00581 "[expr {1.0 * $field(24) / $field(25)}] mm"
00582 }
00583 # Field 26-28 are unknown.
00584 if {[info exists field(29)]} {
00585 if {$field(29) & 0x0010} {
00586 lappend result(FlashMode) "FP_sync_enabled"
00587 }
00588 if {$field(29) & 0x0800} {
00589 lappend result(FlashMode) "FP_sync_used"
00590 }
00591 if {$field(29) & 0x2000} {
00592 lappend result(FlashMode) "internal_flash"
00593 }
00594 if {$field(29) & 0x4000} {
00595 lappend result(FlashMode) "external_E-TTL"
00596 }
00597 }
00598 if {[info exists field(34)] && \
00599 [string match -nocase "*pro90*" $cameraModel]} {
00600 if {$field(34)} {
00601 set result(ImageStabilisation) on
00602 } else {
00603 set result(ImageStabilisation) off
00604 }
00605 }
00606 } elseif {$tag == 4} {
00607 set result(WhiteBalance) [switch $field(7) {
00608 0 {format Auto}
00609 1 {format Daylight}
00610 2 {format Cloudy}
00611 3 {format Tungsten}
00612 4 {format Fluorescent}
00613 5 {format Flash}
00614 6 {format Custom}
00615 default {format Unknown}
00616 }]
00617 if {$field(14) & 0x07} {
00618 set result(AFPointsUsed) \
00619 [expr {($field(14)>>12) & 0x0F}]
00620 if {$field(14)&0x04} {
00621 append result(AFPointsUsed) " left"
00622 }
00623 if {$field(14)&0x02} {
00624 append result(AFPointsUsed) " center"
00625 }
00626 if {$field(14)&0x01} {
00627 append result(AFPointsUsed) " right"
00628 }
00629 }
00630 if {[info exists field(15)]} {
00631 set v $field(15)
00632 if {32768 < $v} {incr v -65536}
00633 set v [compensationFraction [expr {$v / 32.0}]]
00634 set result(FlashExposureCompensation) $v
00635 }
00636 if {[info exists field(19)]} {
00637 set result(SubjectDistance) "$field(19) m"
00638 }
00639 } elseif {$tag == 15} {
00640 foreach k [array names field] {
00641 set func [expr {($field($k) >> 8) & 0xFF}]
00642 set v [expr {$field($k) & 0xFF}]
00643 if {$func==1 && $v} {
00644 set result(LongExposureNoiseReduction) on
00645 } elseif {$func==1 && !$v} {
00646 set result(LongExposureNoiseReduction) off
00647 } elseif {$func==2} {
00648 set result(Shutter/AE-Lock) [switch $v {
00649 0 {format "AF/AE lock"}
00650 1 {format "AE lock/AF"}
00651 2 {format "AF/AF lock"}
00652 3 {format "AE+release/AE+AF"}
00653 default {format "Unknown"}
00654 }]
00655 } elseif {$func==3} {
00656 if {$v} {
00657 set result(MirrorLockup) enable
00658 } else {
00659 set result(MirrorLockup) disable
00660 }
00661 } elseif {$func==4} {
00662 if {$v} {
00663 set result(Tv/AvExposureLevel) "1/3 stop"
00664 } else {
00665 set result(Tv/AvExposureLevel) "1/2 stop"
00666 }
00667 } elseif {$func==5} {
00668 if {$v} {
00669 set result(AFAssistLight) off
00670 } else {
00671 set result(AFAssistLight) on
00672 }
00673 } elseif {$func==6} {
00674 if {$v} {
00675 set result(ShutterSpeedInAVMode) "Fixed 1/200"
00676 } else {
00677 set result(ShutterSpeedInAVMode) "Auto"
00678 }
00679 } elseif {$func==7} {
00680 set result(AEBSeq/AutoCancel) [switch $v {
00681 0 {format "0, -, + enabled"}
00682 1 {format "0, -, + disabled"}
00683 2 {format "-, 0, + enabled"}
00684 3 {format "-, 0, + disabled"}
00685 default {format unknown}
00686 }]
00687 } elseif {$func==8} {
00688 if {$v} {
00689 set result(ShutterCurtainSync) "2nd curtain sync"
00690 } else {
00691 set result(ShutterCurtainSync) "1st curtain sync"
00692 }
00693 } elseif {$func==9} {
00694 set result(LensAFStopButtonFnSwitch) [switch $v {
00695 0 {format "AF stop"}
00696 1 {format "operate AF"}
00697 2 {format "lock AE and start timer"}
00698 default {format unknown}
00699 }]
00700 } elseif {$func==10} {
00701 if {$v} {
00702 set result(AutoReductionOfFillFlash) disable
00703 } else {
00704 set result(AutoReductionOfFillFlash) enable
00705 }
00706 } elseif {$func==11} {
00707 if {$v} {
00708 set result(MenuButtonReturnPosition) previous
00709 } else {
00710 set result(MenuButtonReturnPosition) top
00711 }
00712 } elseif {$func==12} {
00713 set result(SetButtonFuncWhenShooting) [switch $v {
00714 0 {format "not assigned"}
00715 1 {format "change quality"}
00716 2 {format "change ISO speed"}
00717 3 {format "select parameters"}
00718 default {format unknown}
00719 }]
00720 } elseif {$func==13} {
00721 if {$v} {
00722 set result(SensorCleaning) enable
00723 } else {
00724 set result(SensorCleaning) disable
00725 }
00726 } elseif {$func==0} {
00727 # Discovered by DNew?
00728 set result(CameraOwner) $v
00729 } else {
00730 append result(UnknownCustomFunc) "$func=$v "
00731 }
00732 }
00733 }
00734 } else {
00735 debug [format "makerNote: Unrecognized TAG: 0x%x" $tag]
00736 }
00737 }
00738 }
00739 return [array get result]
00740 }
00741
00742 ret ::exif::readShort (type data , type offset) {
00743 variable intel
00744 if {[string length $data] < [expr {$offset+2}]} {
00745 error "readShort: end of string reached"
00746 }
00747 set ch1 [string index $data $offset]
00748 set ch2 [string index $data [expr {$offset+1}]]
00749 scan $ch1 %c ch1 ; scan $ch2 %c ch2
00750 if {$intel} {
00751 return [expr {$ch1 + 256 * $ch2}]
00752 } else {
00753 return [expr {$ch2 + 256 * $ch1}]
00754 }
00755 }
00756
00757 ret ::exif::readLong (type data , type offset) {
00758 variable intel
00759 if {[string length $data] < [expr {$offset+4}]} {
00760 error "readLong: end of string reached"
00761 }
00762 set ch1 [string index $data $offset]
00763 set ch2 [string index $data [expr {$offset+1}]]
00764 set ch3 [string index $data [expr {$offset+2}]]
00765 set ch4 [string index $data [expr {$offset+3}]]
00766 scan $ch1 %c ch1 ; scan $ch2 %c ch2
00767 scan $ch3 %c ch3 ; scan $ch4 %c ch4
00768 if {$intel} {
00769 return [expr {(((($ch4 * 256) + $ch3) * 256) + $ch2) * 256 + $ch1}]
00770 } else {
00771 return [expr {(((($ch1 * 256) + $ch2) * 256) + $ch3) * 256 + $ch4}]
00772 }
00773 }
00774
00775 ret ::exif::readIFDEntry (type data , type format , type components , type offset) {
00776 variable intel
00777 if {$format == 2} {
00778 # ASCII string
00779 set value [string range $data $offset [expr {$offset+$components-1}]]
00780 return [string trimright $value "\0"]
00781 } elseif {$format == 3} {
00782 # unsigned short
00783 if {!$intel} {
00784 set offset [expr {0xFFFF & ($offset >> 16)}]
00785 }
00786 return $offset
00787 } elseif {$format == 4} {
00788 # unsigned long
00789 return $offset
00790 } elseif {$format == 5} {
00791 # unsigned rational
00792 # This could be messy, if either is >2**31
00793 set numerator [readLong $data $offset]
00794 set denominator [readLong $data [expr {$offset + 4}]]
00795 return [expr {(1.0*$numerator)/$denominator}]
00796 } elseif {$format == 10} {
00797 # signed rational
00798 # Should work normally, since everything in Tcl is signed
00799 set numerator [readLong $data $offset]
00800 set denominator [readLong $data [expr {$offset + 4}]]
00801 return [expr {(1.0*$numerator)/$denominator}]
00802 } else {
00803 set x [format %08x $format]
00804 error "Invalid IFD entry format: $x"
00805 }
00806 }
00807
00808 ret ::exif::compensationFraction (type value) {
00809 if {$value==0} {return 0}
00810 if {$value < 0} {
00811 set result "-"
00812 set value [expr {0-$value}]
00813 } else {
00814 set result "+"
00815 }
00816 set value [expr {int(0.5 + $value * 6)}]
00817 set integer [expr {int($value / 6)}]
00818 set sixths [expr {$value % 6}]
00819 if {$integer != 0} {
00820 append result $integer
00821 if {$sixths != 0} {
00822 append result " "
00823 }
00824 }
00825 if {$sixths == 2} {
00826 append result "1/3"
00827 } elseif {$sixths == 3} {
00828 append result "1/2"
00829 } elseif {$sixths == 4} {
00830 append result "2/3"
00831 } else {
00832 # Added by DNew
00833 append result "$sixths/6"
00834 }
00835 return $result
00836 }
00837
00838
00839
00840 ret ::exif::fieldnames () {
00841 variable cached_fieldnames
00842 if {[info exists cached_fieldnames]} {
00843 return $cached_fieldnames
00844 }
00845 # Otherwise, parse the source to find the fieldnames.
00846 # Cool, huh? Don'tcha just love Tcl?
00847 # Because of this, "result(...)" should only appear
00848 # in these functions when "..." is the literal name
00849 # of a field to be returned.
00850 array set namelist {}
00851 foreach proc {analyze app1 exifSubIFD makerNote} {
00852 set body [info body ::exif::$proc]
00853 foreach line [split $body \n] {
00854 if {[regexp {result\(([^)]+)\)} $line junk name]} {
00855 set namelist($name) {}
00856 }
00857 }
00858 }
00859 set cached_fieldnames [lsort -dictionary [array names namelist]]
00860 return $cached_fieldnames
00861 }
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928 if {0} {
00929
00930 x = [exif::fieldnames]
00931 puts "fieldnames = $x"
00932 f = [open [lindex $argv 0]]
00933 array v = [exif::analyze $f]
00934 close $f
00935 parray v
00936 }
00937
00938