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,681 @@
1
+ = PngConform Architecture
2
+
3
+ == Overview
4
+
5
+ PngConform is a pure Ruby implementation of PNG/MNG/JNG validity checking with profile support. The architecture follows strict object-oriented principles, MECE (Mutually Exclusive, Collectively Exhaustive) design, and separation of concerns.
6
+
7
+ == Core Architectural Principles
8
+
9
+ === Object-Oriented Design
10
+
11
+ *Encapsulation*:: Each class has a single, well-defined responsibility
12
+ *Inheritance*:: Chunk validators inherit from base classes with clear contracts
13
+ *Polymorphism*:: Different chunk types implement common validation interfaces
14
+ *Abstraction*:: Implementation details hidden behind clean interfaces
15
+
16
+ === MECE Structure
17
+
18
+ Each layer and component has:
19
+
20
+ * *Mutually Exclusive*: No overlap in responsibilities
21
+ * *Collectively Exhaustive*: Complete coverage of the problem domain
22
+
23
+ === Separation of Concerns
24
+
25
+ Clear boundaries between:
26
+
27
+ * Binary parsing (BinData layer)
28
+ * Domain modeling (Lutaml::Model layer)
29
+ * Business logic (Validators)
30
+ * Presentation (Reporters)
31
+ * Interface (CLI)
32
+
33
+ == Layered Architecture
34
+
35
+ [source]
36
+ ----
37
+ ┌─────────────────────────────────────────────────────────────┐
38
+ │ Presentation Layer │
39
+ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
40
+ │ │ CLI (Thor) │ │ Reporter │ │ ColorOutput │ │
41
+ │ └──────────────┘ └──────────────┘ └──────────────┘ │
42
+ └───────────────────────────┬─────────────────────────────────┘
43
+
44
+ ┌───────────────────────────▼─────────────────────────────────┐
45
+ │ Application/Service Layer │
46
+ │ ┌──────────────────────────────────────────────────────┐ │
47
+ │ │ ValidationService │ │
48
+ │ │ • Orchestrates validation workflow │ │
49
+ │ │ • Manages profiles │ │
50
+ │ │ • Coordinates validators │ │
51
+ │ └──────────────────────────────────────────────────────┘ │
52
+ └───────────────────────────┬─────────────────────────────────┘
53
+
54
+ ┌───────────────────────────▼─────────────────────────────────┐
55
+ │ Business Logic Layer │
56
+ │ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
57
+ │ │ Validator │ │ Profile │ │ ChunkRegistry│ │
58
+ │ │ (Base) │ │ │ │ │ │
59
+ │ └──────┬──────┘ └──────────────┘ └──────────────┘ │
60
+ │ │ │
61
+ │ ┌──────▼──────────────────────────────────────────┐ │
62
+ │ │ Chunk Validators │ │
63
+ │ │ ┌────────────┐ ┌────────────┐ ┌───────────┐ │ │
64
+ │ │ │ IHDRVal │ │ PLTEVal │ │ IDATVal │ │ │
65
+ │ │ └────────────┘ └────────────┘ └───────────┘ │ │
66
+ │ │ ┌────────────┐ ┌────────────┐ ┌───────────┐ │ │
67
+ │ │ │ bKGDVal │ │ tRNSVal │ │ MHDRVal │ │ │
68
+ │ │ └────────────┘ └────────────┘ └───────────┘ │ │
69
+ │ │ ... (40+ chunk validators) │ │
70
+ │ └──────────────────────────────────────────────────┘ │
71
+ └───────────────────────────┬─────────────────────────────────┘
72
+
73
+ ┌───────────────────────────▼─────────────────────────────────┐
74
+ │ Domain Model Layer │
75
+ │ ┌──────────────────────────────────────────────────────┐ │
76
+ │ │ Models (Lutaml::Model) │ │
77
+ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
78
+ │ │ │ Chunk │ │ Result │ │ Validation │ │ │
79
+ │ │ │ │ │ │ │ Error │ │ │
80
+ │ │ └────────────┘ └────────────┘ └────────────┘ │ │
81
+ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
82
+ │ │ │ ChunkData │ │ FileInfo │ │ Profile │ │ │
83
+ │ │ │ (various) │ │ │ │ Config │ │ │
84
+ │ │ └────────────┘ └────────────┘ └────────────┘ │ │
85
+ │ └──────────────────────────────────────────────────────┘ │
86
+ └───────────────────────────┬─────────────────────────────────┘
87
+
88
+ ┌───────────────────────────▼─────────────────────────────────┐
89
+ │ Binary Parsing Layer (BinData) │
90
+ │ ┌──────────────────────────────────────────────────────┐ │
91
+ │ │ Binary Structures │ │
92
+ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
93
+ │ │ │ PngFile │ │ MngFile │ │ JngFile │ │ │
94
+ │ │ └────────────┘ └────────────┘ └────────────┘ │ │
95
+ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
96
+ │ │ │ChunkStruct │ │ IHDRStruct │ │ PLTEStruct │ │ │
97
+ │ │ └────────────┘ └────────────┘ └────────────┘ │ │
98
+ │ │ ... (40+ chunk structures) │ │
99
+ │ └──────────────────────────────────────────────────────┘ │
100
+ │ ┌──────────────────────────────────────────────────────┐ │
101
+ │ │ File Readers │ │
102
+ │ │ ┌────────────┐ ┌────────────┐ │ │
103
+ │ │ │ Streaming │ │ FullLoad │ │ │
104
+ │ │ │ Reader │ │ Reader │ │ │
105
+ │ │ └────────────┘ └────────────┘ │ │
106
+ │ └──────────────────────────────────────────────────────┘ │
107
+ └─────────────────────────────────────────────────────────────┘
108
+ ----
109
+
110
+ == Module Structure
111
+
112
+ === Top-Level Module: `PngConform`
113
+
114
+ All classes are namespaced under `PngConform` to avoid pollution of global namespace.
115
+
116
+ [source,ruby]
117
+ ----
118
+ module PngConform
119
+ # Binary parsing layer
120
+ module BinData
121
+ class PngFile < ::BinData::Record
122
+ class MngFile < ::BinData::Record
123
+ class ChunkStructure < ::BinData::Record
124
+ # ... chunk-specific structures
125
+ end
126
+
127
+ # Domain models
128
+ module Models
129
+ class Chunk < Lutaml::Model::Serializable
130
+ class Result < Lutaml::Model::Serializable
131
+ class ValidationError < Lutaml::Model::Serializable
132
+ class Profile < Lutaml::Model::Serializable
133
+ end
134
+
135
+ # Business logic - Validators
136
+ module Validators
137
+ class Base
138
+
139
+ module Critical
140
+ class IhdrValidator < Base
141
+ class PlteValidator < Base
142
+ class IdatValidator < Base
143
+ class IendValidator < Base
144
+ end
145
+
146
+ module Ancillary
147
+ class BkgdValidator < Base
148
+ class ChrmValidator < Base
149
+ # ... 30+ ancillary validators
150
+ end
151
+
152
+ module Apng
153
+ class ActlValidator < Base
154
+ class FctlValidator < Base
155
+ class FdatValidator < Base
156
+ end
157
+
158
+ module Mng
159
+ class MhdrValidator < Base
160
+ class MendValidator < Base
161
+ # ... MNG-specific validators
162
+ end
163
+
164
+ module Jng
165
+ class JhdrValidator < Base
166
+ class JdatValidator < Base
167
+ # ... JNG-specific validators
168
+ end
169
+ end
170
+
171
+ # Services/Application logic
172
+ module Services
173
+ class ValidationService
174
+ class ProfileManager
175
+ class ChunkRegistry
176
+ end
177
+
178
+ # Infrastructure
179
+ module Readers
180
+ class StreamingReader
181
+ class FullLoadReader
182
+ end
183
+
184
+ module Reporters
185
+ class PngcheckReporter
186
+ class VerboseReporter
187
+ class JsonReporter
188
+ end
189
+
190
+ # Presentation
191
+ class CLI < Thor
192
+ end
193
+ ----
194
+
195
+ == Core Classes and Responsibilities
196
+
197
+ === Binary Parsing Layer
198
+
199
+ ==== `PngConform::BinData::PngFile`
200
+
201
+ *Responsibility*: Binary structure definition for PNG files
202
+
203
+ *MECE*: Exclusively handles binary format definition
204
+
205
+ * Signature validation
206
+ * Chunk sequence structure
207
+ * Endianness handling
208
+
209
+ ==== `PngConform::BinData::ChunkStructure`
210
+
211
+ *Responsibility*: Generic chunk binary structure
212
+
213
+ *MECE*: Exclusively handles chunk-level binary format
214
+
215
+ * Length field (4 bytes, big-endian)
216
+ * Type field (4 bytes, ASCII)
217
+ * Data field (variable)
218
+ * CRC field (4 bytes)
219
+
220
+ ==== `PngConform::Readers::StreamingReader`
221
+
222
+ *Responsibility*: Memory-efficient chunk-by-chunk reading
223
+
224
+ *MECE*: Exclusively handles streaming I/O
225
+
226
+ * Reads chunks one at a time
227
+ * Minimal memory footprint
228
+ * Suitable for large files
229
+
230
+ ==== `PngConform::Readers::FullLoadReader`
231
+
232
+ *Responsibility*: Load entire file into memory
233
+
234
+ *MECE*: Exclusively handles full-file reading
235
+
236
+ * Reads entire file at once
237
+ * Faster for small/medium files
238
+ * Enables random access to chunks
239
+
240
+ === Domain Model Layer
241
+
242
+ ==== `PngConform::Models::Chunk`
243
+
244
+ *Responsibility*: Domain representation of a PNG chunk
245
+
246
+ *MECE*: Exclusively represents chunk data in domain terms
247
+
248
+ *Attributes*:
249
+
250
+ * `type`: String (4-byte chunk type)
251
+ * `length`: Integer (data length)
252
+ * `data`: Binary string (chunk data)
253
+ * `crc`: Integer (CRC-32 value)
254
+ * `crc_valid`: Boolean (calculated)
255
+ * `position`: Integer (file offset)
256
+
257
+ ==== `PngConform::Models::Result`
258
+
259
+ *Responsibility*: Validation result aggregation
260
+
261
+ *MECE*: Exclusively represents validation outcomes
262
+
263
+ *Attributes*:
264
+
265
+ * `file_info`: FileInfo (dimensions, color type, etc.)
266
+ * `chunks`: Array<Chunk> (all chunks found)
267
+ * `errors`: Array<ValidationError> (all errors)
268
+ * `warnings`: Array<ValidationError> (all warnings)
269
+ * `valid`: Boolean (overall validity)
270
+ * `profile`: Profile (validation profile used)
271
+
272
+ ==== `PngConform::Models::ValidationError`
273
+
274
+ *Responsibility*: Single validation error/warning
275
+
276
+ *MECE*: Exclusively represents one validation issue
277
+
278
+ *Attributes*:
279
+
280
+ * `severity`: Symbol (:error, :warning, :info)
281
+ * `chunk_type`: String (related chunk)
282
+ * `message`: String (human-readable description)
283
+ * `position`: Integer (file offset)
284
+ * `code`: Symbol (error code for programmatic use)
285
+
286
+ ==== `PngConform::Models::Profile`
287
+
288
+ *Responsibility*: Validation profile configuration
289
+
290
+ *MECE*: Exclusively defines what's valid for a profile
291
+
292
+ *Attributes*:
293
+
294
+ * `name`: String (e.g., "PNG baseline", "MNG-VLC")
295
+ * `required_chunks`: Array<String> (must be present)
296
+ * `allowed_chunks`: Array<String> (may be present)
297
+ * `forbidden_chunks`: Array<String> (must not be present)
298
+ * `chunk_rules`: Hash (ordering, dependencies, etc.)
299
+
300
+ === Business Logic Layer
301
+
302
+ ==== `PngConform::Validators::Base`
303
+
304
+ *Responsibility*: Common validator interface and utilities
305
+
306
+ *MECE*: Exclusively provides validator infrastructure
307
+
308
+ *Interface*:
309
+
310
+ [source,ruby]
311
+ ----
312
+ class Base
313
+ def validate(chunk, context)
314
+ # Returns array of ValidationError
315
+ end
316
+
317
+ protected
318
+
319
+ def error(message, code:, severity: :error)
320
+ def warning(message, code:)
321
+ def check_range(value, min, max, field_name)
322
+ def check_required_chunk(chunk_type, context)
323
+ end
324
+ ----
325
+
326
+ ==== `PngConform::Validators::Critical::IhdrValidator`
327
+
328
+ *Responsibility*: IHDR chunk validation
329
+
330
+ *MECE*: Exclusively validates IHDR requirements
331
+
332
+ *Validations*:
333
+
334
+ * Width > 0 and <= 2^31-1
335
+ * Height > 0 and <= 2^31-1
336
+ * Bit depth valid for color type
337
+ * Color type in allowed set (0,2,3,4,6)
338
+ * Compression method = 0
339
+ * Filter method = 0
340
+ * Interlace method = 0 or 1
341
+ * Must be first chunk after signature
342
+
343
+ ==== `PngConform::Services::ValidationService`
344
+
345
+ *Responsibility*: Orchestrate validation workflow
346
+
347
+ *MECE*: Exclusively coordinates validation process
348
+
349
+ *Methods*:
350
+
351
+ [source,ruby]
352
+ ----
353
+ class ValidationService
354
+ def initialize(profile: :png_baseline)
355
+
356
+ def validate_file(path, streaming: false)
357
+ # Returns Result
358
+ end
359
+
360
+ def validate_io(io, streaming: false)
361
+ # Returns Result
362
+ end
363
+
364
+ private
365
+
366
+ def read_chunks(source, streaming)
367
+ def validate_signature(signature)
368
+ def validate_chunk_sequence(chunks)
369
+ def validate_individual_chunks(chunks)
370
+ def aggregate_results(chunks, errors)
371
+ end
372
+ ----
373
+
374
+ ==== `PngConform::Services::ChunkRegistry`
375
+
376
+ *Responsibility*: Map chunk types to validators
377
+
378
+ *MECE*: Exclusively manages chunk type -> validator mapping
379
+
380
+ *Methods*:
381
+
382
+ [source,ruby]
383
+ ----
384
+ class ChunkRegistry
385
+ def self.validator_for(chunk_type)
386
+ # Returns appropriate validator class
387
+ end
388
+
389
+ def self.register(chunk_type, validator_class)
390
+
391
+ def self.critical?(chunk_type)
392
+ def self.ancillary?(chunk_type)
393
+ def self.private?(chunk_type)
394
+ def self.safe_to_copy?(chunk_type)
395
+ end
396
+ ----
397
+
398
+ === Presentation Layer
399
+
400
+ ==== `PngConform::Reporters::PngcheckReporter`
401
+
402
+ *Responsibility*: Format output to match pngcheck exactly
403
+
404
+ *MECE*: Exclusively handles pngcheck-compatible output
405
+
406
+ *Methods*:
407
+
408
+ [source,ruby]
409
+ ----
410
+ class PngcheckReporter
411
+ def format(result, verbose: 0, color: false)
412
+ # Returns formatted string matching pngcheck output
413
+ end
414
+
415
+ private
416
+
417
+ def format_ok(result)
418
+ def format_errors(result)
419
+ def format_chunk_info(chunk, verbose_level)
420
+ def compression_ratio(result)
421
+ end
422
+ ----
423
+
424
+ ==== `PngConform::CLI`
425
+
426
+ *Responsibility*: Command-line interface
427
+
428
+ *MECE*: Exclusively handles CLI concerns
429
+
430
+ *Commands*:
431
+
432
+ * `validate`: Validate PNG/MNG/JNG files
433
+ * `version`: Show version
434
+ * `help`: Show help
435
+
436
+ *Options* (matching pngcheck):
437
+
438
+ * `-v, --verbose`: Verbose output (can stack: -v, -vv, -vvv, -vvvv)
439
+ * `-q, --quiet`: Quiet mode (errors only)
440
+ * `-c, --color`: Colorized output
441
+ * `-7`: Print tEXt chunks, escape chars >= 128
442
+ * `-t`: Print tEXt chunks
443
+ * `-p`: Print palette contents
444
+ * `-s`: Search for PNGs within file
445
+ * `-x`: Extract PNGs found by search
446
+ * `-w`: Suppress windowBits warning
447
+
448
+ == Data Flow
449
+
450
+ === Validation Flow
451
+
452
+ [source]
453
+ ----
454
+ 1. File Input
455
+
456
+ 2. Reader Selection (Streaming vs FullLoad)
457
+
458
+ 3. Binary Parsing (BinData)
459
+
460
+ 4. Domain Model Creation (Chunk objects)
461
+
462
+ 5. Profile Loading (based on file type/user choice)
463
+
464
+ 6. Signature Validation
465
+
466
+ 7. Chunk Sequence Validation
467
+
468
+ 8. Individual Chunk Validation (parallel possible)
469
+
470
+ 9. Result Aggregation
471
+
472
+ 10. Reporter Formatting
473
+
474
+ 11. Output to Console/File
475
+ ----
476
+
477
+ === Chunk Validation Flow
478
+
479
+ [source]
480
+ ----
481
+ For each chunk:
482
+ 1. Look up validator in ChunkRegistry
483
+ 2. Parse chunk data into typed structure (BinData)
484
+ 3. Create domain Chunk object
485
+ 4. Invoke validator with chunk + context
486
+ 5. Collect errors/warnings
487
+ 6. Add to result
488
+ ----
489
+
490
+ === Context Object
491
+
492
+ The validation context flows through the validation process:
493
+
494
+ [source,ruby]
495
+ ----
496
+ class ValidationContext
497
+ attr_reader :file_type # :png, :mng, :jng
498
+ attr_reader :profile # Profile object
499
+ attr_reader :seen_chunks # Hash of chunk types seen
500
+ attr_reader :ihdr_data # IHDR info (width, height, etc.)
501
+ attr_reader :has_plte # Boolean
502
+ attr_reader :color_type # Integer from IHDR
503
+ attr_reader :bit_depth # Integer from IHDR
504
+ attr_reader :chunk_sequence # Array of chunk types
505
+
506
+ def record_chunk(chunk)
507
+ def requires_plte?
508
+ def allows_plte?
509
+ def in_mng_top_level?
510
+ end
511
+ ----
512
+
513
+ == Profile System
514
+
515
+ === Profile Hierarchy
516
+
517
+ [source]
518
+ ----
519
+ Profile (Base)
520
+ ├── PngBaselineProfile
521
+ ├── PngExtendedProfile
522
+ ├── ApngProfile
523
+ ├── MngVlcProfile
524
+ ├── MngLcProfile
525
+ ├── MngFullProfile
526
+ └── JngProfile
527
+ ----
528
+
529
+ Each profile defines:
530
+
531
+ . Required chunks (must appear)
532
+ . Allowed chunks (may appear)
533
+ . Forbidden chunks (must not appear)
534
+ . Chunk ordering rules
535
+ . Chunk dependency rules
536
+ . Special validation rules
537
+
538
+ === Profile Configuration Example
539
+
540
+ [source,ruby]
541
+ ----
542
+ class PngBaselineProfile < Profile
543
+ def initialize
544
+ super("PNG Baseline")
545
+
546
+ @required_chunks = %w[IHDR IDAT IEND]
547
+
548
+ @allowed_chunks = %w[
549
+ PLTE bKGD cHRM gAMA hIST iCCP pHYs sBIT
550
+ sPLT sRGB tEXt tIME tRNS zTXt
551
+ ]
552
+
553
+ @chunk_rules = {
554
+ "IHDR" => { position: :first },
555
+ "PLTE" => { before: "IDAT", after: "IHDR" },
556
+ "IDAT" => { consecutive: true },
557
+ "IEND" => { position: :last }
558
+ }
559
+
560
+ @dependencies = {
561
+ "tRNS" => { requires_one_of: ["IHDR", "PLTE"] }
562
+ }
563
+ end
564
+ end
565
+ ----
566
+
567
+ == Extension Points
568
+
569
+ The architecture supports extension through:
570
+
571
+ . *New Chunk Validators*: Implement `Validators::Base`, register in `ChunkRegistry`
572
+ . *New Profiles*: Subclass `Profile`, define rules
573
+ . *New Reporters*: Implement reporter interface
574
+ . *New Readers*: Implement reader interface
575
+
576
+ == Error Handling Strategy
577
+
578
+ . *Parse Errors*: Caught at BinData layer, converted to ValidationErrors
579
+ . *Validation Errors*: Collected, not thrown (allows complete validation)
580
+ . *I/O Errors*: Bubble up as exceptions (critical failures)
581
+ . *CRC Errors*: Recorded as validation errors
582
+
583
+ == Performance Considerations
584
+
585
+ . *Streaming vs Full-Load*: User choice based on file size
586
+ . *Lazy Validation*: Only validate requested verbosity level
587
+ . *Parallel Validation*: Chunks validated independently (future optimization)
588
+ . *Caching*: Profile objects cached, chunk structures reused
589
+
590
+ == Testing Strategy
591
+
592
+ . *Unit Tests*: Each validator class tested independently
593
+ . *Integration Tests*: Full validation flows tested
594
+ . *Fixture Tests*: Compare output with pngcheck reference
595
+ . *Property Tests*: Generate valid/invalid PNGs programmatically
596
+ . *Performance Tests*: Benchmark streaming vs full-load
597
+
598
+ == Dependencies
599
+
600
+ *Runtime*:
601
+
602
+ * `bindata` (~> 2.5): Binary parsing
603
+ * `lutaml-model` (~> 0.7): Domain models
604
+ * `thor` (~> 1.0): CLI framework
605
+
606
+ *Development*:
607
+
608
+ * `rspec` (~> 3.0): Testing
609
+ * `rubocop` (~> 1.0): Linting
610
+ * `yard` (~> 0.9): Documentation
611
+
612
+ == File Organization
613
+
614
+ [source]
615
+ ----
616
+ lib/png_conform/
617
+ ├── version.rb
618
+ ├── cli.rb
619
+ ├── bindata/
620
+ │ ├── png_file.rb
621
+ │ ├── mng_file.rb
622
+ │ ├── jng_file.rb
623
+ │ └── chunks/
624
+ │ ├── ihdr_structure.rb
625
+ │ ├── plte_structure.rb
626
+ │ └── ... (40+ structures)
627
+ ├── models/
628
+ │ ├── chunk.rb
629
+ │ ├── result.rb
630
+ │ ├── validation_error.rb
631
+ │ ├── profile.rb
632
+ │ ├── file_info.rb
633
+ │ └── validation_context.rb
634
+ ├── validators/
635
+ │ ├── base.rb
636
+ │ ├── critical/
637
+ │ │ ├── ihdr_validator.rb
638
+ │ │ ├── plte_validator.rb
639
+ │ │ ├── idat_validator.rb
640
+ │ │ └── iend_validator.rb
641
+ │ ├── ancillary/
642
+ │ │ └── ... (30+ validators)
643
+ │ ├── apng/
644
+ │ │ └── ... (APNG validators)
645
+ │ ├── mng/
646
+ │ │ └── ... (MNG validators)
647
+ │ └── jng/
648
+ │ └── ... (JNG validators)
649
+ ├── services/
650
+ │ ├── validation_service.rb
651
+ │ ├── profile_manager.rb
652
+ │ └── chunk_registry.rb
653
+ ├── readers/
654
+ │ ├── streaming_reader.rb
655
+ │ └── full_load_reader.rb
656
+ ├── reporters/
657
+ │ ├── pngcheck_reporter.rb
658
+ │ ├── verbose_reporter.rb
659
+ │ └── json_reporter.rb
660
+ └── profiles/
661
+ ├── png_baseline.rb
662
+ ├── png_extended.rb
663
+ ├── apng.rb
664
+ ├── mng_vlc.rb
665
+ ├── mng_lc.rb
666
+ └── jng.rb
667
+ ----
668
+
669
+ == Summary
670
+
671
+ This architecture provides:
672
+
673
+ * ✅ *Strict OO*: Each class has single responsibility, clear interfaces
674
+ * ✅ *MECE*: No overlapping responsibilities, complete coverage
675
+ * ✅ *Separation of Concerns*: Clear layer boundaries
676
+ * ✅ *Abstraction*: Implementation details hidden
677
+ * ✅ *Extensibility*: Easy to add new chunks, profiles, reporters
678
+ * ✅ *Testability*: Each component independently testable
679
+ * ✅ *Maintainability*: Clear structure, well-documented
680
+ * ✅ *Performance*: Both streaming and full-load modes
681
+ * ✅ *Compatibility*: Output matches pngcheck exactly