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 +4 -4
- data/Gemfile.lock +3 -3
- data/README.md +5 -3
- data/VERSION +1 -1
- data/lib/zpng/chunk.rb +31 -6
- data/lib/zpng/cli.rb +8 -0
- data/lib/zpng/image.rb +38 -6
- data/lib/zpng/scan_line.rb +9 -1
- data/samples/bad/149511457-47db5096-662d-4221-84f9-1c2a0e20e323.png +0 -0
- data/zpng.gemspec +4 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c97dbfaadf55cefb99220c713c5f7a7d909e11d0350d811f617046bce519a645
|
4
|
+
data.tar.gz: 8e249402ddd8052020c2e61a9c4f130ea8108d362d999c44d3473cbb3ee741f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
63
|
-
mini_portile2 (~> 2.
|
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.
|
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
|
-
|
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
|
data/lib/zpng/scan_line.rb
CHANGED
@@ -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
|
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
|
Binary file
|
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.
|
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.
|
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
|
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.
|
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
|
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
|