png_conform 0.1.2 → 0.1.3

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +116 -6
  3. data/Gemfile +1 -1
  4. data/config/validation_profiles.yml +105 -0
  5. data/lib/png_conform/analyzers/comparison_analyzer.rb +41 -7
  6. data/lib/png_conform/analyzers/metrics_analyzer.rb +6 -9
  7. data/lib/png_conform/analyzers/optimization_analyzer.rb +30 -24
  8. data/lib/png_conform/analyzers/resolution_analyzer.rb +31 -32
  9. data/lib/png_conform/cli.rb +12 -0
  10. data/lib/png_conform/commands/check_command.rb +118 -53
  11. data/lib/png_conform/configuration.rb +147 -0
  12. data/lib/png_conform/container.rb +113 -0
  13. data/lib/png_conform/models/validation_result.rb +30 -4
  14. data/lib/png_conform/pipelines/pipeline_result.rb +39 -0
  15. data/lib/png_conform/pipelines/stages/analysis_stage.rb +35 -0
  16. data/lib/png_conform/pipelines/stages/base_stage.rb +23 -0
  17. data/lib/png_conform/pipelines/stages/chunk_validation_stage.rb +74 -0
  18. data/lib/png_conform/pipelines/stages/sequence_validation_stage.rb +77 -0
  19. data/lib/png_conform/pipelines/stages/signature_validation_stage.rb +41 -0
  20. data/lib/png_conform/pipelines/validation_pipeline.rb +90 -0
  21. data/lib/png_conform/readers/full_load_reader.rb +13 -4
  22. data/lib/png_conform/readers/streaming_reader.rb +27 -2
  23. data/lib/png_conform/reporters/color_reporter.rb +17 -14
  24. data/lib/png_conform/reporters/visual_elements.rb +22 -16
  25. data/lib/png_conform/services/analysis_manager.rb +120 -0
  26. data/lib/png_conform/services/chunk_processor.rb +195 -0
  27. data/lib/png_conform/services/file_signature.rb +226 -0
  28. data/lib/png_conform/services/file_strategy.rb +78 -0
  29. data/lib/png_conform/services/lru_cache.rb +170 -0
  30. data/lib/png_conform/services/parallel_validator.rb +118 -0
  31. data/lib/png_conform/services/profile_manager.rb +41 -12
  32. data/lib/png_conform/services/result_builder.rb +299 -0
  33. data/lib/png_conform/services/validation_cache.rb +210 -0
  34. data/lib/png_conform/services/validation_orchestrator.rb +188 -0
  35. data/lib/png_conform/services/validation_service.rb +53 -337
  36. data/lib/png_conform/services/validator_pool.rb +142 -0
  37. data/lib/png_conform/utils/colorizer.rb +149 -0
  38. data/lib/png_conform/validators/chunk_registry.rb +12 -0
  39. data/lib/png_conform/validators/streaming_idat_validator.rb +123 -0
  40. data/lib/png_conform/version.rb +1 -1
  41. data/png_conform.gemspec +1 -0
  42. metadata +38 -2
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "paint"
4
+
5
+ module PngConform
6
+ module Utils
7
+ # Colorization utility using the Paint gem
8
+ #
9
+ # Provides a centralized colorization service for terminal output.
10
+ # Wraps the Paint gem with semantic method names and caching.
11
+ #
12
+ # @example Basic usage
13
+ # Colorizer.success("Success message")
14
+ # Colorizer.error("Error message")
15
+ # Colorizer.warning("Warning message", bold: true)
16
+ #
17
+ class Colorizer
18
+ class << self
19
+ # Check if colorization is enabled
20
+ #
21
+ # @return [Boolean] true if colors are enabled
22
+ def enabled?
23
+ Paint.mode != 0
24
+ end
25
+
26
+ # Enable or disable colorization
27
+ #
28
+ # @param value [Boolean] true to enable, false to disable
29
+ def enabled=(value)
30
+ Paint.mode = value ? Paint.detect_mode : 0
31
+ end
32
+
33
+ # Colorize text as success (green)
34
+ #
35
+ # @param text [String] Text to colorize
36
+ # @param bold [Boolean] Whether to use bold formatting
37
+ # @return [String] Colorized text
38
+ def success(text, bold: false)
39
+ colorize(text, :green, bold: bold)
40
+ end
41
+
42
+ # Colorize text as error (red)
43
+ #
44
+ # @param text [String] Text to colorize
45
+ # @param bold [Boolean] Whether to use bold formatting
46
+ # @return [String] Colorized text
47
+ def error(text, bold: false)
48
+ colorize(text, :red, bold: bold)
49
+ end
50
+
51
+ # Colorize text as warning (yellow)
52
+ #
53
+ # @param text [String] Text to colorize
54
+ # @param bold [Boolean] Whether to use bold formatting
55
+ # @return [String] Colorized text
56
+ def warning(text, bold: false)
57
+ colorize(text, :yellow, bold: bold)
58
+ end
59
+
60
+ # Colorize text as info (blue)
61
+ #
62
+ # @param text [String] Text to colorize
63
+ # @param bold [Boolean] Whether to use bold formatting
64
+ # @return [String] Colorized text
65
+ def info(text, bold: false)
66
+ colorize(text, :blue, bold: bold)
67
+ end
68
+
69
+ # Colorize text with bold formatting
70
+ #
71
+ # @param text [String] Text to colorize
72
+ # @param color [Symbol] Color to use
73
+ # @return [String] Colorized text
74
+ def bold(text, color = nil)
75
+ return Paint[text, :bold] unless color
76
+
77
+ Paint[text, color, :bold]
78
+ end
79
+
80
+ # Colorize text with a specific color
81
+ #
82
+ # @param text [String] Text to colorize
83
+ # @param color [Symbol] Color to use
84
+ # @param bold [Boolean] Whether to use bold formatting
85
+ # @return [String] Colorized text
86
+ def colorize(text, color, bold: false)
87
+ return text.to_s unless enabled?
88
+
89
+ if bold
90
+ Paint[text, color, :bold]
91
+ else
92
+ Paint[text, color]
93
+ end
94
+ end
95
+
96
+ # Colorize text based on priority
97
+ #
98
+ # @param text [String] Text to colorize
99
+ # @param priority [Symbol] Priority (:high, :medium, :low)
100
+ # @return [String] Colorized text
101
+ def priority(text, priority:)
102
+ case priority
103
+ when :high
104
+ error(text, bold: true)
105
+ when :medium
106
+ warning(text, bold: true)
107
+ when :low
108
+ info(text)
109
+ else
110
+ text.to_s
111
+ end
112
+ end
113
+
114
+ # Get a checkmark symbol (with color)
115
+ #
116
+ # @param success [Boolean] Whether the check should be green
117
+ # @return [String] Colored checkmark
118
+ def checkmark(success: true)
119
+ if success
120
+ success("✓")
121
+ else
122
+ error("✗")
123
+ end
124
+ end
125
+
126
+ # Remove all color codes from text
127
+ #
128
+ # @param text [String] Text to uncolorize
129
+ # @return [String] Plain text
130
+ def uncolorize(text)
131
+ Paint.unpaint(text)
132
+ end
133
+
134
+ # Apply multiple colorizations to different parts of text
135
+ #
136
+ # @param template [String] Template with %{key} placeholders
137
+ # @param substitutions [Hash] Hash of key => [text, color] pairs
138
+ # @return [String] Colorized text
139
+ # @example
140
+ # Colorizer.template("%{greeting} %{name}",
141
+ # greeting: ["Hello", :green],
142
+ # name: ["World", :blue])
143
+ def template(template, substitutions)
144
+ Paint[template, substitutions]
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -189,6 +189,18 @@ module PngConform
189
189
  validator_class.new(chunk, context)
190
190
  end
191
191
 
192
+ # Preload all validators for performance
193
+ #
194
+ # Loads all validator classes into cache at startup to improve
195
+ # validation performance by avoiding lazy loading overhead.
196
+ #
197
+ # @return [void]
198
+ def preload_validators!
199
+ VALIDATOR_PATHS.each_key do |chunk_type|
200
+ validator_for(chunk_type)
201
+ end
202
+ end
203
+
192
204
  private
193
205
 
194
206
  # Cache for loaded validator classes
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../base_validator"
4
+
5
+ module PngConform
6
+ module Validators
7
+ # Streaming IDAT validator for memory-efficient validation
8
+ #
9
+ # This validator processes IDAT chunk data in streaming mode to avoid
10
+ # loading large decompressed image data into memory. Instead of
11
+ # fully decompressing, it validates the zlib stream incrementally.
12
+ #
13
+ class StreamingIdatValidator < BaseValidator
14
+ # Validate IDAT chunk
15
+ #
16
+ # Performs streaming validation of zlib-compressed image data
17
+ # without fully decompressing into memory.
18
+ #
19
+ # @return [void]
20
+ def validate
21
+ return add_error("Invalid IDAT sequence") unless check_idat_sequence
22
+ return add_error("Invalid IDAT order") unless check_idat_order
23
+
24
+ # Validate compression in streaming mode
25
+ validate_streaming_compression
26
+ end
27
+
28
+ private
29
+
30
+ # Check IDAT sequence requirements
31
+ #
32
+ # @return [Boolean] True if IDAT sequence is valid
33
+ def check_idat_sequence
34
+ # IDAT chunks must be consecutive
35
+ previous_chunk_type = @context.seen?("IDAT") ? "IDAT" : nil
36
+
37
+ if previous_chunk_type && previous_chunk_type != "IDAT"
38
+ add_warning("IDAT chunks must be consecutive")
39
+ return false
40
+ end
41
+
42
+ true
43
+ end
44
+
45
+ # Check IDAT order requirements
46
+ #
47
+ # IDAT must come after PLTE (if present) and before IEND
48
+ #
49
+ # @return [Boolean] True if IDAT is in correct position
50
+ def check_idat_order
51
+ # IDAT cannot be first (must have IHDR)
52
+ return false unless @context.seen?("IHDR")
53
+
54
+ # If PLTE exists, IDAT must come after it
55
+ if @context.seen?("PLTE")
56
+ @context.chunks_of_type("PLTE")
57
+ find_chunk_index("PLTE")
58
+ @context.chunks_of_type("IDAT").length
59
+
60
+ # For now, just check that PLTE was seen before any IDAT
61
+ # The actual ordering is validated in sequence validation
62
+ end
63
+
64
+ true
65
+ end
66
+
67
+ # Find the index of a chunk in the validation sequence
68
+ #
69
+ # @param chunk_type [String] Chunk type to find
70
+ # @return [Integer, nil] Index of chunk or nil
71
+ def find_chunk_index(_chunk_type)
72
+ # This would require tracking chunk order in context
73
+ # For now, return nil as this is handled by sequence validation
74
+ nil
75
+ end
76
+
77
+ # Validate compression using streaming zlib validation
78
+ #
79
+ # Instead of fully decompressing the image data (which can be huge),
80
+ # this validates that the compressed data is valid zlib format without
81
+ # loading the decompressed result.
82
+ #
83
+ # @return [void]
84
+ def validate_streaming_compression
85
+ require "zlib"
86
+
87
+ # Try to validate the zlib stream without full decompression
88
+ # We inflate just enough to verify validity
89
+ begin
90
+ inflater = Zlib::Inflate.new(Zlib::MAX_WBITS)
91
+
92
+ # Process data in chunks to avoid large memory allocation
93
+ chunk.data.each_slice(8192) do |slice|
94
+ # << is used to append data to the inflate stream
95
+ # This validates the data format without storing result
96
+ result = inflater << slice
97
+
98
+ # If we got decompressed data back, the stream is valid
99
+ # We don't need to store it, just validate
100
+ if result && !result.empty?
101
+ # Data is valid, we can discard it
102
+ # This means the compressed data is well-formed
103
+ end
104
+ rescue Zlib::DataError => e
105
+ return add_error("Invalid compressed data in IDAT: #{e.message}")
106
+ end
107
+
108
+ # Finish the stream to validate end of data
109
+ inflater.finish
110
+ inflater.close
111
+
112
+ add_info("Streaming compression validation successful")
113
+ rescue Zlib::StreamError => e
114
+ add_error("Invalid zlib stream in IDAT: #{e.message}")
115
+ rescue Zlib::BufError => e
116
+ add_error("Buffer error in IDAT compression: #{e.message}")
117
+ rescue StandardError => e
118
+ add_error("Compression validation error: #{e.message}")
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PngConform
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
data/png_conform.gemspec CHANGED
@@ -39,5 +39,6 @@ Gem::Specification.new do |spec|
39
39
 
40
40
  spec.add_dependency "bindata", "~> 2.5"
41
41
  spec.add_dependency "lutaml-model", "~> 0.7"
42
+ spec.add_dependency "paint", "~> 2.3"
42
43
  spec.add_dependency "thor", "~> 1.4"
43
44
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: png_conform
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-11-23 00:00:00.000000000 Z
11
+ date: 2026-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bindata
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: paint
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.3'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: thor
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -91,6 +105,7 @@ files:
91
105
  - benchmarks/profile_validation.rb
92
106
  - benchmarks/results/.gitkeep
93
107
  - benchmarks/run_benchmark.rb
108
+ - config/validation_profiles.yml
94
109
  - docs/ARCHITECTURE.adoc
95
110
  - docs/CHUNK_TYPES.adoc
96
111
  - docs/CLI_OPTIONS.adoc
@@ -112,6 +127,8 @@ files:
112
127
  - lib/png_conform/cli.rb
113
128
  - lib/png_conform/commands/check_command.rb
114
129
  - lib/png_conform/commands/list_command.rb
130
+ - lib/png_conform/configuration.rb
131
+ - lib/png_conform/container.rb
115
132
  - lib/png_conform/models/chunk.rb
116
133
  - lib/png_conform/models/chunk_info.rb
117
134
  - lib/png_conform/models/compression_info.rb
@@ -121,6 +138,13 @@ files:
121
138
  - lib/png_conform/models/image_info.rb
122
139
  - lib/png_conform/models/validation_error.rb
123
140
  - lib/png_conform/models/validation_result.rb
141
+ - lib/png_conform/pipelines/pipeline_result.rb
142
+ - lib/png_conform/pipelines/stages/analysis_stage.rb
143
+ - lib/png_conform/pipelines/stages/base_stage.rb
144
+ - lib/png_conform/pipelines/stages/chunk_validation_stage.rb
145
+ - lib/png_conform/pipelines/stages/sequence_validation_stage.rb
146
+ - lib/png_conform/pipelines/stages/signature_validation_stage.rb
147
+ - lib/png_conform/pipelines/validation_pipeline.rb
124
148
  - lib/png_conform/readers/full_load_reader.rb
125
149
  - lib/png_conform/readers/streaming_reader.rb
126
150
  - lib/png_conform/reporters/base_reporter.rb
@@ -135,9 +159,20 @@ files:
135
159
  - lib/png_conform/reporters/very_verbose_reporter.rb
136
160
  - lib/png_conform/reporters/visual_elements.rb
137
161
  - lib/png_conform/reporters/yaml_reporter.rb
162
+ - lib/png_conform/services/analysis_manager.rb
163
+ - lib/png_conform/services/chunk_processor.rb
164
+ - lib/png_conform/services/file_signature.rb
165
+ - lib/png_conform/services/file_strategy.rb
166
+ - lib/png_conform/services/lru_cache.rb
167
+ - lib/png_conform/services/parallel_validator.rb
138
168
  - lib/png_conform/services/profile_manager.rb
169
+ - lib/png_conform/services/result_builder.rb
170
+ - lib/png_conform/services/validation_cache.rb
171
+ - lib/png_conform/services/validation_orchestrator.rb
139
172
  - lib/png_conform/services/validation_service.rb
173
+ - lib/png_conform/services/validator_pool.rb
140
174
  - lib/png_conform/services/zlib_validator.rb
175
+ - lib/png_conform/utils/colorizer.rb
141
176
  - lib/png_conform/validators/ancillary/bkgd_validator.rb
142
177
  - lib/png_conform/validators/ancillary/chrm_validator.rb
143
178
  - lib/png_conform/validators/ancillary/cicp_validator.rb
@@ -187,6 +222,7 @@ files:
187
222
  - lib/png_conform/validators/mng/seek_validator.rb
188
223
  - lib/png_conform/validators/mng/show_validator.rb
189
224
  - lib/png_conform/validators/mng/term_validator.rb
225
+ - lib/png_conform/validators/streaming_idat_validator.rb
190
226
  - lib/png_conform/version.rb
191
227
  - png_conform.gemspec
192
228
  homepage: https://github.com/claricle/png_conform