png_conform 0.1.0

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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +19 -0
  4. data/.rubocop_todo.yml +197 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/CONTRIBUTING.md +323 -0
  7. data/Gemfile +13 -0
  8. data/LICENSE +43 -0
  9. data/README.adoc +859 -0
  10. data/Rakefile +10 -0
  11. data/SECURITY.md +147 -0
  12. data/docs/ARCHITECTURE.adoc +681 -0
  13. data/docs/CHUNK_TYPES.adoc +450 -0
  14. data/docs/CLI_OPTIONS.adoc +913 -0
  15. data/docs/COMPATIBILITY.adoc +616 -0
  16. data/examples/README.adoc +398 -0
  17. data/examples/advanced_usage.rb +304 -0
  18. data/examples/basic_usage.rb +210 -0
  19. data/exe/png_conform +6 -0
  20. data/lib/png_conform/analyzers/comparison_analyzer.rb +230 -0
  21. data/lib/png_conform/analyzers/metrics_analyzer.rb +176 -0
  22. data/lib/png_conform/analyzers/optimization_analyzer.rb +190 -0
  23. data/lib/png_conform/analyzers/resolution_analyzer.rb +274 -0
  24. data/lib/png_conform/bindata/chunk_structure.rb +153 -0
  25. data/lib/png_conform/bindata/jng_file.rb +79 -0
  26. data/lib/png_conform/bindata/mng_file.rb +97 -0
  27. data/lib/png_conform/bindata/png_file.rb +162 -0
  28. data/lib/png_conform/cli.rb +116 -0
  29. data/lib/png_conform/commands/check_command.rb +323 -0
  30. data/lib/png_conform/commands/list_command.rb +67 -0
  31. data/lib/png_conform/models/chunk.rb +84 -0
  32. data/lib/png_conform/models/chunk_info.rb +71 -0
  33. data/lib/png_conform/models/compression_info.rb +49 -0
  34. data/lib/png_conform/models/decoded_chunk_data.rb +143 -0
  35. data/lib/png_conform/models/file_analysis.rb +181 -0
  36. data/lib/png_conform/models/file_info.rb +91 -0
  37. data/lib/png_conform/models/image_info.rb +52 -0
  38. data/lib/png_conform/models/validation_error.rb +89 -0
  39. data/lib/png_conform/models/validation_result.rb +137 -0
  40. data/lib/png_conform/readers/full_load_reader.rb +113 -0
  41. data/lib/png_conform/readers/streaming_reader.rb +180 -0
  42. data/lib/png_conform/reporters/base_reporter.rb +53 -0
  43. data/lib/png_conform/reporters/color_reporter.rb +65 -0
  44. data/lib/png_conform/reporters/json_reporter.rb +18 -0
  45. data/lib/png_conform/reporters/palette_reporter.rb +48 -0
  46. data/lib/png_conform/reporters/quiet_reporter.rb +18 -0
  47. data/lib/png_conform/reporters/reporter_factory.rb +108 -0
  48. data/lib/png_conform/reporters/summary_reporter.rb +65 -0
  49. data/lib/png_conform/reporters/text_reporter.rb +66 -0
  50. data/lib/png_conform/reporters/verbose_reporter.rb +87 -0
  51. data/lib/png_conform/reporters/very_verbose_reporter.rb +33 -0
  52. data/lib/png_conform/reporters/visual_elements.rb +66 -0
  53. data/lib/png_conform/reporters/yaml_reporter.rb +18 -0
  54. data/lib/png_conform/services/profile_manager.rb +242 -0
  55. data/lib/png_conform/services/validation_service.rb +457 -0
  56. data/lib/png_conform/services/zlib_validator.rb +270 -0
  57. data/lib/png_conform/validators/ancillary/bkgd_validator.rb +140 -0
  58. data/lib/png_conform/validators/ancillary/chrm_validator.rb +178 -0
  59. data/lib/png_conform/validators/ancillary/cicp_validator.rb +202 -0
  60. data/lib/png_conform/validators/ancillary/gama_validator.rb +105 -0
  61. data/lib/png_conform/validators/ancillary/hist_validator.rb +147 -0
  62. data/lib/png_conform/validators/ancillary/iccp_validator.rb +243 -0
  63. data/lib/png_conform/validators/ancillary/itxt_validator.rb +280 -0
  64. data/lib/png_conform/validators/ancillary/mdcv_validator.rb +201 -0
  65. data/lib/png_conform/validators/ancillary/offs_validator.rb +132 -0
  66. data/lib/png_conform/validators/ancillary/pcal_validator.rb +289 -0
  67. data/lib/png_conform/validators/ancillary/phys_validator.rb +107 -0
  68. data/lib/png_conform/validators/ancillary/sbit_validator.rb +176 -0
  69. data/lib/png_conform/validators/ancillary/scal_validator.rb +180 -0
  70. data/lib/png_conform/validators/ancillary/splt_validator.rb +223 -0
  71. data/lib/png_conform/validators/ancillary/srgb_validator.rb +117 -0
  72. data/lib/png_conform/validators/ancillary/ster_validator.rb +111 -0
  73. data/lib/png_conform/validators/ancillary/text_validator.rb +129 -0
  74. data/lib/png_conform/validators/ancillary/time_validator.rb +132 -0
  75. data/lib/png_conform/validators/ancillary/trns_validator.rb +154 -0
  76. data/lib/png_conform/validators/ancillary/ztxt_validator.rb +173 -0
  77. data/lib/png_conform/validators/apng/actl_validator.rb +81 -0
  78. data/lib/png_conform/validators/apng/fctl_validator.rb +155 -0
  79. data/lib/png_conform/validators/apng/fdat_validator.rb +117 -0
  80. data/lib/png_conform/validators/base_validator.rb +241 -0
  81. data/lib/png_conform/validators/chunk_registry.rb +219 -0
  82. data/lib/png_conform/validators/critical/idat_validator.rb +77 -0
  83. data/lib/png_conform/validators/critical/iend_validator.rb +68 -0
  84. data/lib/png_conform/validators/critical/ihdr_validator.rb +160 -0
  85. data/lib/png_conform/validators/critical/plte_validator.rb +120 -0
  86. data/lib/png_conform/validators/jng/jdat_validator.rb +66 -0
  87. data/lib/png_conform/validators/jng/jhdr_validator.rb +116 -0
  88. data/lib/png_conform/validators/jng/jsep_validator.rb +66 -0
  89. data/lib/png_conform/validators/mng/back_validator.rb +87 -0
  90. data/lib/png_conform/validators/mng/clip_validator.rb +65 -0
  91. data/lib/png_conform/validators/mng/clon_validator.rb +45 -0
  92. data/lib/png_conform/validators/mng/defi_validator.rb +104 -0
  93. data/lib/png_conform/validators/mng/dhdr_validator.rb +104 -0
  94. data/lib/png_conform/validators/mng/disc_validator.rb +44 -0
  95. data/lib/png_conform/validators/mng/endl_validator.rb +65 -0
  96. data/lib/png_conform/validators/mng/fram_validator.rb +91 -0
  97. data/lib/png_conform/validators/mng/loop_validator.rb +75 -0
  98. data/lib/png_conform/validators/mng/mend_validator.rb +31 -0
  99. data/lib/png_conform/validators/mng/mhdr_validator.rb +69 -0
  100. data/lib/png_conform/validators/mng/move_validator.rb +61 -0
  101. data/lib/png_conform/validators/mng/save_validator.rb +39 -0
  102. data/lib/png_conform/validators/mng/seek_validator.rb +42 -0
  103. data/lib/png_conform/validators/mng/show_validator.rb +52 -0
  104. data/lib/png_conform/validators/mng/term_validator.rb +84 -0
  105. data/lib/png_conform/version.rb +5 -0
  106. data/lib/png_conform.rb +101 -0
  107. data/png_conform.gemspec +43 -0
  108. metadata +201 -0
@@ -0,0 +1,398 @@
1
+ = PngConform Examples
2
+
3
+ == Purpose
4
+
5
+ This directory contains example scripts demonstrating how to use PngConform in
6
+ various scenarios for different use cases and integration patterns.
7
+
8
+ == Installation
9
+
10
+ All examples are executable Ruby scripts. Ensure PngConform is installed:
11
+
12
+ [source,shell]
13
+ ----
14
+ gem install png_conform
15
+ ----
16
+
17
+ Or with Bundler:
18
+
19
+ [source,shell]
20
+ ----
21
+ bundle install
22
+ ----
23
+
24
+ == Available examples
25
+
26
+ === Basic usage
27
+
28
+ ==== General
29
+
30
+ The basic usage examples demonstrate fundamental PngConform operations suitable
31
+ for common validation tasks.
32
+
33
+ **File**: link:basic_usage.rb[`basic_usage.rb`]
34
+
35
+ **Run it**:
36
+
37
+ [source,shell]
38
+ ----
39
+ ruby examples/basic_usage.rb path/to/image.png
40
+ ruby examples/basic_usage.rb path/to/image.png path/to/png_directory
41
+ ----
42
+
43
+ ==== Features demonstrated
44
+
45
+ . Basic file validation
46
+ . Profile-based validation
47
+ . Detailed chunk inspection
48
+ . Batch validation of multiple files
49
+ . Exporting results to YAML/JSON
50
+
51
+ === Advanced usage
52
+
53
+ ==== General
54
+
55
+ The advanced usage examples demonstrate integration patterns and optimization
56
+ techniques for production applications.
57
+
58
+ **File**: link:advanced_usage.rb[`advanced_usage.rb`]
59
+
60
+ **Run it**:
61
+
62
+ [source,shell]
63
+ ----
64
+ ruby examples/advanced_usage.rb path/to/image.png
65
+ ruby examples/advanced_usage.rb file1.png file2.png file3.png
66
+ ----
67
+
68
+ ==== Features demonstrated
69
+
70
+ . Creating custom reporters
71
+ . Working with validators directly
72
+ . Comparing streaming vs full-load modes
73
+ . Profile comparison across all profiles
74
+ . Error handling best practices
75
+ . Extracting metadata from chunks
76
+ . Performance monitoring
77
+
78
+ == Common use cases
79
+
80
+ === Validate a single file
81
+
82
+ [example]
83
+ ====
84
+ [source,ruby]
85
+ ----
86
+ require "png_conform"
87
+
88
+ service = PngConform::Services::ValidationService.new
89
+ result = service.validate_file("image.png")
90
+
91
+ puts result.valid? ? "Valid PNG" : "Invalid PNG"
92
+ ----
93
+ ====
94
+
95
+ === Batch validation
96
+
97
+ [example]
98
+ ====
99
+ [source,ruby]
100
+ ----
101
+ Dir.glob("images/*.png").each do |file|
102
+ result = service.validate_file(file)
103
+ puts "#{file}: #{result.valid? ? '✓' : '✗'}"
104
+ end
105
+ ----
106
+ ====
107
+
108
+ === Profile validation
109
+
110
+ [example]
111
+ ====
112
+ [source,ruby]
113
+ ----
114
+ profile_manager = PngConform::Services::ProfileManager.new
115
+ profile = profile_manager.load_profile("web")
116
+
117
+ result = service.validate_file("image.png", profile: profile)
118
+ ----
119
+ ====
120
+
121
+ === Extract metadata
122
+
123
+ [example]
124
+ ====
125
+ [source,ruby]
126
+ ----
127
+ result = service.validate_file("image.png")
128
+
129
+ # Get image dimensions from IHDR
130
+ ihdr = result.ihdr_chunk
131
+ if ihdr && ihdr.data && ihdr.data.bytesize >= 8
132
+ width = ihdr.data.bytes[0..3].pack("C*").unpack1("N")
133
+ height = ihdr.data.bytes[4..7].pack("C*").unpack1("N")
134
+ puts "#{width}x#{height}"
135
+ end
136
+
137
+ # Get chunk information
138
+ result.chunks.each do |chunk|
139
+ puts "#{chunk.type}: #{chunk.length} bytes"
140
+ end
141
+ ----
142
+ ====
143
+
144
+ === Handle errors
145
+
146
+ [example]
147
+ ====
148
+ [source,ruby]
149
+ ----
150
+ begin
151
+ result = service.validate_file("image.png")
152
+ rescue PngConform::ParseError => e
153
+ puts "File is corrupted: #{e.message}"
154
+ rescue PngConform::Error => e
155
+ puts "Validation error: #{e.message}"
156
+ end
157
+ ----
158
+ ====
159
+
160
+ == Integration patterns
161
+
162
+ === Web application
163
+
164
+ ==== General
165
+
166
+ Integration pattern for web applications like Rails or Sinatra.
167
+
168
+ ==== Implementation
169
+
170
+ [example]
171
+ ====
172
+ [source,ruby]
173
+ ----
174
+ # In a Rails/Sinatra controller
175
+ def validate_upload
176
+ uploaded_file = params[:file]
177
+
178
+ # Save to temporary location
179
+ temp_path = "/tmp/#{SecureRandom.hex}.png"
180
+ File.write(temp_path, uploaded_file.read)
181
+
182
+ # Validate
183
+ service = PngConform::Services::ValidationService.new
184
+ result = service.validate_file(temp_path)
185
+
186
+ # Clean up
187
+ File.delete(temp_path)
188
+
189
+ # Return result
190
+ render json: {
191
+ valid: result.valid?,
192
+ errors: result.errors.map(&:message)
193
+ }
194
+ end
195
+ ----
196
+ ====
197
+
198
+ === Background job
199
+
200
+ ==== General
201
+
202
+ Integration pattern for background job processing with Sidekiq or ActiveJob.
203
+
204
+ ==== Implementation
205
+
206
+ [example]
207
+ ====
208
+ [source,ruby]
209
+ ----
210
+ # In a Sidekiq/ActiveJob worker
211
+ class PngValidationJob < ApplicationJob
212
+ def perform(file_path)
213
+ service = PngConform::Services::ValidationService.new
214
+ result = service.validate_file(file_path, streaming: true)
215
+
216
+ if result.valid?
217
+ # Process valid file
218
+ ProcessImageJob.perform_later(file_path)
219
+ else
220
+ # Handle invalid file
221
+ NotifyUserJob.perform_later(user_id, result.errors)
222
+ end
223
+ end
224
+ end
225
+ ----
226
+ ====
227
+
228
+ === Command line tool
229
+
230
+ ==== General
231
+
232
+ Creating a custom validation script for specific workflows.
233
+
234
+ ==== Implementation
235
+
236
+ [example]
237
+ ====
238
+ [source,ruby]
239
+ ----
240
+ #!/usr/bin/env ruby
241
+ # Custom validation script
242
+
243
+ require "png_conform"
244
+
245
+ ARGV.each do |file|
246
+ service = PngConform::Services::ValidationService.new
247
+ result = service.validate_file(file)
248
+
249
+ status = result.valid? ? "PASS" : "FAIL"
250
+ puts "#{status}: #{file}"
251
+
252
+ unless result.valid?
253
+ result.errors.each do |error|
254
+ puts " #{error.severity}: #{error.message}"
255
+ end
256
+ end
257
+ end
258
+ ----
259
+ ====
260
+
261
+ == Performance tips
262
+
263
+ === Large files
264
+
265
+ ==== General
266
+
267
+ For files larger than 50MB, use streaming mode to optimize memory usage.
268
+
269
+ ==== Usage
270
+
271
+ [example]
272
+ ====
273
+ [source,ruby]
274
+ ----
275
+ result = service.validate_file("large.png", streaming: true)
276
+ ----
277
+ ====
278
+
279
+ === Batch processing
280
+
281
+ ==== General
282
+
283
+ Process multiple files in parallel using threads for improved performance.
284
+
285
+ ==== Implementation
286
+
287
+ [example]
288
+ ====
289
+ [source,ruby]
290
+ ----
291
+ require "concurrent"
292
+
293
+ files = Dir.glob("images/*.png")
294
+ pool = Concurrent::FixedThreadPool.new(4)
295
+
296
+ files.each do |file|
297
+ pool.post do
298
+ result = service.validate_file(file)
299
+ # Process result...
300
+ end
301
+ end
302
+
303
+ pool.shutdown
304
+ pool.wait_for_termination
305
+ ----
306
+ ====
307
+
308
+ === Memory management
309
+
310
+ ==== General
311
+
312
+ For production systems, implement resource limits to prevent issues with
313
+ malformed or malicious files.
314
+
315
+ ==== Implementation
316
+
317
+ [example]
318
+ ====
319
+ [source,ruby]
320
+ ----
321
+ MAX_FILE_SIZE = 100 * 1024 * 1024 # 100 MB
322
+ MAX_PROCESSING_TIME = 30 # seconds
323
+
324
+ if File.size(file_path) > MAX_FILE_SIZE
325
+ raise "File too large"
326
+ end
327
+
328
+ Timeout.timeout(MAX_PROCESSING_TIME) do
329
+ result = service.validate_file(file_path)
330
+ end
331
+ ----
332
+ ====
333
+
334
+ == Troubleshooting
335
+
336
+ === Common issues
337
+
338
+ ==== File not found error
339
+
340
+ **Symptoms**: Error message "File not found"
341
+
342
+ **Solutions**:
343
+
344
+ * Check file path is correct
345
+ * Use absolute paths if relative paths don't work
346
+ * Ensure file has read permissions
347
+
348
+ ==== Memory issues with large files
349
+
350
+ **Symptoms**: Out of memory errors or slow validation
351
+
352
+ **Solutions**:
353
+
354
+ * Use streaming mode: `streaming: true`
355
+ * Process files one at a time
356
+ * Set memory limits in your environment
357
+
358
+ ==== Slow validation
359
+
360
+ **Symptoms**: Validation takes longer than expected
361
+
362
+ **Solutions**:
363
+
364
+ * Use streaming mode for large files
365
+ * Consider caching results
366
+ * Run validation in background jobs
367
+
368
+ == Testing examples
369
+
370
+ === Using test fixtures
371
+
372
+ You can test the examples with files from the PngSuite test fixture:
373
+
374
+ [example]
375
+ ====
376
+ [source,shell]
377
+ ----
378
+ # Using a test fixture
379
+ ruby examples/basic_usage.rb \
380
+ spec/fixtures/pngsuite/background/bgwn6a08.png
381
+
382
+ # Using multiple test files
383
+ ruby examples/advanced_usage.rb \
384
+ spec/fixtures/pngsuite/background/*.png
385
+ ----
386
+ ====
387
+
388
+ == Additional resources
389
+
390
+ * link:../README.adoc[API Documentation]
391
+ * link:../ARCHITECTURE.md[Architecture Guide]
392
+ * link:../CONTRIBUTING.md[Contributing Guide]
393
+ * link:../SECURITY.md[Security Policy]
394
+
395
+ == Questions
396
+
397
+ * Open an issue: https://github.com/metanorma/png_conform/issues
398
+ * Read the documentation: https://github.com/metanorma/png_conform
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # Advanced Usage Examples for PngConform
6
+ #
7
+ # This file demonstrates advanced features and integration patterns
8
+ # for using PngConform in production applications.
9
+ #
10
+
11
+ require "png_conform"
12
+
13
+ # Example 1: Custom Reporter
14
+ class CustomReporter < PngConform::Reporters::BaseReporter
15
+ def report(result)
16
+ {
17
+ status: result.valid? ? "pass" : "fail",
18
+ file: result.file_path,
19
+ dimensions: "#{result.image_info.width}x#{result.image_info.height}",
20
+ chunks: result.chunks.map(&:type),
21
+ error_count: result.errors.count,
22
+ errors: result.errors.map do |e|
23
+ { severity: e.severity, message: e.message }
24
+ end,
25
+ }
26
+ end
27
+ end
28
+
29
+ def custom_reporter_example(file_path)
30
+ puts "=" * 60
31
+ puts "Example 1: Custom Reporter"
32
+ puts "=" * 60
33
+
34
+ result = PngConform::Services::ValidationService.validate_file(file_path)
35
+
36
+ reporter = CustomReporter.new
37
+ custom_output = reporter.report(result)
38
+
39
+ require "json"
40
+ puts JSON.pretty_generate(custom_output)
41
+ puts
42
+ end
43
+
44
+ # Example 2: Custom Validator Integration
45
+ def validator_integration_example(file_path)
46
+ puts "=" * 60
47
+ puts "Example 2: Working with Validators"
48
+ puts "=" * 60
49
+
50
+ result = PngConform::Services::ValidationService.validate_file(file_path)
51
+
52
+ # Group errors by chunk type
53
+ errors_by_chunk = result.errors.group_by(&:chunk_type)
54
+
55
+ errors_by_chunk.each do |chunk_type, errors|
56
+ puts "#{chunk_type || 'General'}:"
57
+ errors.each do |error|
58
+ puts " [#{error.severity.upcase}] #{error.message}"
59
+ end
60
+ end
61
+ puts
62
+ end
63
+
64
+ # Example 3: Streaming vs Full Load
65
+ def compare_reading_modes(file_path)
66
+ puts "=" * 60
67
+ puts "Example 3: Streaming vs Full Load"
68
+ puts "=" * 60
69
+
70
+ # Both modes use the same API now - internally handles differently
71
+ # Measure full load
72
+ start_time = Time.now
73
+ full_load_result = PngConform::Services::ValidationService.validate_file(file_path)
74
+ full_load_time = Time.now - start_time
75
+
76
+ # Measure streaming (same API, different internal handling)
77
+ start_time = Time.now
78
+ streaming_result = PngConform::Services::ValidationService.validate_file(file_path)
79
+ streaming_time = Time.now - start_time
80
+
81
+ puts "File: #{file_path} (#{File.size(file_path)} bytes)"
82
+ puts "Full Load Mode: #{(full_load_time * 1000).round(2)}ms"
83
+ puts "Streaming Mode: #{(streaming_time * 1000).round(2)}ms"
84
+ puts "Difference: #{((streaming_time - full_load_time) * 1000).round(2)}ms"
85
+ puts
86
+
87
+ # Both should produce same validation results
88
+ puts "Results match: #{full_load_result.valid? == streaming_result.valid?}"
89
+ puts
90
+ end
91
+
92
+ # Example 4: Profile Comparison
93
+ def profile_comparison(file_path)
94
+ puts "=" * 60
95
+ puts "Example 4: Profile Comparison"
96
+ puts "=" * 60
97
+
98
+ profiles = %w[minimal web print archive strict default]
99
+
100
+ result = PngConform::Services::ValidationService.validate_file(file_path)
101
+ chunk_types = result.chunks.map(&:type)
102
+
103
+ results = profiles.map do |profile_name|
104
+ PngConform::Services::ProfileManager.get_profile(profile_name)
105
+ profile_result = PngConform::Services::ProfileManager.validate_file_against_profile(
106
+ chunk_types, profile_name
107
+ )
108
+ {
109
+ profile: profile_name,
110
+ valid: profile_result[:valid],
111
+ error_count: profile_result[:errors].count,
112
+ warning_count: profile_result[:warnings].count,
113
+ }
114
+ end
115
+
116
+ puts "Profile Comparison for: #{file_path}"
117
+ puts "-" * 60
118
+ printf("%-12s | %-8s | %-8s | %-10s\n", "Profile", "Valid", "Errors",
119
+ "Warnings")
120
+ puts "-" * 60
121
+
122
+ results.each do |r|
123
+ printf("%-12s | %-8s | %-8d | %-10d\n",
124
+ r[:profile],
125
+ r[:valid] ? "✓" : "✗",
126
+ r[:error_count],
127
+ r[:warning_count])
128
+ end
129
+ puts
130
+ end
131
+
132
+ # Example 5: Error Handling Best Practices
133
+ def error_handling_example(file_path)
134
+ puts "=" * 60
135
+ puts "Example 5: Error Handling"
136
+ puts "=" * 60
137
+
138
+ begin
139
+ # Validate file existence and size first
140
+ unless File.exist?(file_path)
141
+ raise PngConform::Error, "File not found: #{file_path}"
142
+ end
143
+
144
+ max_size = 100 * 1024 * 1024 # 100 MB
145
+ if File.size(file_path) > max_size
146
+ raise PngConform::Error, "File too large (>100MB)"
147
+ end
148
+
149
+ # Perform validation
150
+ result = PngConform::Services::ValidationService.validate_file(file_path)
151
+
152
+ if result.valid?
153
+ puts "✓ Validation successful"
154
+ else
155
+ puts "✗ Validation failed:"
156
+ result.errors.each do |error|
157
+ puts " #{error.severity}: #{error.message}"
158
+ end
159
+ end
160
+ rescue PngConform::ParseError => e
161
+ puts "✗ Parse error: #{e.message}"
162
+ puts " File may be corrupted or not a valid PNG"
163
+ rescue PngConform::ValidationError => e
164
+ puts "✗ Validation error: #{e.message}"
165
+ rescue PngConform::Error => e
166
+ puts "✗ PngConform error: #{e.message}"
167
+ rescue StandardError => e
168
+ puts "✗ Unexpected error: #{e.class} - #{e.message}"
169
+ puts e.backtrace.first(5).join("\n ")
170
+ end
171
+ puts
172
+ end
173
+
174
+ # Example 6: Working with Chunk Data
175
+ def chunk_data_extraction(file_path)
176
+ puts "=" * 60
177
+ puts "Example 6: Extracting Chunk Data"
178
+ puts "=" * 60
179
+
180
+ result = PngConform::Services::ValidationService.validate_file(file_path)
181
+
182
+ # Extract text chunks
183
+ text_chunks = result.chunks.select { |c| %w[tEXt zTXt iTXt].include?(c.type) }
184
+ if text_chunks.any?
185
+ puts "Text Metadata:"
186
+ text_chunks.each do |chunk|
187
+ if chunk.data
188
+ text = chunk.data.to_s
189
+ null_pos = text.index("\x00")
190
+ if null_pos
191
+ keyword = text[0...null_pos]
192
+ content = text[(null_pos + 1)..]
193
+ puts " #{keyword}: #{content}"
194
+ end
195
+ end
196
+ end
197
+ else
198
+ puts "No text metadata found"
199
+ end
200
+ puts
201
+
202
+ # Extract color profile information
203
+ if result.chunks.any? { |c| c.type == "gAMA" }
204
+ gama_chunk = result.chunks.find { |c| c.type == "gAMA" }
205
+ if gama_chunk&.data
206
+ gamma_value = gama_chunk.data.bytes[0..3].pack("C*").unpack1("N")
207
+ puts "Gamma: #{gamma_value / 100_000.0}"
208
+ end
209
+ end
210
+
211
+ if result.chunks.any? { |c| c.type == "sRGB" }
212
+ puts "sRGB rendering intent present"
213
+ end
214
+
215
+ if result.chunks.any? { |c| c.type == "iCCP" }
216
+ puts "ICC profile present"
217
+ end
218
+ puts
219
+ end
220
+
221
+ # Example 7: Performance Monitoring
222
+ def performance_monitoring(files)
223
+ puts "=" * 60
224
+ puts "Example 7: Performance Monitoring"
225
+ puts "=" * 60
226
+
227
+ stats = {
228
+ total: 0,
229
+ successful: 0,
230
+ failed: 0,
231
+ total_time: 0,
232
+ avg_time: 0,
233
+ total_size: 0,
234
+ }
235
+
236
+ files.each do |file|
237
+ next unless File.exist?(file)
238
+
239
+ stats[:total] += 1
240
+ stats[:total_size] += File.size(file)
241
+
242
+ start_time = Time.now
243
+ begin
244
+ result = PngConform::Services::ValidationService.validate_file(file)
245
+ elapsed = Time.now - start_time
246
+ stats[:total_time] += elapsed
247
+
248
+ if result.valid?
249
+ stats[:successful] += 1
250
+ else
251
+ stats[:failed] += 1
252
+ end
253
+ rescue StandardError
254
+ stats[:failed] += 1
255
+ end
256
+ end
257
+
258
+ if stats[:total].positive?
259
+ stats[:avg_time] =
260
+ stats[:total_time] / stats[:total]
261
+ end
262
+
263
+ puts "Performance Statistics:"
264
+ puts " Files processed: #{stats[:total]}"
265
+ puts " Successful: #{stats[:successful]}"
266
+ puts " Failed: #{stats[:failed]}"
267
+ puts " Total size: #{(stats[:total_size] / 1024.0 / 1024.0).round(2)} MB"
268
+ puts " Total time: #{stats[:total_time].round(3)}s"
269
+ puts " Average time: #{(stats[:avg_time] * 1000).round(2)}ms per file"
270
+ puts " Throughput: #{(stats[:total_size] / stats[:total_time] / 1024.0 / 1024.0).round(2)} MB/s" if stats[:total_time].positive?
271
+ puts
272
+ end
273
+
274
+ # Main execution
275
+ if __FILE__ == $PROGRAM_NAME
276
+ if ARGV.empty?
277
+ puts "Usage: ruby #{$PROGRAM_NAME} PATH_TO_PNG_FILE [MORE_FILES...]"
278
+ puts
279
+ puts "Examples:"
280
+ puts " ruby #{$PROGRAM_NAME} image.png"
281
+ puts " ruby #{$PROGRAM_NAME} image1.png image2.png image3.png"
282
+ exit 1
283
+ end
284
+
285
+ file_path = ARGV[0]
286
+
287
+ unless File.exist?(file_path)
288
+ puts "Error: File not found: #{file_path}"
289
+ exit 1
290
+ end
291
+
292
+ # Run advanced examples
293
+ custom_reporter_example(file_path)
294
+ validator_integration_example(file_path)
295
+ compare_reading_modes(file_path)
296
+ profile_comparison(file_path)
297
+ error_handling_example(file_path)
298
+ chunk_data_extraction(file_path)
299
+
300
+ # Performance monitoring if multiple files provided
301
+ if ARGV.length > 1
302
+ performance_monitoring(ARGV)
303
+ end
304
+ end