tessel 0.2.0 → 0.2.1
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/.codespellignore +1 -0
- data/.yamllint +16 -0
- data/CHANGELOG.md +4 -1
- data/lib/tessel/version.rb +1 -1
- data/lib/tessel.rb +30 -6
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 57081a7b90e42e245d8ef8122d836932f1064a7d3a77b6215572e686c2df4bb0
|
|
4
|
+
data.tar.gz: 46d5bfddf74bab962b5a6214f2c9d2ccc97ccc5c8343de81fa319c8f652f107e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8d8ebb312c5b64c97ecf15633adabf33ad64d4bddd225541002e6a4194cf2bc0da786c6b668c3ace818e493eb7216d88f0bd9ab09fbca3e30227b8dd754197ac
|
|
7
|
+
data.tar.gz: 1a805ae47035578ffdc854e39185fda0f025bde5b9169166a035f38bd406006fac6eb815911cca511e0081664cd8979af0b65315b49583ddc853e244184f0303
|
data/.codespellignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
paeth
|
data/.yamllint
ADDED
data/CHANGELOG.md
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.2.1] - 2026-09-26
|
|
6
|
+
|
|
7
|
+
- Limit decoded PNG text metadata to prevent excessive memory use.
|
|
8
|
+
|
|
5
9
|
## [0.2.0] - 2026-09-25
|
|
6
10
|
|
|
7
11
|
- Add shared median-cut quantization and indexed color dithering.
|
|
8
12
|
- Add fixed 256-color and web-safe palettes.
|
|
9
|
-
- Update gem author metadata.
|
|
10
13
|
|
|
11
14
|
## [0.1.0] - 2026-09-23
|
|
12
15
|
|
data/lib/tessel/version.rb
CHANGED
data/lib/tessel.rb
CHANGED
|
@@ -422,10 +422,12 @@ module Tessel
|
|
|
422
422
|
module_function
|
|
423
423
|
|
|
424
424
|
ADAM7 = [[0, 0, 8, 8], [4, 0, 8, 8], [0, 4, 4, 8], [2, 0, 4, 4], [0, 2, 2, 4], [1, 0, 2, 2], [0, 1, 1, 2]].freeze
|
|
425
|
+
MAX_METADATA_BYTES = 1_048_576
|
|
425
426
|
|
|
426
|
-
def decode(bytes, max_pixels: 16384 * 16384, verify_crc: true)
|
|
427
|
+
def decode(bytes, max_pixels: 16384 * 16384, max_metadata_bytes: MAX_METADATA_BYTES, verify_crc: true)
|
|
427
428
|
bytes = String(bytes).b
|
|
428
429
|
raise DecodeError, "invalid PNG signature" unless bytes.start_with?(SIGNATURE)
|
|
430
|
+
raise ArgumentError, "max_metadata_bytes must be non-negative" unless max_metadata_bytes.is_a?(Integer) && !max_metadata_bytes.negative?
|
|
429
431
|
|
|
430
432
|
chunks = []
|
|
431
433
|
offset = SIGNATURE.bytesize
|
|
@@ -472,12 +474,12 @@ module Tessel
|
|
|
472
474
|
idat_ended = true
|
|
473
475
|
end
|
|
474
476
|
end
|
|
475
|
-
parse(chunks, max_pixels: max_pixels)
|
|
477
|
+
parse(chunks, max_pixels: max_pixels, max_metadata_bytes: max_metadata_bytes)
|
|
476
478
|
rescue Zlib::Error => e
|
|
477
479
|
raise DecodeError, "invalid PNG data: #{e.message}"
|
|
478
480
|
end
|
|
479
481
|
|
|
480
|
-
def parse(chunks, max_pixels:)
|
|
482
|
+
def parse(chunks, max_pixels:, max_metadata_bytes:)
|
|
481
483
|
raise DecodeError, "PNG is missing IHDR" unless chunks.first&.first == "IHDR"
|
|
482
484
|
raise DecodeError, "invalid IHDR length" unless chunks.first[1].bytesize == 13
|
|
483
485
|
width, height, bit_depth, color_type, compression, filter_method, interlace = chunks.first[1].unpack("N2C5")
|
|
@@ -561,7 +563,7 @@ module Tessel
|
|
|
561
563
|
end
|
|
562
564
|
end
|
|
563
565
|
end
|
|
564
|
-
Image.from_rgba(width, height, rgba, metadata: parse_metadata(chunks))
|
|
566
|
+
Image.from_rgba(width, height, rgba, metadata: parse_metadata(chunks, max_metadata_bytes))
|
|
565
567
|
end
|
|
566
568
|
private_class_method :parse
|
|
567
569
|
|
|
@@ -620,12 +622,14 @@ module Tessel
|
|
|
620
622
|
end
|
|
621
623
|
private_class_method :pixel_rgba
|
|
622
624
|
|
|
623
|
-
def parse_metadata(chunks)
|
|
625
|
+
def parse_metadata(chunks, remaining)
|
|
624
626
|
chunks.each_with_object({}) do |(type, data), metadata|
|
|
625
627
|
case type
|
|
626
628
|
when "tEXt"
|
|
627
629
|
key, value = data.split("\0", 2)
|
|
628
630
|
raise DecodeError, "invalid PNG tEXt chunk" unless key && !key.empty? && value
|
|
631
|
+
raise LimitError, "PNG metadata exceeds limit" if key.bytesize + value.bytesize > remaining
|
|
632
|
+
remaining -= key.bytesize + value.bytesize
|
|
629
633
|
metadata[key.force_encoding("ISO-8859-1").encode("UTF-8")] = value.force_encoding("ISO-8859-1").encode("UTF-8")
|
|
630
634
|
when "iTXt"
|
|
631
635
|
key, rest = data.split("\0", 2)
|
|
@@ -637,7 +641,10 @@ module Tessel
|
|
|
637
641
|
translated_end = text.index("\0", language_end + 1) if language_end
|
|
638
642
|
raise DecodeError, "invalid PNG iTXt chunk" unless translated_end
|
|
639
643
|
value = text.byteslice(translated_end + 1..)
|
|
640
|
-
|
|
644
|
+
raise LimitError, "PNG metadata exceeds limit" if key.bytesize > remaining
|
|
645
|
+
value = inflate_metadata(value, remaining - key.bytesize) if compressed == 1
|
|
646
|
+
raise LimitError, "PNG metadata exceeds limit" if value.bytesize > remaining - key.bytesize
|
|
647
|
+
remaining -= key.bytesize + value.bytesize
|
|
641
648
|
value.force_encoding("UTF-8")
|
|
642
649
|
raise DecodeError, "invalid PNG iTXt text" unless value.valid_encoding?
|
|
643
650
|
key.force_encoding("UTF-8")
|
|
@@ -647,6 +654,23 @@ module Tessel
|
|
|
647
654
|
end
|
|
648
655
|
end
|
|
649
656
|
private_class_method :parse_metadata
|
|
657
|
+
|
|
658
|
+
def inflate_metadata(data, limit)
|
|
659
|
+
output = "".b
|
|
660
|
+
inflater = Zlib::Inflate.new
|
|
661
|
+
append = lambda do |part|
|
|
662
|
+
raise LimitError, "PNG metadata exceeds limit" if part.bytesize > limit - output.bytesize
|
|
663
|
+
output << part
|
|
664
|
+
end
|
|
665
|
+
begin
|
|
666
|
+
(0...data.bytesize).step(1024) { |offset| inflater.inflate(data.byteslice(offset, 1024), &append) }
|
|
667
|
+
inflater.finish(&append)
|
|
668
|
+
ensure
|
|
669
|
+
inflater.close
|
|
670
|
+
end
|
|
671
|
+
output
|
|
672
|
+
end
|
|
673
|
+
private_class_method :inflate_metadata
|
|
650
674
|
end
|
|
651
675
|
|
|
652
676
|
module_function
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tessel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yudai Takada
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A dependency-light PNG reader/writer and shared RGBA8 pixel surface.
|
|
14
14
|
email:
|
|
@@ -17,6 +17,8 @@ executables: []
|
|
|
17
17
|
extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
|
19
19
|
files:
|
|
20
|
+
- ".codespellignore"
|
|
21
|
+
- ".yamllint"
|
|
20
22
|
- CHANGELOG.md
|
|
21
23
|
- LICENSE.txt
|
|
22
24
|
- README.md
|