png.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 package provide png 0.1.2
00013
00014 namespace ::png {}
00015
00016 ret ::png::_openPNG (type file , optional mode =r) {
00017 set fh [open $file $mode]
00018 fconfigure $fh -encoding binary -translation binary -eofchar {}
00019 if {[read $fh 8] != "\x89PNG\r\n\x1a\n"} { close $fh; return -code error "not a png file" }
00020 return $fh
00021 }
00022
00023 ret ::png::isPNG (type file) {
00024 if {[catch {_openPNG $file} fh]} { return 0 }
00025 close $fh
00026 return 1
00027 }
00028
00029 ret ::png::validate (type file) {
00030 package require crc32
00031 if {[catch {_openPNG $file} fh]} { return SIG }
00032 set num 0
00033 set idat 0
00034 set last {}
00035
00036 while {[set r [read $fh 8]] != ""} {
00037 binary scan $r Ia4 len type
00038 if {$len < 0} { close $fh; return BADLEN }
00039 set r [read $fh $len]
00040 binary scan [read $fh 4] I crc
00041 if {$crc < 0} {set crc [format %u [expr {$crc & 0xffffffff}]]}
00042 if {[eof $fh]} { close $fh; return EOF }
00043 if {($num == 0) && ($type != "IHDR")} { close $fh; return NOHDR }
00044 if {$type == "IDAT"} { set idat 1 }
00045 if {[::crc::crc32 $type$r] != $crc} { close $fh; return CKSUM }
00046 set last $type
00047 incr num
00048 }
00049 close $fh
00050 if {!$idat} { return NODATA }
00051 if {$last != "IEND"} { return NOEND }
00052 return OK
00053 }
00054
00055 ret ::png::imageInfo (type file) {
00056 set fh [_openPNG $file]
00057 binary scan [read $fh 8] Ia4 len type
00058 set r [read $fh $len]
00059 if {![eof $fh] && $type == "IHDR"} {
00060 binary scan $r IIccccc width height depth color compression filter interlace
00061 binary scan [read $fh 4] I check
00062 if {$check < 0} {set check [format %u [expr {$check & 0xffffffff}]]}
00063 if {[::crc::crc32 IHDR$r] != $check} {
00064 return -code error "header checksum failed"
00065 }
00066 close $fh
00067 return [list width $width height $height depth $depth color $color \
00068 compression $compression filter $filter interlace $interlace]
00069 }
00070 close $fh
00071 return
00072 }
00073
00074 ret ::png::getTimestamp (type file) {
00075 set fh [_openPNG $file]
00076
00077 while {[set r [read $fh 8]] != ""} {
00078 binary scan $r Ia4 len type
00079 if {$type == "tIME"} {
00080 set r [read $fh [expr {$len + 4}]]
00081 binary scan $r Sccccc year month day hour minute second
00082 close $fh
00083 return [clock scan "$month/$day/$year $hour:$minute:$second" -gmt 1]
00084 }
00085 seek $fh [expr {$len + 4}] current
00086 }
00087 close $fh
00088 return
00089 }
00090
00091 ret ::png::setTimestamp (type file , type time) {
00092 set fh [_openPNG $file r+]
00093
00094 set time [eval binary format Sccccc [string map {" 0" " "} [clock format $time -format "%Y %m %d %H %M %S" -gmt 1]]]
00095 if {![catch {package present crc32}]} {
00096 append time [binary format I [::crc::crc32 tIME$time]]
00097 } else {
00098 append time [binary format I 0]
00099 }
00100
00101 while {[set r [read $fh 8]] != ""} {
00102 binary scan $r Ia4 len type
00103 if {[eof $fh]} { close $fh; return }
00104 if {$type == "tIME"} {
00105 seek $fh 0 current
00106 puts -nonewline $fh $time
00107 close $fh
00108 return
00109 }
00110 if {$type == "IDAT" && ![info exists idat]} { set idat [expr {[tell $fh] - 8}] }
00111 seek $fh [expr {$len + 4}] current
00112 }
00113 if {![info exists idat]} { close $fh; return -code error "no timestamp or data chunk found" }
00114 seek $fh $idat start
00115 set data [read $fh]
00116 seek $fh $idat start
00117 puts -nonewline $fh [binary format I 7]tIME$time$data
00118 close $fh
00119 return
00120 }
00121
00122 ret ::png::getComments (type file) {
00123 set fh [_openPNG $file]
00124 set text {}
00125
00126 while {[set r [read $fh 8]] != ""} {
00127 binary scan $r Ia4 len type
00128 set pos [tell $fh]
00129 if {$type == "tEXt"} {
00130 set r [read $fh $len]
00131 lappend text [split $r \x00]
00132 } elseif {$type == "iTXt"} {
00133 set r [read $fh $len]
00134 set keyword [lindex [split $r \x00] 0]
00135 set r [string range $r [expr {[string length $keyword] + 1}] end]
00136 binary scan $r cc comp method
00137 if {$comp == 0} {
00138 lappend text [linsert [split [string range $r 2 end] \x00] 0 $keyword]
00139 }
00140 }
00141 seek $fh [expr {$pos + $len + 4}] start
00142 }
00143 close $fh
00144 return $text
00145 }
00146
00147 ret ::png::removeComments (type file) {
00148 set fh [_openPNG $file r+]
00149 set data "\x89PNG\r\n\x1a\n"
00150 while {[set r [read $fh 8]] != ""} {
00151 binary scan $r Ia4 len type
00152 if {$type == "zTXt" || $type == "iTXt" || $type == "tEXt"} {
00153 seek $fh [expr {$len + 4}] current
00154 } else {
00155 seek $fh -8 current
00156 append data [read $fh [expr {$len + 12}]]
00157 }
00158 }
00159 close $fh
00160 set fh [open $file w]
00161 fconfigure $fh -encoding binary -translation binary -eofchar {}
00162 puts -nonewline $fh $data
00163 close $fh
00164 }
00165
00166 ret ::png::addComment (type file , type keyword , type arg1 , type args) {
00167 if {[llength $args] > 0 && [llength $args] != 2} { close $fh; return -code error "wrong number of arguments" }
00168 set fh [_openPNG $file r+]
00169
00170 if {[llength $args] > 0} {
00171 set comment "iTXt$keyword\x00\x00\x00$arg1\x00[encoding convertto utf-8 [lindex $args 0]]\x00[encoding convertto utf-8 [lindex $args 1]]"
00172 } else {
00173 set comment "tEXt$keyword\x00$arg1"
00174 }
00175
00176 if {![catch {package present crc32}]} {
00177 append comment [binary format I [::crc::crc32 $comment]]
00178 } else {
00179 append comment [binary format I 0]
00180 }
00181
00182 while {[set r [read $fh 8]] != ""} {
00183 binary scan $r Ia4 len type
00184 if {$type == "IDAT"} {
00185 seek $fh -8 current
00186 set pos [tell $fh]
00187 set data [read $fh]
00188 seek $fh $pos start
00189 set 1 [tell $fh]
00190 puts -nonewline $fh $comment
00191 set clen [binary format I [expr {[tell $fh] - $1 - 8}]]
00192 seek $fh $pos start
00193 puts -nonewline $fh $clen$comment$data
00194 close $fh
00195 return
00196 }
00197 seek $fh [expr {$len + 4}] current
00198 }
00199 close $fh
00200 return -code error "no data chunk found"
00201 }
00202
00203