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