Keith Vetter -- 2015-02-16 Here a tool that parses a png file and displays detailed information about each chunk in it. You can specify the amount of details displayed by adjusting the verbosity level with -v and -q flags.
The PNG specification is at http://www.libpng.org/pub/png/spec/1.2 .
set verbose 2 proc PngDump {fname} { global IHDR unset -nocomplain ::IDAT unset -nocomplain ::PALETTE unset -nocomplain ::APALETTE ShowLine 1 $fname ShowLine 1 "[string repeat = [string length $fname]]" set fh [open $fname r] try { fconfigure $fh -encoding binary -translation binary -eofchar {} if {[read $fh 8] != "\x89PNG\r\n\x1a\n"} { ERROR "$fname is not a png file" return } while {[set r [read $fh 8]] != ""} { binary scan $r Ia4 len type set data [read $fh $len] set crc [read $fh 4] set handler "Do[string toupper $type]" if {[info procs $handler] ne ""} { $handler $data } else { ERROR "unknown chunk type: $type" } } } finally { close $fh } ShowLine 1 "" if {$::verbose == 0} { set msg "$fname: $IHDR(width)x$IHDR(height) " append msg "$IHDR(color)/$IHDR(depth)/$IHDR(interlace)" ShowLine 0 $msg } } proc ERROR {msg} { puts stderr $msg } proc ShowLine {lvl msg} { if {$lvl > $::verbose} return puts $msg } proc ShowData {lvl args} { if {$lvl > $::verbose} return foreach {key value} $args { set msg [format " %-12s %s" "${key}:" $value] puts $msg } } proc Adorn {value labels} { set lbl "-" if {$value < [llength $labels]} { set lbl [lindex $labels $value] } if {$lbl eq "-"} { return $value } return "$value -- $lbl" } ################################################################ proc DoIHDR {data} { global IHDR set ctypes_ {grayscale - RGB indexed "grayscale with alpha" - RGBA} binary scan $data IIccccc IHDR(width) IHDR(height) IHDR(depth) IHDR(color) \ IHDR(compression) IHDR(filter) IHDR(interlace) if {$IHDR(color) == 0 || $IHDR(color) == 3} { set bits [expr {$IHDR(width) * $IHDR(depth)}] set IHDR(bytes,row) [expr {int(ceil($bits / 8.0))}] set IHDR(bpp) [expr {$IHDR(depth) > 8 ? 2 : 1}] } elseif {$IHDR(color) == 2} { set IHDR(bytes,row) [expr {$IHDR(width) * 3 * $IHDR(depth) / 8}] set IHDR(bpp) [expr {3 * $IHDR(depth) / 8}] } elseif {$IHDR(color) == 4} { set IHDR(bytes,row) [expr {$IHDR(width) * $IHDR(depth) / 8}] set IHDR(bpp) [expr {2 * $IHDR(depth) / 8}] } elseif {$IHDR(color) == 6} { set IHDR(bytes,row) [expr {$IHDR(width) * 4 * $IHDR(depth) / 8}] set IHDR(bpp) [expr {4 * $IHDR(depth) / 8}] } ShowLine 1 "IHDR : Image header" ShowData 1 size "$IHDR(width)x$IHDR(height)" ShowData 1 "color type" [Adorn $IHDR(color) $ctypes_] ShowData 1 depth $IHDR(depth) ShowData 2 compression $IHDR(compression) ShowData 2 filter $IHDR(filter) ShowData 1 interlace [Adorn $IHDR(interlace) {none Adam7}] } proc DoPLTE {data} { global PALETTE ShowLine 1 "PLTE : Palette" set cnt [expr {-1 + [string length $data] / 3}] for {set i 0} {$i <= $cnt} {incr i} { set rgb [string range $data [expr {$i * 3}] [expr {$i * 3 + 2}]] binary scan $rgb cucucu r g b set PALETTE($i) [expr {($r << 16) | ($g << 8) | $b}] if {$i < 5} { ShowData 2 "palette\[$i]" [format "#%06X" $PALETTE($i)] } } if {$cnt >= 5} { ShowLine 2 " ..." ShowData 2 "palette\[$cnt]" [format "#%06X" $PALETTE($cnt)] } } proc DoIDAT {data} { global IDAT # Just accumulate info for summary info in IEND incr IDAT(cnt) incr IDAT(len) [string length $data] append IDAT(data) $data } proc DoIEND {data} { # Combine multiple IDAT and display info here binary scan $::IDAT(data) cucu CMF FLG set CM [expr {$CMF & 0xF}] set methods_ {- - - - - - - - deflate} set CINFO [expr {$CMF >> 4}] set window [expr {2**($CINFO+8)}] set FCHECK [expr {$FLG & 0x1F}] set FDICT [expr {($FLG & 0x20) >> 5}] set FLEVEL [expr {$FLG >> 6 }] set flevels_ {fastest fast default maximum} ShowLine 1 "IDAT : Image data" ShowData 2 segments $::IDAT(cnt) size $::IDAT(len) ShowData 2 method [Adorn $CM $methods_] ShowData 2 window $window ShowData 2 level "[Adorn $FLEVEL $flevels_] compression" ShowLine 1 "IEND : Image trailer" } proc DoTRNS {data} { global IHDR APALETTE ShowLine 1 "tRNS : Transparency" if {$IHDR(color) == 3} { ;# Indexed color png set cnt [expr {-1 + [string length $data]}] for {set i 0} {$i <= $cnt} {incr i} { binary scan [string index $data $i] cu alpha set APALETTE($i) $alpha if {$i > 4} continue if {$alpha == 0} { set alpha "$alpha -- transparent" } elseif {$alpha == 255} { set alpha "$alpha -- opaque" } ShowData 2 "alpha palette\[$i\]" $alpha } if {$cnt >= 4} { ShowLine 2 " ..." set alpha $APALETTE($cnt) if {$alpha == 0} { set alpha "$alpha -- transparent" } elseif {$alpha == 255} { set alpha "$alpha -- opaque" } ShowData 2 "alpha palette\[$cnt\]" $alpha } } elseif {$IHDR(color) == 0} { ;# Grayscale png binary scan $data S alpha ShowData 2 "gray alpha" $alpha } elseif {$IHDR(color) == 2} { ;# Truecolor png binary scan $data SSS red green blue ShowData 2 "red alpha" $red "green alpha" $green "blue alpha" $blue } else { ShowData 2 ? ? } } proc DoGAMA {data} { binary scan $data I gamma set gamma [expr {$gamma / 100000.}] ShowLine 1 "gAMA : Image gamma" ShowData 2 gamma $gamma } proc DoCHRM {data} { ShowLine 1 "cHRM : Primary chromaticities" set lbls {"white x" "white y" "red x" "red y" "green x" "green y" "blue x" "blue y"} for {set i 0} {$i < 8} {incr i} { set chrm [string range $data [expr {$i*4}] [expr {$i*4 + 3}]] binary scan $chrm I val ShowData 2 [lindex $lbls $i] $val } } proc DoSRGB {data} { binary scan $data c render set intents_ {Perceptual "Relative colorimetric" Saturation "Absolute colorimetric"} ShowData 2 render [Adorn $render $intents_] } proc DoICCP {data} { set name [lindex [split $data \x00] 0] ShowLine 1 "iCCP : Embedded ICC profile" ShowData 2 name $name } proc DoTEXT {data} { ShowLine 1 "tEXt : Textual data" lassign [split $data \x00] key value ShowData 2 key $key value $value } proc DoZTXT {data} { set ::data $data ShowLine 1 "zTXt : Compressed textual data" lassign [split $data \x00] key set keylen [expr {[string length $key] + 1}] binary scan [string index $data $keylen] cu method set value [string range $data $keylen+1 end] set compressed [string range $value 2 end-4] set uncompressed [zlib inflate $compressed] ShowData 2 method [Adorn $method {deflate}] key $key text $uncompressed } proc DoITXT {data} { ShowLine 1 "iTXt : International textual data" lassign [split $data \x00] key set keylen [expr {[string length $key] + 1}] binary scan [string range $data $keylen $keylen+2] cc compress method if {$compress == 1} { ShowData 2 $key ... ShowData 2 compress $compress method [Adorn $method {deflate}] text ... } else { set rest [string range $data $keylen+2 end] lassign [split $rest \x00] language key2 value ShowData 2 key $key language $language key2 $key2 text $value } } proc DoBKGD {data} { ShowLine 1 "bKGD : Background color" set len [string length $data] if {$len == 1} { binary scan $data cu idx ShowData 2 "palette idx" $idx } elseif {$len == 2} { binary scan $data cucu gray alpha ShowData 2 gray $gray alpha $alpha } elseif {$len == 6} { binary scan $data SSS red green blue ShowData 2 red $red green $green blue $blue } else { ShowData 2 ? ? } } proc DoPHYS {data} { binary scan $data IIc x y units ShowLine 1 "pHYs : Physical pixel dimensions" ShowData 2 x-axis $x ShowData 2 y-axis $y ShowData 2 units [Adorn $units {"unknown" "meters"}] } proc DoSBIT {data} { ShowLine 1 "sBIT : Significant bits" set len [string length $data] if {$len == 1} { binary scan $data c gray ShowData 2 gray $gray } elseif {$len == 2} { binary scan $data cc gray alpha ShowData 2 gray $gray alpha $alpha } elseif {$len == 3} { binary scan $data ccc red green blue ShowData 2 red $red green $green blue $blue } elseif {$len == 4} { binary scan $data cccc red green blue alpha ShowData 2 red $red green $green blue $blue alpha $alpha } else { ShowData 2 ? ? } } proc DoSPLT {data} { ShowLine 1 "sPLT : Suggested palette" set name [lindex [split $data \x00] 0] ShowData 2 "palette name" $name } proc DoSPAL {data} { # see ftp://ftp.simplesystems.org/pub/libpng/png-group/documents/history/png-proposed-sPLT-19961107.html lassign [split $data \x00] name signature ShowLine 1 "spAL : Suggested palette beta sPLT" ShowData 2 "palette name" $name signature $signature } proc DoHIST {data} { set cnt [expr {[string length $data] / 2}] set min [expr {min(5,$cnt)}] ShowLine 1 "hIST : Palette histogram" ShowData 2 entries $cnt for {set i 0} {$i < $min} {incr i} { binary scan [string range $data [expr {2 * $i}] end] S value ShowData 2 "hist\[$i]" $value } if {$min < $cnt} { ShowLine 2 " ..." } } proc DoTIME {data} { binary scan $data Sccccc year month day hour minute second ShowLine 1 "tIME : Image last-modification time" ShowData 2 time "$year/$month/$day $hour:$minute:$second" } ################################################################ if {$argc == 0} { ERROR "usage: pngDump ?-v? ?-q? image1.png ?image2.png ...?" return } foreach fname $argv { if {$fname eq "-v"} { incr verbose ; continue } if {$fname eq "-q"} { incr verbose -1 ; continue } if {$fname eq "-qq"} { incr verbose -2 ; continue } PngDump $fname } if {! $tcl_interactive} exit return