zpng 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c80d32f2f6027ae69acc2f9f21142d89fe2d8bb24bd1625d2eae5470df90bc4
4
- data.tar.gz: 76a0239f9c6d80fb7883f9c5e80c325ae74e94b72cf345ac6b0a168291ab75f3
3
+ metadata.gz: 24abc68e64e96c4df6b0626ce0f393a4de8e1363551f5a3deed4f77af3cf8402
4
+ data.tar.gz: ece0c993df0a99091a542f7db3f7ad95975eccffbf915d15f8d692ff83d7fdfe
5
5
  SHA512:
6
- metadata.gz: 1402123bc6a4fb28084b3f2645b9df50877da1bd157b6e888f4f2bdde301d8d54cf8620a77d2fd6c6e0fc1615f712f14379549576d395a9185f8641c369db8a2
7
- data.tar.gz: 497d257914ba09e8f482a311bf6c499bebb4c09622b1681ba1fab845d76175d4e94163bf05158ef1e7013a080ebc00befd49fec3f6a209d92ef0e50b9df61927
6
+ metadata.gz: 2716d2d131a293f5aeb04c8ef0d8b7423b41adfedb05507b8fec063a6d02b16e8ff5f652f6461e3114e05eb7210cf62b85898ab57e93aa798bb7477a09043f61
7
+ data.tar.gz: 8bd1800562ec76857d7164099e112224516d9f9fb11b4bb7a428f39e583b3820ac33f00ecd12569d1fe4ded091ae9d20cbc7c42e327370dec5d663fd0bd26a18
data/README.md CHANGED
@@ -36,8 +36,10 @@ Usage
36
36
 
37
37
  -C, --crop GEOMETRY crop image, {WIDTH}x{HEIGHT}+{X}+{Y},
38
38
  puts results on stdout unless --ascii given
39
+ -R, --rebuild NEW_FILENAME rebuild image, useful in restoring borked images
39
40
 
40
41
  -A, --ascii Try to convert image to ASCII (works best with monochrome images)
42
+ --ascii-string STRING Use specific string to map pixels to ASCII characters
41
43
  -N, --ansi Try to display image as ANSI colored text
42
44
  -2, --256 Try to display image as 256-colored text
43
45
  -W, --wide Use 2 horizontal characters per one pixel
@@ -52,7 +54,7 @@ Usage
52
54
 
53
55
  [.] image size 35x35, 24bpp, COLOR_RGB
54
56
  [.] uncompressed imagedata size = 3710 bytes
55
- [.] <Chunk #00 IHDR size= 13, crc=91bb240e, color=2, compression=0, depth=8, filter=0, height=35, interlace=0, width=35> CRC OK
57
+ [.] <Chunk #00 IHDR size= 13, crc=91bb240e, color=2, compression=0, depth=8, filter=0, height=35, interlace=0, offset=8, width=35> CRC OK
56
58
  [.] <Chunk #01 sRGB size= 1, crc=aece1ce9 > CRC OK
57
59
  [.] <Chunk #02 IDAT size= 399, crc=59790716 > CRC OK
58
60
  [.] <Chunk #03 IEND size= 0, crc=ae426082 > CRC OK
@@ -66,7 +68,7 @@ Usage
66
68
  01 ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 |................|
67
69
  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 3678 bytes
68
70
 
69
- [.] <Chunk #00 IHDR size= 13, crc=91bb240e, color=2, compression=0, depth=8, filter=0, height=35, idx=0, interlace=0, width=35> CRC OK
71
+ [.] <Chunk #00 IHDR size= 13, crc=91bb240e, color=2, compression=0, depth=8, filter=0, height=35, idx=0, interlace=0, offset=8, width=35> CRC OK
70
72
  00 00 00 23 00 00 00 23 08 02 00 00 00 |...#...#..... |
71
73
 
72
74
  [.] <Chunk #01 sRGB size= 1, crc=aece1ce9 > CRC OK
@@ -84,7 +86,7 @@ Usage
84
86
 
85
87
  # zpng --chunks qr_aux_chunks.png
86
88
 
87
- [.] <Chunk #00 IHDR size= 13, crc=36a28ef4, color=0, compression=0, depth=1, filter=0, height=35, interlace=0, width=35> CRC OK
89
+ [.] <Chunk #00 IHDR size= 13, crc=36a28ef4, color=0, compression=0, depth=1, filter=0, height=35, interlace=0, offset=8, width=35> CRC OK
88
90
  [.] <Chunk #01 gAMA size= 4, crc=0bfc6105 > CRC OK
89
91
  [.] <Chunk #02 sRGB size= 1, crc=aece1ce9 > CRC OK
90
92
  [.] <Chunk #03 cHRM size= 32, crc=9cba513c > CRC OK
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.4
1
+ 0.4.0
data/lib/zpng/chunk.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  module ZPNG
2
2
  class Chunk
3
- attr_accessor :size, :type, :data, :crc, :idx
3
+ attr_accessor :size, :type, :data, :crc, :idx, :offset
4
+
5
+ KNOWN_TYPES = %w'IHDR PLTE IDAT IEND cHRM gAMA iCCP sBIT sRGB bKGD hIST tRNS pHYs sPLT tIME iTXt tEXt zTXt'
6
+ VALID_SIZE_RANGE = 0..((2**31)-1)
4
7
 
5
8
  include DeepCopyable
6
9
 
@@ -10,21 +13,27 @@ module ZPNG
10
13
  begin
11
14
  if const_defined?(type.upcase)
12
15
  klass = const_get(type.upcase)
13
- klass.new(io)
14
- else
15
- Chunk.new(io)
16
+ return klass.new(io)
16
17
  end
17
18
  rescue NameError
18
19
  # invalid chunk type?
19
- Chunk.new(io)
20
20
  end
21
+ # putting this out of rescue makes better non-confusing exception messages
22
+ # if exception occurs somewhere in Chunk.new
23
+ Chunk.new(io)
21
24
  end
22
25
 
23
26
  def initialize x = {}
24
27
  if x.respond_to?(:read)
25
28
  # IO
29
+ @offset = x.tell
26
30
  @size, @type = x.read(8).unpack('Na4')
27
- @data = x.read(size)
31
+ begin
32
+ @data = x.read(size)
33
+ rescue Errno::EINVAL
34
+ # TODO: show warning?
35
+ @data = x.read if size > VALID_SIZE_RANGE.end
36
+ end
28
37
  @crc = x.read(4).to_s.unpack('N').first
29
38
  elsif x.respond_to?(:[])
30
39
  # Hash
@@ -66,6 +75,22 @@ module ZPNG
66
75
  expected_crc == crc
67
76
  end
68
77
 
78
+ def check checks = {type: true, crc: true, size: true}
79
+ checks.each do |check_type, check_mode|
80
+ case check_type
81
+ when :type
82
+ return false if check_mode != KNOWN_TYPES.include?(self.type)
83
+ when :crc
84
+ return false if check_mode != crc_ok?
85
+ when :size
86
+ return false if check_mode != VALID_SIZE_RANGE.include?(self.size)
87
+ end
88
+ end
89
+ true
90
+ end
91
+
92
+ def valid?; check; end
93
+
69
94
  def fix_crc!
70
95
  @crc = Zlib.crc32(data, Zlib.crc32(type))
71
96
  end
data/lib/zpng/cli.rb CHANGED
@@ -52,6 +52,10 @@ module ZPNG
52
52
  @actions << [:crop, x]
53
53
  end
54
54
 
55
+ opts.on "-R", "--rebuild NEW_FILENAME", "rebuild image, useful in restoring borked images" do |x|
56
+ @actions << [:rebuild, x]
57
+ end
58
+
55
59
  opts.separator ""
56
60
  opts.on "-A", '--ascii', 'Try to convert image to ASCII (works best with monochrome images)' do
57
61
  @actions << :ascii
@@ -137,6 +141,10 @@ module ZPNG
137
141
  print @img.export unless @actions.include?(:ascii)
138
142
  end
139
143
 
144
+ def rebuild fname
145
+ File.binwrite(fname, @img.export)
146
+ end
147
+
140
148
  def load_file fname
141
149
  @img = Image.load fname, :verbose => @options[:verbose]+1
142
150
  end
data/lib/zpng/image.rb CHANGED
@@ -108,11 +108,43 @@ module ZPNG
108
108
  end
109
109
  end
110
110
 
111
+ HEUR_CHUNK_SIZE_RANGE = -16..16
112
+
113
+ # assume previous chunk size is not right, try to iterate over neighbour data
114
+ def _apply_heuristics io, prev_chunk, chunk
115
+ prev_pos = io.tell
116
+ HEUR_CHUNK_SIZE_RANGE.each do |delta|
117
+ next if delta == 0
118
+ next if prev_chunk.data.size + delta < 0
119
+ io.seek(chunk.offset+delta, IO::SEEK_SET)
120
+ potential_chunk = Chunk.new(io)
121
+ if potential_chunk.valid?
122
+ STDERR.puts "[!] heuristics: found invalid chunk at offset #{chunk.offset}, but valid one at #{chunk.offset+delta}. using latter".red
123
+ if delta > 0
124
+ io.seek(chunk.offset, IO::SEEK_SET)
125
+ data = io.read(delta)
126
+ STDERR.puts "[!] #{delta} extra bytes of data: #{data.inspect}".red
127
+ else
128
+ io.seek(chunk.offset+delta, IO::SEEK_SET)
129
+ end
130
+ return true
131
+ end
132
+ end
133
+ false
134
+ end
135
+
111
136
  def _read_png io
137
+ prev_chunk = nil
112
138
  while !io.eof?
113
139
  chunk = Chunk.from_stream(io)
140
+ # heuristics
141
+ if prev_chunk && prev_chunk.check(type: true, crc: false) &&
142
+ chunk.check(type: false, crc: false) && chunk.data
143
+ redo if _apply_heuristics(io, prev_chunk, chunk)
144
+ end
114
145
  chunk.idx = @chunks.size
115
146
  @chunks << chunk
147
+ prev_chunk = chunk
116
148
  break if chunk.is_a?(Chunk::IEND)
117
149
  end
118
150
  end
@@ -137,7 +169,7 @@ module ZPNG
137
169
  unless io.eof?
138
170
  offset = io.tell
139
171
  @extradata << io.read
140
- puts "[?] #{@extradata.last.size} bytes of extra data after image end (IEND), offset = 0x#{offset.to_s(16)}".red if @verbose >= 1
172
+ STDERR.puts "[?] #{@extradata.last.size} bytes of extra data after image end (IEND), offset = 0x#{offset.to_s(16)}".red if @verbose >= 1
141
173
  end
142
174
  end
143
175
 
@@ -250,16 +282,16 @@ module ZPNG
250
282
  r << zi.inflate(pos==0 ? data : data[pos..-1])
251
283
  if zi.total_in < data.size
252
284
  @extradata << data[zi.total_in..-1]
253
- puts "[?] #{@extradata.last.size} bytes of extra data after zlib stream".red if @verbose >= 1
285
+ STDERR.puts "[?] #{@extradata.last.size} bytes of extra data after zlib stream".red if @verbose >= 1
254
286
  end
255
287
  # decompress OK
256
288
  rescue Zlib::BufError
257
289
  # tried to decompress, but got EOF - need more data
258
- puts "[!] #{$!.inspect}".red if @verbose >= -1
290
+ STDERR.puts "[!] #{$!.inspect}".red if @verbose >= -1
259
291
  # collect any remaining data in decompress buffer
260
292
  r << zi.flush_next_out
261
293
  rescue Zlib::DataError
262
- puts "[!] #{$!.inspect}".red if @verbose >= -1
294
+ STDERR.puts "[!] #{$!.inspect}".red if @verbose >= -1
263
295
  #p [pos, zi.total_in, zi.total_out, data.size, r.size]
264
296
  r << zi.flush_next_out
265
297
  # XXX TODO try to skip error and continue
@@ -273,7 +305,7 @@ module ZPNG
273
305
  # pos = 0
274
306
  # retry if pos < data.size
275
307
  rescue Zlib::NeedDict
276
- puts "[!] #{$!.inspect}".red if @verbose >= -1
308
+ STDERR.puts "[!] #{$!.inspect}".red if @verbose >= -1
277
309
  # collect any remaining data in decompress buffer
278
310
  r << zi.flush_next_out
279
311
  end
@@ -288,7 +320,7 @@ module ZPNG
288
320
  def imagedata
289
321
  @imagedata ||=
290
322
  begin
291
- puts "[?] no image header, assuming non-interlaced RGB".yellow unless header
323
+ STDERR.puts "[?] no image header, assuming non-interlaced RGB".yellow unless header
292
324
  data = _imagedata
293
325
  (data && data.size > 0) ? _safe_inflate(data) : ''
294
326
  end
@@ -7,6 +7,8 @@ module ZPNG
7
7
  FILTER_AVERAGE = 3
8
8
  FILTER_PAETH = 4
9
9
 
10
+ VALID_FILTERS = FILTER_NONE..FILTER_PAETH
11
+
10
12
  attr_accessor :image, :idx, :filter, :offset, :bpp
11
13
  attr_writer :decoded_bytes
12
14
 
@@ -44,6 +46,10 @@ module ZPNG
44
46
  !@filter
45
47
  end
46
48
 
49
+ def valid_filter?
50
+ VALID_FILTERS.include?(@filter)
51
+ end
52
+
47
53
  # total scanline size in bytes, INCLUDING leading 'filter' byte
48
54
  def size
49
55
  @size ||=
@@ -286,7 +292,10 @@ module ZPNG
286
292
  )
287
293
  end
288
294
 
289
- else raise "invalid ScanLine filter #{@filter}"
295
+ else
296
+ STDERR.puts "[!] #{self.class}: ##@idx: invalid filter #@filter, assuming FILTER_NONE".red
297
+ s = raw
298
+ raise
290
299
  end
291
300
 
292
301
  s
data/zpng.gemspec CHANGED
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: zpng 0.3.4 ruby lib
5
+ # stub: zpng 0.4.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "zpng".freeze
9
- s.version = "0.3.4"
9
+ s.version = "0.4.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Andrey \"Zed\" Zaikin".freeze]
14
- s.date = "2022-01-29"
14
+ s.date = "2022-01-31"
15
15
  s.email = "zed.0xff@gmail.com".freeze
16
16
  s.executables = ["zpng".freeze]
17
17
  s.extra_rdoc_files = [
@@ -52,6 +52,7 @@ Gem::Specification.new do |s|
52
52
  "misc/chars.png",
53
53
  "misc/gen_ascii_map.rb",
54
54
  "samples/bad/000000.png",
55
+ "samples/bad/149511457-47db5096-662d-4221-84f9-1c2a0e20e323.png",
55
56
  "samples/bad/b1.png",
56
57
  "samples/captcha_4bpp.png",
57
58
  "samples/cats.png",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zpng
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey "Zed" Zaikin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-29 00:00:00.000000000 Z
11
+ date: 2022-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rainbow
@@ -136,6 +136,7 @@ files:
136
136
  - misc/chars.png
137
137
  - misc/gen_ascii_map.rb
138
138
  - samples/bad/000000.png
139
+ - samples/bad/149511457-47db5096-662d-4221-84f9-1c2a0e20e323.png
139
140
  - samples/bad/b1.png
140
141
  - samples/captcha_4bpp.png
141
142
  - samples/cats.png