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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +19 -0
- data/.rubocop_todo.yml +197 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/CONTRIBUTING.md +323 -0
- data/Gemfile +13 -0
- data/LICENSE +43 -0
- data/README.adoc +859 -0
- data/Rakefile +10 -0
- data/SECURITY.md +147 -0
- data/docs/ARCHITECTURE.adoc +681 -0
- data/docs/CHUNK_TYPES.adoc +450 -0
- data/docs/CLI_OPTIONS.adoc +913 -0
- data/docs/COMPATIBILITY.adoc +616 -0
- data/examples/README.adoc +398 -0
- data/examples/advanced_usage.rb +304 -0
- data/examples/basic_usage.rb +210 -0
- data/exe/png_conform +6 -0
- data/lib/png_conform/analyzers/comparison_analyzer.rb +230 -0
- data/lib/png_conform/analyzers/metrics_analyzer.rb +176 -0
- data/lib/png_conform/analyzers/optimization_analyzer.rb +190 -0
- data/lib/png_conform/analyzers/resolution_analyzer.rb +274 -0
- data/lib/png_conform/bindata/chunk_structure.rb +153 -0
- data/lib/png_conform/bindata/jng_file.rb +79 -0
- data/lib/png_conform/bindata/mng_file.rb +97 -0
- data/lib/png_conform/bindata/png_file.rb +162 -0
- data/lib/png_conform/cli.rb +116 -0
- data/lib/png_conform/commands/check_command.rb +323 -0
- data/lib/png_conform/commands/list_command.rb +67 -0
- data/lib/png_conform/models/chunk.rb +84 -0
- data/lib/png_conform/models/chunk_info.rb +71 -0
- data/lib/png_conform/models/compression_info.rb +49 -0
- data/lib/png_conform/models/decoded_chunk_data.rb +143 -0
- data/lib/png_conform/models/file_analysis.rb +181 -0
- data/lib/png_conform/models/file_info.rb +91 -0
- data/lib/png_conform/models/image_info.rb +52 -0
- data/lib/png_conform/models/validation_error.rb +89 -0
- data/lib/png_conform/models/validation_result.rb +137 -0
- data/lib/png_conform/readers/full_load_reader.rb +113 -0
- data/lib/png_conform/readers/streaming_reader.rb +180 -0
- data/lib/png_conform/reporters/base_reporter.rb +53 -0
- data/lib/png_conform/reporters/color_reporter.rb +65 -0
- data/lib/png_conform/reporters/json_reporter.rb +18 -0
- data/lib/png_conform/reporters/palette_reporter.rb +48 -0
- data/lib/png_conform/reporters/quiet_reporter.rb +18 -0
- data/lib/png_conform/reporters/reporter_factory.rb +108 -0
- data/lib/png_conform/reporters/summary_reporter.rb +65 -0
- data/lib/png_conform/reporters/text_reporter.rb +66 -0
- data/lib/png_conform/reporters/verbose_reporter.rb +87 -0
- data/lib/png_conform/reporters/very_verbose_reporter.rb +33 -0
- data/lib/png_conform/reporters/visual_elements.rb +66 -0
- data/lib/png_conform/reporters/yaml_reporter.rb +18 -0
- data/lib/png_conform/services/profile_manager.rb +242 -0
- data/lib/png_conform/services/validation_service.rb +457 -0
- data/lib/png_conform/services/zlib_validator.rb +270 -0
- data/lib/png_conform/validators/ancillary/bkgd_validator.rb +140 -0
- data/lib/png_conform/validators/ancillary/chrm_validator.rb +178 -0
- data/lib/png_conform/validators/ancillary/cicp_validator.rb +202 -0
- data/lib/png_conform/validators/ancillary/gama_validator.rb +105 -0
- data/lib/png_conform/validators/ancillary/hist_validator.rb +147 -0
- data/lib/png_conform/validators/ancillary/iccp_validator.rb +243 -0
- data/lib/png_conform/validators/ancillary/itxt_validator.rb +280 -0
- data/lib/png_conform/validators/ancillary/mdcv_validator.rb +201 -0
- data/lib/png_conform/validators/ancillary/offs_validator.rb +132 -0
- data/lib/png_conform/validators/ancillary/pcal_validator.rb +289 -0
- data/lib/png_conform/validators/ancillary/phys_validator.rb +107 -0
- data/lib/png_conform/validators/ancillary/sbit_validator.rb +176 -0
- data/lib/png_conform/validators/ancillary/scal_validator.rb +180 -0
- data/lib/png_conform/validators/ancillary/splt_validator.rb +223 -0
- data/lib/png_conform/validators/ancillary/srgb_validator.rb +117 -0
- data/lib/png_conform/validators/ancillary/ster_validator.rb +111 -0
- data/lib/png_conform/validators/ancillary/text_validator.rb +129 -0
- data/lib/png_conform/validators/ancillary/time_validator.rb +132 -0
- data/lib/png_conform/validators/ancillary/trns_validator.rb +154 -0
- data/lib/png_conform/validators/ancillary/ztxt_validator.rb +173 -0
- data/lib/png_conform/validators/apng/actl_validator.rb +81 -0
- data/lib/png_conform/validators/apng/fctl_validator.rb +155 -0
- data/lib/png_conform/validators/apng/fdat_validator.rb +117 -0
- data/lib/png_conform/validators/base_validator.rb +241 -0
- data/lib/png_conform/validators/chunk_registry.rb +219 -0
- data/lib/png_conform/validators/critical/idat_validator.rb +77 -0
- data/lib/png_conform/validators/critical/iend_validator.rb +68 -0
- data/lib/png_conform/validators/critical/ihdr_validator.rb +160 -0
- data/lib/png_conform/validators/critical/plte_validator.rb +120 -0
- data/lib/png_conform/validators/jng/jdat_validator.rb +66 -0
- data/lib/png_conform/validators/jng/jhdr_validator.rb +116 -0
- data/lib/png_conform/validators/jng/jsep_validator.rb +66 -0
- data/lib/png_conform/validators/mng/back_validator.rb +87 -0
- data/lib/png_conform/validators/mng/clip_validator.rb +65 -0
- data/lib/png_conform/validators/mng/clon_validator.rb +45 -0
- data/lib/png_conform/validators/mng/defi_validator.rb +104 -0
- data/lib/png_conform/validators/mng/dhdr_validator.rb +104 -0
- data/lib/png_conform/validators/mng/disc_validator.rb +44 -0
- data/lib/png_conform/validators/mng/endl_validator.rb +65 -0
- data/lib/png_conform/validators/mng/fram_validator.rb +91 -0
- data/lib/png_conform/validators/mng/loop_validator.rb +75 -0
- data/lib/png_conform/validators/mng/mend_validator.rb +31 -0
- data/lib/png_conform/validators/mng/mhdr_validator.rb +69 -0
- data/lib/png_conform/validators/mng/move_validator.rb +61 -0
- data/lib/png_conform/validators/mng/save_validator.rb +39 -0
- data/lib/png_conform/validators/mng/seek_validator.rb +42 -0
- data/lib/png_conform/validators/mng/show_validator.rb +52 -0
- data/lib/png_conform/validators/mng/term_validator.rb +84 -0
- data/lib/png_conform/version.rb +5 -0
- data/lib/png_conform.rb +101 -0
- data/png_conform.gemspec +43 -0
- 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
|