png_conform 0.1.1 → 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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +82 -42
- data/Gemfile +2 -0
- data/README.adoc +3 -2
- data/benchmarks/README.adoc +570 -0
- data/benchmarks/config/default.yml +35 -0
- data/benchmarks/config/full.yml +32 -0
- data/benchmarks/config/quick.yml +32 -0
- data/benchmarks/direct_validation.rb +18 -0
- data/benchmarks/lib/benchmark_runner.rb +204 -0
- data/benchmarks/lib/metrics_collector.rb +193 -0
- data/benchmarks/lib/png_conform_runner.rb +68 -0
- data/benchmarks/lib/pngcheck_runner.rb +67 -0
- data/benchmarks/lib/report_generator.rb +301 -0
- data/benchmarks/lib/tool_runner.rb +104 -0
- data/benchmarks/profile_loading.rb +12 -0
- data/benchmarks/profile_validation.rb +18 -0
- data/benchmarks/results/.gitkeep +0 -0
- data/benchmarks/run_benchmark.rb +159 -0
- data/config/validation_profiles.yml +105 -0
- data/docs/CHUNK_TYPES.adoc +42 -0
- data/examples/README.md +282 -0
- data/lib/png_conform/analyzers/comparison_analyzer.rb +41 -7
- data/lib/png_conform/analyzers/metrics_analyzer.rb +6 -9
- data/lib/png_conform/analyzers/optimization_analyzer.rb +30 -24
- data/lib/png_conform/analyzers/resolution_analyzer.rb +31 -32
- data/lib/png_conform/cli.rb +12 -0
- data/lib/png_conform/commands/check_command.rb +118 -52
- data/lib/png_conform/configuration.rb +147 -0
- data/lib/png_conform/container.rb +113 -0
- data/lib/png_conform/models/decoded_chunk_data.rb +33 -0
- data/lib/png_conform/models/validation_result.rb +30 -4
- data/lib/png_conform/pipelines/pipeline_result.rb +39 -0
- data/lib/png_conform/pipelines/stages/analysis_stage.rb +35 -0
- data/lib/png_conform/pipelines/stages/base_stage.rb +23 -0
- data/lib/png_conform/pipelines/stages/chunk_validation_stage.rb +74 -0
- data/lib/png_conform/pipelines/stages/sequence_validation_stage.rb +77 -0
- data/lib/png_conform/pipelines/stages/signature_validation_stage.rb +41 -0
- data/lib/png_conform/pipelines/validation_pipeline.rb +90 -0
- data/lib/png_conform/readers/full_load_reader.rb +13 -4
- data/lib/png_conform/readers/streaming_reader.rb +27 -2
- data/lib/png_conform/reporters/color_reporter.rb +17 -14
- data/lib/png_conform/reporters/reporter_factory.rb +18 -11
- data/lib/png_conform/reporters/visual_elements.rb +22 -16
- data/lib/png_conform/services/analysis_manager.rb +120 -0
- data/lib/png_conform/services/chunk_processor.rb +195 -0
- data/lib/png_conform/services/file_signature.rb +226 -0
- data/lib/png_conform/services/file_strategy.rb +78 -0
- data/lib/png_conform/services/lru_cache.rb +170 -0
- data/lib/png_conform/services/parallel_validator.rb +118 -0
- data/lib/png_conform/services/profile_manager.rb +41 -12
- data/lib/png_conform/services/result_builder.rb +299 -0
- data/lib/png_conform/services/validation_cache.rb +210 -0
- data/lib/png_conform/services/validation_orchestrator.rb +188 -0
- data/lib/png_conform/services/validation_service.rb +82 -321
- data/lib/png_conform/services/validator_pool.rb +142 -0
- data/lib/png_conform/utils/colorizer.rb +149 -0
- data/lib/png_conform/validators/ancillary/idot_validator.rb +102 -0
- data/lib/png_conform/validators/chunk_registry.rb +143 -128
- data/lib/png_conform/validators/streaming_idat_validator.rb +123 -0
- data/lib/png_conform/version.rb +1 -1
- data/lib/png_conform.rb +7 -46
- data/png_conform.gemspec +1 -0
- metadata +55 -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
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../base_validator"
|
|
4
|
+
|
|
5
|
+
module PngConform
|
|
6
|
+
module Validators
|
|
7
|
+
module Ancillary
|
|
8
|
+
# Validator for PNG iDOT (Apple Display Optimization) chunk
|
|
9
|
+
#
|
|
10
|
+
# iDOT is an Apple-specific chunk found in screenshots and images saved
|
|
11
|
+
# from macOS/iOS devices. It contains display optimization data for
|
|
12
|
+
# Retina displays and multi-core decoding performance.
|
|
13
|
+
#
|
|
14
|
+
# Structure (28 bytes - seven 32-bit little-endian integers):
|
|
15
|
+
# - Display scale factor (4 bytes)
|
|
16
|
+
# - Pixel format information (4 bytes)
|
|
17
|
+
# - Color space information (4 bytes)
|
|
18
|
+
# - Backing scale factor (4 bytes)
|
|
19
|
+
# - Flags (4 bytes)
|
|
20
|
+
# - Reserved field 1 (4 bytes)
|
|
21
|
+
# - Reserved field 2 (4 bytes)
|
|
22
|
+
#
|
|
23
|
+
# Validation rules:
|
|
24
|
+
# - Chunk must be exactly 28 bytes
|
|
25
|
+
# - Only one iDOT chunk allowed
|
|
26
|
+
# - Must appear before IDAT chunk
|
|
27
|
+
#
|
|
28
|
+
# References:
|
|
29
|
+
# - Apple's proprietary display optimization format
|
|
30
|
+
# - Used in macOS/iOS screenshot PNG files
|
|
31
|
+
class IdotValidator < BaseValidator
|
|
32
|
+
# Expected chunk length (7 x 4-byte integers)
|
|
33
|
+
EXPECTED_LENGTH = 28
|
|
34
|
+
|
|
35
|
+
# Validate iDOT chunk
|
|
36
|
+
#
|
|
37
|
+
# @return [Boolean] True if validation passed
|
|
38
|
+
def validate
|
|
39
|
+
return false unless check_crc
|
|
40
|
+
return false unless check_length(EXPECTED_LENGTH)
|
|
41
|
+
return false unless check_uniqueness
|
|
42
|
+
return false unless check_position
|
|
43
|
+
|
|
44
|
+
decode_and_store_data
|
|
45
|
+
true
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# Check that only one iDOT chunk exists
|
|
51
|
+
def check_uniqueness
|
|
52
|
+
if context.seen?("iDOT")
|
|
53
|
+
add_error("duplicate iDOT chunk (only one allowed)")
|
|
54
|
+
return false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Check that iDOT appears before IDAT
|
|
61
|
+
def check_position
|
|
62
|
+
if context.seen?("IDAT")
|
|
63
|
+
add_error("iDOT chunk after IDAT (must be before)")
|
|
64
|
+
return false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
true
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Decode iDOT data and store in context
|
|
71
|
+
def decode_and_store_data
|
|
72
|
+
values = chunk.chunk_data.unpack("V7")
|
|
73
|
+
|
|
74
|
+
# Create IdotData model
|
|
75
|
+
idot_data = create_idot_data(values)
|
|
76
|
+
|
|
77
|
+
# Store in context for later use
|
|
78
|
+
context.store(:idot_data, idot_data)
|
|
79
|
+
|
|
80
|
+
# Add info message with decoded data
|
|
81
|
+
add_info("iDOT: Apple display optimization (#{idot_data.detailed_info})")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Create IdotData model from parsed values
|
|
85
|
+
#
|
|
86
|
+
# @param values [Array<Integer>] Seven 32-bit integers
|
|
87
|
+
# @return [Models::IdotData] The decoded data model
|
|
88
|
+
def create_idot_data(values)
|
|
89
|
+
Models::IdotData.new(
|
|
90
|
+
display_scale: values[0],
|
|
91
|
+
pixel_format: values[1],
|
|
92
|
+
color_space: values[2],
|
|
93
|
+
backing_scale_factor: values[3],
|
|
94
|
+
flags: values[4],
|
|
95
|
+
reserved1: values[5],
|
|
96
|
+
reserved2: values[6],
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -1,54 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "critical/ihdr_validator"
|
|
4
|
-
require_relative "critical/plte_validator"
|
|
5
|
-
require_relative "critical/idat_validator"
|
|
6
|
-
require_relative "critical/iend_validator"
|
|
7
|
-
require_relative "ancillary/text_validator"
|
|
8
|
-
require_relative "ancillary/ztxt_validator"
|
|
9
|
-
require_relative "ancillary/itxt_validator"
|
|
10
|
-
require_relative "ancillary/gama_validator"
|
|
11
|
-
require_relative "ancillary/chrm_validator"
|
|
12
|
-
require_relative "ancillary/srgb_validator"
|
|
13
|
-
require_relative "ancillary/sbit_validator"
|
|
14
|
-
require_relative "ancillary/bkgd_validator"
|
|
15
|
-
require_relative "ancillary/iccp_validator"
|
|
16
|
-
require_relative "ancillary/hist_validator"
|
|
17
|
-
require_relative "ancillary/splt_validator"
|
|
18
|
-
require_relative "ancillary/trns_validator"
|
|
19
|
-
require_relative "ancillary/phys_validator"
|
|
20
|
-
require_relative "ancillary/time_validator"
|
|
21
|
-
require_relative "ancillary/offs_validator"
|
|
22
|
-
require_relative "ancillary/pcal_validator"
|
|
23
|
-
require_relative "ancillary/scal_validator"
|
|
24
|
-
require_relative "ancillary/ster_validator"
|
|
25
|
-
require_relative "ancillary/cicp_validator"
|
|
26
|
-
require_relative "ancillary/mdcv_validator"
|
|
27
|
-
require_relative "apng/actl_validator"
|
|
28
|
-
require_relative "apng/fctl_validator"
|
|
29
|
-
require_relative "apng/fdat_validator"
|
|
30
|
-
require_relative "mng/mhdr_validator"
|
|
31
|
-
require_relative "mng/mend_validator"
|
|
32
|
-
require_relative "mng/dhdr_validator"
|
|
33
|
-
require_relative "mng/fram_validator"
|
|
34
|
-
require_relative "mng/defi_validator"
|
|
35
|
-
require_relative "mng/back_validator"
|
|
36
|
-
require_relative "mng/loop_validator"
|
|
37
|
-
require_relative "mng/endl_validator"
|
|
38
|
-
require_relative "mng/term_validator"
|
|
39
|
-
require_relative "mng/save_validator"
|
|
40
|
-
require_relative "mng/seek_validator"
|
|
41
|
-
require_relative "mng/move_validator"
|
|
42
|
-
require_relative "mng/clip_validator"
|
|
43
|
-
require_relative "mng/show_validator"
|
|
44
|
-
require_relative "mng/clon_validator"
|
|
45
|
-
require_relative "mng/disc_validator"
|
|
46
|
-
require_relative "jng/jhdr_validator"
|
|
47
|
-
require_relative "jng/jdat_validator"
|
|
48
|
-
require_relative "jng/jsep_validator"
|
|
49
|
-
|
|
50
3
|
module PngConform
|
|
51
4
|
module Validators
|
|
5
|
+
# Define validator category modules upfront for proper namespace resolution
|
|
6
|
+
module Critical; end
|
|
7
|
+
module Ancillary; end
|
|
8
|
+
module Apng; end
|
|
9
|
+
module Mng; end
|
|
10
|
+
module Jng; end
|
|
11
|
+
|
|
52
12
|
# Registry of chunk types to their corresponding validator classes
|
|
53
13
|
#
|
|
54
14
|
# This class maintains a mapping between PNG chunk type codes and
|
|
@@ -62,85 +22,100 @@ module PngConform
|
|
|
62
22
|
# - Palette support (hIST, sPLT, tRNS)
|
|
63
23
|
# - Metadata (pHYs, tIME, oFFs, pCAL, sCAL, sTER)
|
|
64
24
|
# - PNG 3rd edition (cICP, mDCv)
|
|
25
|
+
# - Apple extensions (iDOT)
|
|
65
26
|
# - APNG (acTL, fcTL, fdAT)
|
|
66
27
|
# - MNG (MHDR, MEND, DHDR, FRAM, DEFI, BACK, LOOP, ENDL, etc.)
|
|
67
28
|
# - JNG (JHDR, JDAT, JSEP)
|
|
68
29
|
#
|
|
30
|
+
# Validators are loaded lazily on-demand to improve startup performance.
|
|
31
|
+
#
|
|
69
32
|
class ChunkRegistry
|
|
70
|
-
# Map of chunk type codes to validator
|
|
71
|
-
|
|
33
|
+
# Map of chunk type codes to validator file paths and class names
|
|
34
|
+
# Format: [file_path, module_path, class_name]
|
|
35
|
+
VALIDATOR_PATHS = {
|
|
72
36
|
# Critical chunks
|
|
73
|
-
"IHDR" => Critical
|
|
74
|
-
"PLTE" => Critical
|
|
75
|
-
"IDAT" => Critical
|
|
76
|
-
"IEND" => Critical
|
|
37
|
+
"IHDR" => ["critical/ihdr_validator", "Critical", "IhdrValidator"],
|
|
38
|
+
"PLTE" => ["critical/plte_validator", "Critical", "PlteValidator"],
|
|
39
|
+
"IDAT" => ["critical/idat_validator", "Critical", "IdatValidator"],
|
|
40
|
+
"IEND" => ["critical/iend_validator", "Critical", "IendValidator"],
|
|
77
41
|
|
|
78
42
|
# Text chunks
|
|
79
|
-
"tEXt" => Ancillary
|
|
80
|
-
"zTXt" => Ancillary
|
|
81
|
-
"iTXt" => Ancillary
|
|
43
|
+
"tEXt" => ["ancillary/text_validator", "Ancillary", "TextValidator"],
|
|
44
|
+
"zTXt" => ["ancillary/ztxt_validator", "Ancillary", "ZtxtValidator"],
|
|
45
|
+
"iTXt" => ["ancillary/itxt_validator", "Ancillary", "ItxtValidator"],
|
|
82
46
|
|
|
83
47
|
# Color management
|
|
84
|
-
"gAMA" => Ancillary
|
|
85
|
-
"cHRM" => Ancillary
|
|
86
|
-
"sRGB" => Ancillary
|
|
87
|
-
"sBIT" => Ancillary
|
|
88
|
-
"bKGD" => Ancillary
|
|
89
|
-
"iCCP" => Ancillary
|
|
48
|
+
"gAMA" => ["ancillary/gama_validator", "Ancillary", "GamaValidator"],
|
|
49
|
+
"cHRM" => ["ancillary/chrm_validator", "Ancillary", "ChrmValidator"],
|
|
50
|
+
"sRGB" => ["ancillary/srgb_validator", "Ancillary", "SrgbValidator"],
|
|
51
|
+
"sBIT" => ["ancillary/sbit_validator", "Ancillary", "SbitValidator"],
|
|
52
|
+
"bKGD" => ["ancillary/bkgd_validator", "Ancillary", "BkgdValidator"],
|
|
53
|
+
"iCCP" => ["ancillary/iccp_validator", "Ancillary", "IccpValidator"],
|
|
90
54
|
|
|
91
55
|
# Palette support
|
|
92
|
-
"hIST" => Ancillary
|
|
93
|
-
"sPLT" => Ancillary
|
|
94
|
-
"tRNS" => Ancillary
|
|
56
|
+
"hIST" => ["ancillary/hist_validator", "Ancillary", "HistValidator"],
|
|
57
|
+
"sPLT" => ["ancillary/splt_validator", "Ancillary", "SpltValidator"],
|
|
58
|
+
"tRNS" => ["ancillary/trns_validator", "Ancillary", "TrnsValidator"],
|
|
95
59
|
|
|
96
60
|
# Metadata
|
|
97
|
-
"pHYs" => Ancillary
|
|
98
|
-
"tIME" => Ancillary
|
|
99
|
-
"oFFs" => Ancillary
|
|
100
|
-
"pCAL" => Ancillary
|
|
101
|
-
"sCAL" => Ancillary
|
|
102
|
-
"sTER" => Ancillary
|
|
61
|
+
"pHYs" => ["ancillary/phys_validator", "Ancillary", "PhysValidator"],
|
|
62
|
+
"tIME" => ["ancillary/time_validator", "Ancillary", "TimeValidator"],
|
|
63
|
+
"oFFs" => ["ancillary/offs_validator", "Ancillary", "OffsValidator"],
|
|
64
|
+
"pCAL" => ["ancillary/pcal_validator", "Ancillary", "PcalValidator"],
|
|
65
|
+
"sCAL" => ["ancillary/scal_validator", "Ancillary", "ScalValidator"],
|
|
66
|
+
"sTER" => ["ancillary/ster_validator", "Ancillary", "SterValidator"],
|
|
103
67
|
|
|
104
68
|
# PNG 3rd edition
|
|
105
|
-
"cICP" => Ancillary
|
|
106
|
-
"mDCv" => Ancillary
|
|
69
|
+
"cICP" => ["ancillary/cicp_validator", "Ancillary", "CicpValidator"],
|
|
70
|
+
"mDCv" => ["ancillary/mdcv_validator", "Ancillary", "MdcvValidator"],
|
|
71
|
+
|
|
72
|
+
# Apple extensions
|
|
73
|
+
"iDOT" => ["ancillary/idot_validator", "Ancillary", "IdotValidator"],
|
|
107
74
|
|
|
108
75
|
# APNG (Animated PNG)
|
|
109
|
-
"acTL" => Apng
|
|
110
|
-
"fcTL" => Apng
|
|
111
|
-
"fdAT" => Apng
|
|
76
|
+
"acTL" => ["apng/actl_validator", "Apng", "ActlValidator"],
|
|
77
|
+
"fcTL" => ["apng/fctl_validator", "Apng", "FctlValidator"],
|
|
78
|
+
"fdAT" => ["apng/fdat_validator", "Apng", "FdatValidator"],
|
|
112
79
|
|
|
113
80
|
# MNG (Multiple-image Network Graphics)
|
|
114
|
-
"MHDR" => Mng
|
|
115
|
-
"MEND" => Mng
|
|
116
|
-
"DHDR" => Mng
|
|
117
|
-
"FRAM" => Mng
|
|
118
|
-
"DEFI" => Mng
|
|
119
|
-
"BACK" => Mng
|
|
120
|
-
"LOOP" => Mng
|
|
121
|
-
"ENDL" => Mng
|
|
122
|
-
"TERM" => Mng
|
|
123
|
-
"SAVE" => Mng
|
|
124
|
-
"SEEK" => Mng
|
|
125
|
-
"MOVE" => Mng
|
|
126
|
-
"CLIP" => Mng
|
|
127
|
-
"SHOW" => Mng
|
|
128
|
-
"CLON" => Mng
|
|
129
|
-
"DISC" => Mng
|
|
81
|
+
"MHDR" => ["mng/mhdr_validator", "Mng", "MhdrValidator"],
|
|
82
|
+
"MEND" => ["mng/mend_validator", "Mng", "MendValidator"],
|
|
83
|
+
"DHDR" => ["mng/dhdr_validator", "Mng", "DhdrValidator"],
|
|
84
|
+
"FRAM" => ["mng/fram_validator", "Mng", "FramValidator"],
|
|
85
|
+
"DEFI" => ["mng/defi_validator", "Mng", "DefiValidator"],
|
|
86
|
+
"BACK" => ["mng/back_validator", "Mng", "BackValidator"],
|
|
87
|
+
"LOOP" => ["mng/loop_validator", "Mng", "LoopValidator"],
|
|
88
|
+
"ENDL" => ["mng/endl_validator", "Mng", "EndlValidator"],
|
|
89
|
+
"TERM" => ["mng/term_validator", "Mng", "TermValidator"],
|
|
90
|
+
"SAVE" => ["mng/save_validator", "Mng", "SaveValidator"],
|
|
91
|
+
"SEEK" => ["mng/seek_validator", "Mng", "SeekValidator"],
|
|
92
|
+
"MOVE" => ["mng/move_validator", "Mng", "MoveValidator"],
|
|
93
|
+
"CLIP" => ["mng/clip_validator", "Mng", "ClipValidator"],
|
|
94
|
+
"SHOW" => ["mng/show_validator", "Mng", "ShowValidator"],
|
|
95
|
+
"CLON" => ["mng/clon_validator", "Mng", "ClonValidator"],
|
|
96
|
+
"DISC" => ["mng/disc_validator", "Mng", "DiscValidator"],
|
|
130
97
|
|
|
131
98
|
# JNG (JPEG Network Graphics)
|
|
132
|
-
"JHDR" => Jng
|
|
133
|
-
"JDAT" => Jng
|
|
134
|
-
"JSEP" => Jng
|
|
99
|
+
"JHDR" => ["jng/jhdr_validator", "Jng", "JhdrValidator"],
|
|
100
|
+
"JDAT" => ["jng/jdat_validator", "Jng", "JdatValidator"],
|
|
101
|
+
"JSEP" => ["jng/jsep_validator", "Jng", "JsepValidator"],
|
|
135
102
|
}.freeze
|
|
136
103
|
|
|
137
104
|
class << self
|
|
138
|
-
# Get validator class for a chunk type
|
|
105
|
+
# Get validator class for a chunk type (with lazy loading)
|
|
139
106
|
#
|
|
140
107
|
# @param chunk_type [String] Four-character chunk type code
|
|
141
108
|
# @return [Class, nil] Validator class or nil if not found
|
|
142
109
|
def validator_for(chunk_type)
|
|
143
|
-
|
|
110
|
+
# Return cached validator if already loaded
|
|
111
|
+
return loaded_validators[chunk_type] if loaded_validators.key?(chunk_type)
|
|
112
|
+
|
|
113
|
+
# Check if validator path exists
|
|
114
|
+
validator_info = VALIDATOR_PATHS[chunk_type]
|
|
115
|
+
return nil unless validator_info
|
|
116
|
+
|
|
117
|
+
# Load validator on-demand
|
|
118
|
+
load_validator(chunk_type, validator_info)
|
|
144
119
|
end
|
|
145
120
|
|
|
146
121
|
# Check if a validator exists for a chunk type
|
|
@@ -148,14 +123,14 @@ module PngConform
|
|
|
148
123
|
# @param chunk_type [String] Four-character chunk type code
|
|
149
124
|
# @return [Boolean] True if validator exists
|
|
150
125
|
def validator_exists?(chunk_type)
|
|
151
|
-
|
|
126
|
+
VALIDATOR_PATHS.key?(chunk_type)
|
|
152
127
|
end
|
|
153
128
|
|
|
154
129
|
# Get all registered chunk types
|
|
155
130
|
#
|
|
156
131
|
# @return [Array<String>] List of chunk type codes
|
|
157
132
|
def chunk_types
|
|
158
|
-
|
|
133
|
+
VALIDATOR_PATHS.keys
|
|
159
134
|
end
|
|
160
135
|
|
|
161
136
|
# Get validators by category
|
|
@@ -164,34 +139,34 @@ module PngConform
|
|
|
164
139
|
# (:critical, :text, :color, :palette, :metadata, :png3)
|
|
165
140
|
# @return [Hash] Map of chunk types to validators in category
|
|
166
141
|
def validators_by_category(category)
|
|
167
|
-
case category
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
142
|
+
chunk_types = case category
|
|
143
|
+
when :critical
|
|
144
|
+
%w[IHDR PLTE IDAT IEND]
|
|
145
|
+
when :text
|
|
146
|
+
%w[tEXt zTXt iTXt]
|
|
147
|
+
when :color
|
|
148
|
+
%w[gAMA cHRM sRGB sBIT bKGD iCCP]
|
|
149
|
+
when :palette
|
|
150
|
+
%w[hIST sPLT tRNS]
|
|
151
|
+
when :metadata
|
|
152
|
+
%w[pHYs tIME oFFs pCAL sCAL sTER]
|
|
153
|
+
when :png3
|
|
154
|
+
%w[cICP mDCv]
|
|
155
|
+
when :apng
|
|
156
|
+
%w[acTL fcTL fdAT]
|
|
157
|
+
when :mng
|
|
158
|
+
%w[MHDR MEND DHDR FRAM DEFI BACK LOOP ENDL TERM SAVE SEEK
|
|
159
|
+
MOVE CLIP SHOW CLON DISC]
|
|
160
|
+
when :jng
|
|
161
|
+
%w[JHDR JDAT JSEP]
|
|
162
|
+
else
|
|
163
|
+
[]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Load validators for this category
|
|
167
|
+
chunk_types.each_with_object({}) do |chunk_type, result|
|
|
168
|
+
validator = validator_for(chunk_type)
|
|
169
|
+
result[chunk_type] = validator if validator
|
|
195
170
|
end
|
|
196
171
|
end
|
|
197
172
|
|
|
@@ -199,7 +174,7 @@ module PngConform
|
|
|
199
174
|
#
|
|
200
175
|
# @return [Integer] Number of registered validators
|
|
201
176
|
def count
|
|
202
|
-
|
|
177
|
+
VALIDATOR_PATHS.size
|
|
203
178
|
end
|
|
204
179
|
|
|
205
180
|
# Create validator instance for a chunk
|
|
@@ -213,6 +188,46 @@ module PngConform
|
|
|
213
188
|
|
|
214
189
|
validator_class.new(chunk, context)
|
|
215
190
|
end
|
|
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
|
+
|
|
204
|
+
private
|
|
205
|
+
|
|
206
|
+
# Cache for loaded validator classes
|
|
207
|
+
def loaded_validators
|
|
208
|
+
@loaded_validators ||= {}
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Load a validator class on-demand
|
|
212
|
+
#
|
|
213
|
+
# @param chunk_type [String] Chunk type code
|
|
214
|
+
# @param validator_info [Array] [file_path, module_name, class_name]
|
|
215
|
+
# @return [Class, nil] Loaded validator class
|
|
216
|
+
def load_validator(chunk_type, validator_info)
|
|
217
|
+
file_path, module_name, class_name = validator_info
|
|
218
|
+
|
|
219
|
+
# Require the validator file
|
|
220
|
+
require_relative file_path
|
|
221
|
+
|
|
222
|
+
# Resolve the constant (e.g., Critical::IhdrValidator)
|
|
223
|
+
validator_class = Validators.const_get(module_name).const_get(class_name)
|
|
224
|
+
|
|
225
|
+
# Cache and return
|
|
226
|
+
loaded_validators[chunk_type] = validator_class
|
|
227
|
+
rescue NameError, LoadError => e
|
|
228
|
+
warn "Failed to load validator for #{chunk_type}: #{e.message}"
|
|
229
|
+
loaded_validators[chunk_type] = nil
|
|
230
|
+
end
|
|
216
231
|
end
|
|
217
232
|
end
|
|
218
233
|
end
|