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
data/README.adoc ADDED
@@ -0,0 +1,859 @@
1
+ = PngConform: A Comprehensive PNG Validator in Ruby
2
+
3
+ image:https://img.shields.io/gem/v/png_conform.svg[Gem Version, link="https://rubygems.org/gems/png_conform"]
4
+ image:https://img.shields.io/github/license/claricle/png_conform.svg[License, link="https://github.com/claricle/png_conform/blob/main/LICENSE"]
5
+ image:https://github.com/claricle/png_conform/actions/workflows/rake.yml/badge.svg[Build Status, link="https://github.com/claricle/png_conform/actions/workflows/rake.yml"]
6
+
7
+ == Purpose
8
+
9
+ PngConform is a pure Ruby PNG file validator with comprehensive chunk validation
10
+ and profile support. It validates file structure, chunk validity, CRC checksums,
11
+ chunk ordering, and profile conformance for PNG files.
12
+
13
+ The library provides pngcheck-compatible validation output while leveraging
14
+ modern Ruby object-oriented design principles and a layered architecture for
15
+ extensibility and maintainability.
16
+
17
+ == Features
18
+
19
+ * *46 Chunk Validators*: Complete validation of PNG (24), APNG (3), MNG (16), and JNG (3) chunks
20
+ * *PNG 3rd Edition Support*: Includes cICP and mDCv chunks for HDR content
21
+ * *MNG/JNG Support*: Full Multiple-image Network Graphics and JPEG Network Graphics validation
22
+ * *CRC Validation*: CRC-32 checksum verification for all chunks with detailed error reporting
23
+ * *Chunk Ordering*: Validates proper chunk sequence and dependencies
24
+ * *6 Validation Profiles*: Minimal, web, print, archive, strict, and default profiles
25
+ * *Enhanced Output Formats*:
26
+ ** Text output with colors and emojis (✅/❌/⚠️)
27
+ ** Comprehensive YAML output with image info, resolution, and recommendations
28
+ ** Comprehensive JSON output for CI/CD integration
29
+ ** Compression ratio reporting
30
+ * *Analysis Features*:
31
+ ** *Retina/Resolution Analysis*: @1x/@2x/@3x calculations for mobile developers
32
+ ** *Optimization Suggestions*: File size reduction recommendations
33
+ ** *Comprehensive Metrics*: Detailed metrics for CI/CD automation
34
+ ** *iOS/Android Support*: Asset catalog and density bucket suggestions
35
+ * *Multiple Output Modes*: Summary, verbose, very verbose, quiet, with optional palette/text/color output
36
+ * *Clean Architecture*: Layered OO design following MECE principles
37
+ * *CLI Interface*: Thor-based command-line tool with pngcheck-compatible options plus modern analysis features
38
+ * *Comprehensive Testing*: Extensive RSpec test suite (936 examples, 100% passing)
39
+
40
+ == Architecture
41
+
42
+ PngConform uses a layered architecture with strict separation of concerns:
43
+
44
+ .Layered Architecture
45
+ [source]
46
+ ----
47
+ ╔════════════════════════════════════════════════════════════╗
48
+ ║ Presentation Layer ║
49
+ ║ (CLI, Reporters) ║
50
+ ╚════════════════════════════════════════════════════════════╝
51
+
52
+ ╔════════════════════════════════════════════════════════════╗
53
+ ║ Service Layer ║
54
+ ║ (ValidationService, ProfileManager) ║
55
+ ╚════════════════════════════════════════════════════════════╝
56
+
57
+ ╔════════════════════════════════════════════════════════════╗
58
+ ║ Business Logic Layer ║
59
+ ║ (Validators, ChunkRegistry, Readers) ║
60
+ ╚════════════════════════════════════════════════════════════╝
61
+
62
+ ╔════════════════════════════════════════════════════════════╗
63
+ ║ Domain Model Layer ║
64
+ ║ (ChunkInfo, ValidationResult, FileAnalysis) ║
65
+ ╚════════════════════════════════════════════════════════════╝
66
+
67
+ ╔════════════════════════════════════════════════════════════╗
68
+ ║ Binary Parsing Layer ║
69
+ ║ (BinData structures) ║
70
+ ╚════════════════════════════════════════════════════════════╝
71
+ ----
72
+
73
+ .Module Structure
74
+ [source]
75
+ ----
76
+ PngConform
77
+ ├── BinData (Binary parsing structures)
78
+ │ ├── ChunkStructure
79
+ │ ├── PngFile
80
+ │ ├── MngFile ✅
81
+ │ └── JngFile ✅
82
+ ├── Models (Domain models)
83
+ │ ├── ChunkInfo
84
+ │ ├── ValidationResult
85
+ │ ├── ValidationError
86
+ │ ├── FileAnalysis
87
+ │ ├── ImageInfo
88
+ │ ├── CompressionInfo
89
+ │ └── DecodedChunkData
90
+ ├── Validators (Validation logic)
91
+ │ ├── BaseValidator
92
+ │ ├── ChunkRegistry
93
+ │ ├── Critical (IHDR, PLTE, IDAT, IEND)
94
+ │ └── Ancillary (gAMA, tRNS, tEXt, etc. - 20 validators)
95
+ ├── Services (Orchestration)
96
+ │ ├── ValidationService
97
+ │ └── ProfileManager
98
+ ├── Readers (File reading strategies)
99
+ │ ├── StreamingReader
100
+ │ └── FullLoadReader
101
+ ├── Reporters (Output formatting)
102
+ │ ├── BaseReporter
103
+ │ ├── SummaryReporter
104
+ │ ├── VerboseReporter
105
+ │ ├── VeryVerboseReporter
106
+ │ ├── QuietReporter
107
+ │ ├── PaletteReporter (decorator)
108
+ │ ├── TextReporter (decorator)
109
+ │ ├── ColorReporter (decorator)
110
+ │ └── ReporterFactory
111
+ ├── Commands (CLI commands)
112
+ │ ├── CheckCommand
113
+ │ └── ListCommand
114
+ └── Cli (Thor-based CLI)
115
+ ----
116
+
117
+ .Data Flow
118
+ [source]
119
+ ----
120
+ File Input
121
+
122
+
123
+ CLI (CheckCommand)
124
+
125
+ ├─► ProfileManager (load profile)
126
+
127
+ └─► ReporterFactory (create reporter)
128
+
129
+
130
+ ValidationService
131
+
132
+ ├─► BinData Parser (read PNG structure)
133
+
134
+ ├─► ChunkRegistry (lookup validators)
135
+
136
+ └─► Validators (validate chunks)
137
+
138
+ ├─► BaseValidator methods (check_crc, check_length, etc.)
139
+
140
+ └─► ValidationContext (store state)
141
+
142
+
143
+ ValidationResult
144
+
145
+ ├─► FileAnalysis (file-level results)
146
+ ├─► ChunkInfo[] (chunk-level results)
147
+ └─► ValidationError[] (errors/warnings/info)
148
+
149
+
150
+ Reporter (format output)
151
+
152
+ ├─► SummaryReporter
153
+ ├─► VerboseReporter
154
+ ├─► VeryVerboseReporter
155
+ └─► QuietReporter
156
+
157
+
158
+ Output (STDOUT)
159
+ ----
160
+
161
+ == Installation
162
+
163
+ Add this line to your application's Gemfile:
164
+
165
+ [source,ruby]
166
+ ----
167
+ gem "png_conform"
168
+ ----
169
+
170
+ And then execute:
171
+
172
+ [source,shell]
173
+ ----
174
+ bundle install
175
+ ----
176
+
177
+ Or install it yourself as:
178
+
179
+ [source,shell]
180
+ ----
181
+ gem install png_conform
182
+ ----
183
+
184
+ [[cli-usage]]
185
+ == CLI Usage
186
+
187
+ === General
188
+
189
+ PngConform provides a command-line interface with pngcheck-compatible options.
190
+
191
+ === Basic usage
192
+
193
+ [source,shell]
194
+ ----
195
+ png_conform check FILE [FILE...]
196
+ ----
197
+
198
+ .Basic validation (successful)
199
+ [example]
200
+ ====
201
+ [source,shell]
202
+ ----
203
+ $ png_conform check image.png
204
+ ✅ OK: image.png (800x600, 8-bit/color RGB, non-interlaced)
205
+ ----
206
+ ====
207
+
208
+ .Basic validation (with errors)
209
+ [example]
210
+ ====
211
+ [source,shell]
212
+ ----
213
+ $ png_conform check corrupted.png
214
+ ❌ ERROR: corrupted.png, (PNG, 1024 bytes, 3 chunks)
215
+ ERROR: IHDR: invalid bit depth 3 for color type 2
216
+ ERROR: CRC error in chunk IDAT
217
+ WARNING: Missing recommended chunk gAMA
218
+ ----
219
+ ====
220
+
221
+ === CLI options
222
+
223
+ ==== Verbosity options
224
+
225
+ `-v, --verbose`:: Display chunk-level information
226
+ `-vv, --very-verbose`:: Display detailed chunk data
227
+ `-q, --quiet`:: Suppress all output except errors
228
+
229
+ .Verbose output example (with colors and emojis)
230
+ [example]
231
+ ====
232
+ [source,shell]
233
+ ----
234
+ $ png_conform check -v image.png
235
+ 📄 image.png (32768 bytes)
236
+
237
+ ✓ 📦 IHDR at 0x0000c (13 bytes)
238
+ ✓ 📦 gAMA at 0x00025 (4 bytes)
239
+ ✓ 📦 cHRM at 0x00035 (32 bytes)
240
+ ✓ 📦 IDAT at 0x00061 (65445 bytes)
241
+ ✓ 📦 IEND at 0x10072 (0 bytes)
242
+
243
+ ✓ No errors detected in image.png (5 chunks)
244
+ ----
245
+ ====
246
+
247
+ .Verbose with errors
248
+ [example]
249
+ ====
250
+ [source,shell]
251
+ ----
252
+ $ png_conform check -v corrupted.png
253
+ 📄 corrupted.png (1024 bytes)
254
+
255
+ ✗ 📦 IHDR at 0x0000c (13 bytes)
256
+ ✓ 📦 PLTE at 0x00025 (12 bytes)
257
+ ✗ 📦 IDAT at 0x00035 (891 bytes)
258
+ ✓ 📦 IEND at 0x003ec (0 bytes)
259
+
260
+ VALIDATION ERRORS:
261
+ ❌ ERROR: IHDR: invalid bit depth 3 for color type 2
262
+ ❌ ERROR: CRC error in chunk IDAT (expected 0x12345678, got 0x87654321)
263
+ ⚠️ WARNING: Missing recommended chunk gAMA
264
+
265
+ ❌ 2 errors, 1 warning in corrupted.png (4 chunks)
266
+ ----
267
+ ====
268
+
269
+ .Comprehensive YAML output (includes image info, resolution, and recommendations)
270
+ [example]
271
+ ====
272
+ [source,shell]
273
+ ----
274
+ $ png_conform check --format yaml image.png
275
+ ---
276
+ filename: "./spec/fixtures/pngsuite/background/bgwn6a08.png"
277
+ file_type: PNG
278
+ file_size: 202
279
+ compression_ratio: -97.3
280
+ crc_errors_count: 0
281
+ valid: true
282
+ image:
283
+ width: 32
284
+ height: 32
285
+ bit_depth: 8
286
+ color_type: 6
287
+ color_type_name: RGBA
288
+ interlaced: false
289
+ chunks:
290
+ total: 5
291
+ types:
292
+ - IDAT
293
+ - IEND
294
+ - IHDR
295
+ - bKGD
296
+ - gAMA
297
+ resolution:
298
+ dimensions: 32x32
299
+ megapixels: 0.0
300
+ dpi:
301
+ retina:
302
+ at_1x: 14.1x14.1pt
303
+ at_2x: 7.1x7.1pt
304
+ at_3x: 4.7x4.7pt
305
+ recommended: "@1x (too small for higher densities)"
306
+ ios:
307
+ - Custom size
308
+ android: ldpi or mdpi
309
+ recommendations:
310
+ - Image is too small for Retina displays - consider @2x/@3x versions
311
+ - Add pHYs chunk with DPI information for print compatibility
312
+ ----
313
+ ====
314
+
315
+ .JSON output example
316
+ [example]
317
+ ====
318
+ [source,shell]
319
+ ----
320
+ $ png_conform check --format json image.png
321
+ {
322
+ "filename": "image.png",
323
+ "file_type": "PNG",
324
+ "file_size": 32768,
325
+ "crc_errors_count": 0,
326
+ "compression_ratio": -89.2
327
+ }
328
+ ----
329
+ ====
330
+ .YAML with errors
331
+ [example]
332
+ ====
333
+ [source,shell]
334
+ ----
335
+ $ png_conform check --format yaml corrupted.png
336
+ ---
337
+ filename: corrupted.png
338
+ file_type: PNG
339
+ file_size: 1024
340
+ crc_errors_count: 1
341
+ valid: false
342
+ errors:
343
+ - severity: error
344
+ message: 'IHDR: invalid bit depth 3 for color type 2'
345
+ chunk_type: IHDR
346
+ - severity: error
347
+ message: 'CRC error in chunk IDAT'
348
+ chunk_type: IDAT
349
+ expected: '0x12345678'
350
+ actual: '0x87654321'
351
+ - severity: warning
352
+ message: Missing recommended chunk gAMA
353
+ ----
354
+ ====
355
+
356
+ .JSON with errors
357
+ [example]
358
+ ====
359
+ [source,shell]
360
+ ----
361
+ $ png_conform check --format json corrupted.png
362
+ {
363
+ "filename": "corrupted.png",
364
+ "file_type": "PNG",
365
+ "file_size": 1024,
366
+ "crc_errors_count": 1,
367
+ "valid": false,
368
+ "errors": [
369
+ {
370
+ "severity": "error",
371
+ "message": "IHDR: invalid bit depth 3 for color type 2",
372
+ "chunk_type": "IHDR"
373
+ },
374
+ {
375
+ "severity": "error",
376
+ "message": "CRC error in chunk IDAT",
377
+ "chunk_type": "IDAT",
378
+ "expected": "0x12345678",
379
+ "actual": "0x87654321"
380
+ },
381
+ {
382
+ "severity": "warning",
383
+ "message": "Missing recommended chunk gAMA"
384
+ }
385
+ ]
386
+ }
387
+ ----
388
+ ====
389
+
390
+ .Disable colors
391
+ [example]
392
+ ====
393
+ [source,shell]
394
+ ----
395
+ $ png_conform check --no-color image.png
396
+ OK: image.png (800x600, 8-bit/color RGB, non-interlaced)
397
+ ----
398
+ ====
399
+
400
+ ==== Output options
401
+
402
+ `-f, --format FORMAT`:: Output format: text (default), yaml, json
403
+ `--no-color`:: Disable colored output
404
+ `-c, --color`:: Display RGB color values (only with `-p` or `-t`)
405
+ `-p, --palette`:: Display palette entries
406
+ `-t, --text`:: Display text chunk contents
407
+ `-7, --seven-bit`:: Check for 7-bit ASCII in tEXt chunks
408
+
409
+ .Palette display example
410
+ [example]
411
+ ====
412
+ [source,shell]
413
+ ----
414
+ $ png_conform check -p indexed.png
415
+ OK: indexed.png (32x32, 2-bit palette, non-interlaced)
416
+ chunk IHDR at offset 0x0000c, length 13
417
+ chunk PLTE at offset 0x00025, length 12
418
+ 0: (255,255,255) = gray100
419
+ 1: (204,204,204) = gray80
420
+ 2: (153,153,153) = gray60
421
+ 3: (102,102,102) = gray40
422
+ chunk IDAT at offset 0x0003d, length 147
423
+ chunk IEND at offset 0x000d4, length 0
424
+ ----
425
+ ====
426
+
427
+ ==== Profile options
428
+
429
+ `--profile PROFILE`:: Use validation profile (minimal, web, print, archive, strict, default)
430
+ `--strict`:: Shortcut for `--profile strict`
431
+
432
+ .Profile validation example
433
+ [example]
434
+ ====
435
+ [source,shell]
436
+ ----
437
+ $ png_conform check --profile web image.png
438
+ WARN: image.png: missing required chunk gAMA (web profile)
439
+ WARN: image.png: missing required chunk sRGB (web profile)
440
+ OK: image.png (800x600, 8-bit/color RGB, non-interlaced)
441
+ ----
442
+ ====
443
+
444
+ ==== Analysis options
445
+
446
+ `--resolution`:: Display resolution and Retina analysis (@1x/@2x/@3x)
447
+ `--optimize`:: Show file size optimization suggestions
448
+ `--metrics`:: Display comprehensive metrics for CI/CD
449
+ `--mobile-ready`:: Check mobile and Retina readiness
450
+
451
+ .Resolution and Retina analysis (shown by default)
452
+ [example]
453
+ ====
454
+ [source,shell]
455
+ ----
456
+ $ png_conform check icon.png
457
+ ✅ OK: icon.png, (PNG, 202 bytes, 5 chunks, 🗜️ -97.3%)
458
+
459
+ RESOLUTION ANALYSIS:
460
+ Dimensions: 32x32 (0.0 megapixels)
461
+ DPI: Not specified
462
+
463
+ Retina Analysis:
464
+ @1x: 14.1x14.1pt (Small icon)
465
+ @2x: 7.1x7.1pt (Small icon)
466
+ @3x: 4.7x4.7pt (Small icon)
467
+ Recommended: @1x (too small for higher densities)
468
+ iOS: Custom size
469
+ Android: ldpi or mdpi
470
+
471
+ Recommendations:
472
+ [HIGH] Image is too small for Retina displays - consider @2x/@3x versions
473
+ [MEDIUM] Add pHYs chunk with DPI information for print compatibility
474
+ ----
475
+ ====
476
+
477
+ .Optimization suggestions
478
+ [example]
479
+ ====
480
+ [source,shell]
481
+ ----
482
+ $ png_conform check --optimize large-photo.png
483
+ ✅ OK: large-photo.png, (PNG, 4.5 MB, 8 chunks, 🗜️ -42.1%)
484
+
485
+ OPTIMIZATION SUGGESTIONS:
486
+ 1. [HIGH] Convert from 16-bit to 8-bit depth (saves ~45% = 2.1MB)
487
+ 2. [MEDIUM] Remove 3 unnecessary chunks (tIME, pHYs, oFFs) (saves 156 bytes)
488
+ 3. [LOW] Remove interlacing for smaller file size (saves ~15% = 680KB)
489
+
490
+ Total Potential Savings: 2.8MB (62.2%)
491
+ ----
492
+ ====
493
+
494
+ .Mobile and Retina readiness check
495
+ [example]
496
+ ====
497
+ [source,shell]
498
+ ----
499
+ $ png_conform check --mobile-ready app-icon@2x.png
500
+ ✅ OK: app-icon@2x.png, (PNG, 2.3 KB, 5 chunks, 🗜️ -88.5%)
501
+
502
+ MOBILE & RETINA READINESS:
503
+ Status: ✓ READY
504
+
505
+ Checks:
506
+ Retina Ready: ✓
507
+ Mobile Friendly: ✓
508
+ Web Suitable: ✓
509
+
510
+ Retina Densities:
511
+ @1x: 44.0x44.0pt
512
+ @2x: 22.0x22.0pt
513
+ @3x: 14.7x14.7pt
514
+ Recommended: @2x
515
+
516
+ Screen Coverage:
517
+ Mobile (375x667): 23.5% x 13.2%
518
+ Desktop (1920x1080): 4.6% x 8.1%
519
+
520
+ Load Time: 25ms (fast)
521
+ ----
522
+ ====
523
+
524
+ === List profiles
525
+
526
+ Display available validation profiles:
527
+
528
+ [source,shell]
529
+ ----
530
+ png_conform list
531
+ ----
532
+
533
+ .Profile listing output
534
+ [example]
535
+ ====
536
+ [source,shell]
537
+ ----
538
+ $ png_conform list
539
+ Available validation profiles:
540
+
541
+ minimal
542
+ Required chunks: IHDR, IDAT, IEND
543
+ Optional chunks: (any)
544
+ Prohibited chunks: (none)
545
+
546
+ web
547
+ Required chunks: IHDR, IDAT, IEND, gAMA, sRGB
548
+ Optional chunks: tRNS, bKGD, tEXt, iTXt, zTXt
549
+ Prohibited chunks: (none)
550
+
551
+ print
552
+ Required chunks: IHDR, IDAT, IEND, iCCP
553
+ Optional chunks: gAMA, cHRM, tRNS, bKGD
554
+ Prohibited chunks: sRGB
555
+
556
+ archive
557
+ Required chunks: IHDR, IDAT, IEND, tIME
558
+ Optional chunks: (any)
559
+ Prohibited chunks: (none)
560
+
561
+ strict
562
+ Required chunks: IHDR, IDAT, IEND
563
+ Optional chunks: (critical chunks only)
564
+ Prohibited chunks: (unknown chunks)
565
+
566
+ default
567
+ Required chunks: IHDR, IDAT, IEND
568
+ Optional chunks: (any)
569
+ Prohibited chunks: (none)
570
+ ----
571
+ ====
572
+
573
+ [[ruby-api]]
574
+ == Ruby API
575
+
576
+ === General
577
+
578
+ PngConform provides a comprehensive Ruby API for programmatic validation.
579
+
580
+ === Basic validation
581
+
582
+ [source,ruby]
583
+ ----
584
+ require "png_conform"
585
+
586
+ # Validate a file
587
+ service = PngConform::Services::ValidationService.new
588
+ result = service.validate_file("image.png")
589
+
590
+ if result.valid?
591
+ puts "File is valid"
592
+ puts "Image: #{result.image_info.width}x#{result.image_info.height}"
593
+ puts "Chunks: #{result.chunks.count}"
594
+ else
595
+ puts "Validation errors:"
596
+ result.errors.each do |error|
597
+ puts " #{error.severity}: #{error.message}"
598
+ end
599
+ end
600
+ ----
601
+
602
+ === Using profiles
603
+
604
+ [source,ruby]
605
+ ----
606
+ # Load a specific profile
607
+ profile_mgr = PngConform::Services::ProfileManager.new
608
+ profile = profile_mgr.load_profile("web")
609
+
610
+ # Validate with profile
611
+ service = PngConform::Services::ValidationService.new
612
+ result = service.validate_file("image.png", profile: profile)
613
+
614
+ # Check profile conformance
615
+ profile_result = profile_mgr.validate_file_against_profile(
616
+ "image.png",
617
+ "web"
618
+ )
619
+
620
+ if profile_result.profile_errors.any?
621
+ puts "Profile violations:"
622
+ profile_result.profile_errors.each do |error|
623
+ puts " #{error.message}"
624
+ end
625
+ end
626
+ ----
627
+
628
+ === Custom reporters
629
+
630
+ [source,ruby]
631
+ ----
632
+ # Use specific reporter
633
+ reporter = PngConform::Reporters::VerboseReporter.new
634
+ service = PngConform::Services::ValidationService.new
635
+ result = service.validate_file("image.png")
636
+
637
+ reporter.report(result)
638
+
639
+ # Create custom reporter combinations
640
+ factory = PngConform::Reporters::ReporterFactory.new
641
+ reporter = factory.create_reporter(
642
+ verbose: true,
643
+ show_palette: true,
644
+ show_text: true,
645
+ colorize: true
646
+ )
647
+
648
+ reporter.report(result)
649
+ ----
650
+
651
+ === Accessing chunk data
652
+
653
+ [source,ruby]
654
+ ----
655
+ result = service.validate_file("image.png")
656
+
657
+ # Iterate through chunks
658
+ result.chunks.each do |chunk|
659
+ puts "Chunk: #{chunk.type}"
660
+ puts " Offset: #{chunk.offset}"
661
+ puts " Length: #{chunk.length}"
662
+ puts " CRC: 0x#{chunk.crc.to_s(16).upcase}"
663
+
664
+ # Access decoded data
665
+ if chunk.decoded_data
666
+ case chunk.type
667
+ when "IHDR"
668
+ puts " Dimensions: #{chunk.decoded_data.width}x#{chunk.decoded_data.height}"
669
+ puts " Bit depth: #{chunk.decoded_data.bit_depth}"
670
+ puts " Color type: #{chunk.decoded_data.color_type}"
671
+ when "gAMA"
672
+ puts " Gamma: #{chunk.decoded_data.gamma}"
673
+ when "tEXt"
674
+ puts " Keyword: #{chunk.decoded_data.keyword}"
675
+ puts " Text: #{chunk.decoded_data.text}"
676
+ end
677
+ end
678
+ end
679
+ ----
680
+
681
+ [[chunk-validation]]
682
+ == Chunk Validation
683
+
684
+ === General
685
+
686
+ PngConform validates all standard PNG chunk types with dedicated validators.
687
+
688
+ === Critical chunks
689
+
690
+ ==== IHDR (image header)
691
+
692
+ The IHDR chunk must be the first chunk and contains image metadata.
693
+
694
+ Validation checks:
695
+
696
+ * Width and height must be non-zero
697
+ * Bit depth must be valid for color type
698
+ * Color type must be 0, 2, 3, 4, or 6
699
+ * Compression method must be 0 (deflate)
700
+ * Filter method must be 0 (adaptive)
701
+ * Interlace method must be 0 (none) or 1 (Adam7)
702
+
703
+ ==== PLTE (palette)
704
+
705
+ Required for indexed-color images, optional for truecolor.
706
+
707
+ Validation checks:
708
+
709
+ * Length must be divisible by 3 (RGB triplets)
710
+ * Number of entries must not exceed 2^bit_depth
711
+ * Must appear before IDAT chunks
712
+ * Required for color type 3
713
+ * Must not appear for grayscale images
714
+
715
+ ==== IDAT (image data)
716
+
717
+ Contains compressed image data. Multiple IDAT chunks must be consecutive.
718
+
719
+ Validation checks:
720
+
721
+ * IDAT chunks must be consecutive
722
+ * Compressed data must be valid zlib format
723
+ * Decompressed data must match expected size
724
+ * Filter types must be valid (0-4)
725
+
726
+ ==== IEND (image trailer)
727
+
728
+ Marks the end of the PNG file.
729
+
730
+ Validation checks:
731
+
732
+ * Must be the last chunk
733
+ * Data length must be 0
734
+ * Must appear exactly once
735
+
736
+ === Ancillary chunks
737
+
738
+ ==== Color space chunks
739
+
740
+ `gAMA`:: Gamma correction value
741
+ `cHRM`:: Primary chromaticities and white point
742
+ `sRGB`:: Standard RGB color space
743
+ `iCCP`:: ICC color profile
744
+ `cICP`:: Coding-independent code points (HDR)
745
+
746
+ ==== Transparency chunks
747
+
748
+ `tRNS`:: Transparency information
749
+ `bKGD`:: Background color
750
+
751
+ ==== Text chunks
752
+
753
+ `tEXt`:: Uncompressed Latin-1 text
754
+ `zTXt`:: Compressed Latin-1 text
755
+ `iTXt`:: International UTF-8 text
756
+
757
+ ==== Physical dimensions
758
+
759
+ `pHYs`:: Physical pixel dimensions
760
+ `sCAL`:: Physical scale
761
+
762
+ ==== Time stamp
763
+
764
+ `tIME`:: Last modification time
765
+
766
+ ==== Other chunks
767
+
768
+ `hIST`:: Palette histogram
769
+ `sPLT`:: Suggested palette
770
+ `sBIT`:: Significant bits
771
+ `oFFs`:: Image offset
772
+ `pCAL`:: Pixel calibration
773
+ `sTER`:: Stereo image indicator
774
+ `mDCv`:: Mastering display color volume (HDR)
775
+
776
+ [[validation-profiles]]
777
+ == Validation Profiles
778
+
779
+ === General
780
+
781
+ Profiles define conformance requirements for different use cases.
782
+
783
+ === Available profiles
784
+
785
+ ==== minimal
786
+
787
+ Basic PNG structure validation only.
788
+
789
+ * Required: IHDR, IDAT, IEND
790
+ * Optional: Any chunks
791
+ * Prohibited: None
792
+
793
+ ==== web
794
+
795
+ Optimized for web display.
796
+
797
+ * Required: IHDR, IDAT, IEND, gAMA, sRGB
798
+ * Optional: tRNS, bKGD, tEXt, iTXt, zTXt
799
+ * Prohibited: None
800
+
801
+ ==== print
802
+
803
+ For high-quality print reproduction.
804
+
805
+ * Required: IHDR, IDAT, IEND, iCCP
806
+ * Optional: gAMA, cHRM, tRNS, bKGD
807
+ * Prohibited: sRGB
808
+
809
+ ==== archive
810
+
811
+ Long-term preservation requirements.
812
+
813
+ * Required: IHDR, IDAT, IEND, tIME
814
+ * Optional: Any chunks
815
+ * Prohibited: None
816
+
817
+ ==== strict
818
+
819
+ Only standard chunks allowed.
820
+
821
+ * Required: IHDR, IDAT, IEND
822
+ * Optional: Critical chunks only
823
+ * Prohibited: Unknown chunks
824
+
825
+ ==== default
826
+
827
+ Standard PNG validation.
828
+
829
+ * Required: IHDR, IDAT, IEND
830
+ * Optional: Any chunks
831
+ * Prohibited: None
832
+
833
+ [[development]]
834
+ == Development
835
+
836
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
837
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
838
+ prompt that will allow you to experiment.
839
+
840
+ To install this gem onto your local machine, run `bundle exec rake install`.
841
+
842
+ == Contributing
843
+
844
+ Bug reports and pull requests are welcome on GitHub at
845
+ https://github.com/claricle/png_conform.
846
+
847
+
848
+ == Credits
849
+
850
+ PngConform is inspired by https://github.com/pnggroup/pngcheck/[pngcheck],
851
+ originally developed by Greg Roelofs and contributors, and now maintained by the
852
+ PNG Development Group.
853
+
854
+
855
+ == Copyright and license
856
+
857
+ Copyright Ribose.
858
+
859
+ The gem is available as open source under the Ribose BSD-2-Clause License.