zpng 0.3.4 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c80d32f2f6027ae69acc2f9f21142d89fe2d8bb24bd1625d2eae5470df90bc4
4
- data.tar.gz: 76a0239f9c6d80fb7883f9c5e80c325ae74e94b72cf345ac6b0a168291ab75f3
3
+ metadata.gz: c97dbfaadf55cefb99220c713c5f7a7d909e11d0350d811f617046bce519a645
4
+ data.tar.gz: 8e249402ddd8052020c2e61a9c4f130ea8108d362d999c44d3473cbb3ee741f8
5
5
  SHA512:
6
- metadata.gz: 1402123bc6a4fb28084b3f2645b9df50877da1bd157b6e888f4f2bdde301d8d54cf8620a77d2fd6c6e0fc1615f712f14379549576d395a9185f8641c369db8a2
7
- data.tar.gz: 497d257914ba09e8f482a311bf6c499bebb4c09622b1681ba1fab845d76175d4e94163bf05158ef1e7013a080ebc00befd49fec3f6a209d92ef0e50b9df61927
6
+ metadata.gz: bae0bdfcf278b4132d3a5f36c7cba4d7714a6f7a12d637662ece94ef9d48628aa08c92657ff385a5e21c441730d584875f38eb990e76f1daf3b19eee394313cc
7
+ data.tar.gz: de91174b8e0696d325102e6d95231ce3eef147905cc9335a1ff6ca4f37c1beac0c21616ea38351eaf82951f1f9677886b10e66716bb726bda930ca992ae2fc23
data/Gemfile.lock CHANGED
@@ -55,12 +55,12 @@ GEM
55
55
  jwt (2.3.0)
56
56
  kamelcase (0.0.2)
57
57
  semver2 (~> 3)
58
- mini_portile2 (2.7.1)
58
+ mini_portile2 (2.8.0)
59
59
  multi_json (1.15.0)
60
60
  multi_xml (0.6.0)
61
61
  multipart-post (2.1.1)
62
- nokogiri (1.13.1)
63
- mini_portile2 (~> 2.7.0)
62
+ nokogiri (1.13.3)
63
+ mini_portile2 (~> 2.8.0)
64
64
  racc (~> 1.4)
65
65
  oauth2 (1.4.7)
66
66
  faraday (>= 0.8, < 2.0)
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.2
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,9 @@ 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
290
298
  end
291
299
 
292
300
  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.2 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.2"
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-03-01"
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.2
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-03-01 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