0.0
The project is in a healthy, maintained state
PngConform provides a comprehensive PNG, MNG, and JNG file validation library in pure Ruby. It validates file structure, chunk validity, CRC checksums, zlib compression, and profile conformance. Built with a layered architecture using BinData for binary parsing, Lutaml::Model for domain models, and Thor for CLI. Supports PNG baseline, MNG-VLC, MNG-LC, and JNG profiles with detailed validation reporting compatible with pngcheck output.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

~> 2.5
~> 1.4
 Project Readme

PngConform: A Comprehensive PNG Validator in Ruby

Gem Version License Build Status

Purpose

PngConform is a pure Ruby PNG file validator with comprehensive chunk validation and profile support. It validates file structure, chunk validity, CRC checksums, chunk ordering, and profile conformance for PNG files.

The library provides pngcheck-compatible validation output while leveraging modern Ruby object-oriented design principles and a layered architecture for extensibility and maintainability.

Features

  • 46 Chunk Validators: Complete validation of PNG (24), APNG (3), MNG (16), and JNG (3) chunks

  • PNG 3rd Edition Support: Includes cICP and mDCv chunks for HDR content

  • MNG/JNG Support: Full Multiple-image Network Graphics and JPEG Network Graphics validation

  • CRC Validation: CRC-32 checksum verification for all chunks with detailed error reporting

  • Chunk Ordering: Validates proper chunk sequence and dependencies

  • 6 Validation Profiles: Minimal, web, print, archive, strict, and default profiles

  • Enhanced Output Formats:

    • Text output with colors and emojis (✅/❌/⚠️)

    • Comprehensive YAML output with image info, resolution, and recommendations

    • Comprehensive JSON output for CI/CD integration

    • Compression ratio reporting

  • Analysis Features:

    • Retina/Resolution Analysis: @1x/@2x/@3x calculations for mobile developers

    • Optimization Suggestions: File size reduction recommendations

    • Comprehensive Metrics: Detailed metrics for CI/CD automation

    • iOS/Android Support: Asset catalog and density bucket suggestions

  • Multiple Output Modes: Summary, verbose, very verbose, quiet, with optional palette/text/color output

  • Clean Architecture: Layered OO design following MECE principles

  • CLI Interface: Thor-based command-line tool with pngcheck-compatible options plus modern analysis features

  • Comprehensive Testing: Extensive RSpec test suite (936 examples, 100% passing)

Architecture

PngConform uses a layered architecture with strict separation of concerns:

Layered Architecture
╔════════════════════════════════════════════════════════════╗
║                    Presentation Layer                      ║
║                    (CLI, Reporters)                        ║
╚════════════════════════════════════════════════════════════╝
                            │
╔════════════════════════════════════════════════════════════╗
║                     Service Layer                          ║
║           (ValidationService, ProfileManager)              ║
╚════════════════════════════════════════════════════════════╝
                            │
╔════════════════════════════════════════════════════════════╗
║                  Business Logic Layer                      ║
║         (Validators, ChunkRegistry, Readers)               ║
╚════════════════════════════════════════════════════════════╝
                            │
╔════════════════════════════════════════════════════════════╗
║                    Domain Model Layer                      ║
║     (ChunkInfo, ValidationResult, FileAnalysis)            ║
╚════════════════════════════════════════════════════════════╝
                            │
╔════════════════════════════════════════════════════════════╗
║                  Binary Parsing Layer                      ║
║              (BinData structures)                          ║
╚════════════════════════════════════════════════════════════╝
Module Structure
PngConform
├── BinData (Binary parsing structures)
│   ├── ChunkStructure
│   ├── PngFile
│   ├── MngFile ✅
│   └── JngFile ✅
├── Models (Domain models)
│   ├── ChunkInfo
│   ├── ValidationResult
│   ├── ValidationError
│   ├── FileAnalysis
│   ├── ImageInfo
│   ├── CompressionInfo
│   └── DecodedChunkData
├── Validators (Validation logic)
│   ├── BaseValidator
│   ├── ChunkRegistry
│   ├── Critical (IHDR, PLTE, IDAT, IEND)
│   └── Ancillary (gAMA, tRNS, tEXt, etc. - 20 validators)
├── Services (Orchestration)
│   ├── ValidationService
│   └── ProfileManager
├── Readers (File reading strategies)
│   ├── StreamingReader
│   └── FullLoadReader
├── Reporters (Output formatting)
│   ├── BaseReporter
│   ├── SummaryReporter
│   ├── VerboseReporter
│   ├── VeryVerboseReporter
│   ├── QuietReporter
│   ├── PaletteReporter (decorator)
│   ├── TextReporter (decorator)
│   ├── ColorReporter (decorator)
│   └── ReporterFactory
├── Commands (CLI commands)
│   ├── CheckCommand
│   └── ListCommand
└── Cli (Thor-based CLI)
Data Flow
File Input
    │
    ▼
CLI (CheckCommand)
    │
    ├─► ProfileManager (load profile)
    │
    └─► ReporterFactory (create reporter)
        │
        ▼
ValidationService
    │
    ├─► BinData Parser (read PNG structure)
    │
    ├─► ChunkRegistry (lookup validators)
    │
    └─► Validators (validate chunks)
        │
        ├─► BaseValidator methods (check_crc, check_length, etc.)
        │
        └─► ValidationContext (store state)
            │
            ▼
    ValidationResult
        │
        ├─► FileAnalysis (file-level results)
        ├─► ChunkInfo[] (chunk-level results)
        └─► ValidationError[] (errors/warnings/info)
            │
            ▼
    Reporter (format output)
        │
        ├─► SummaryReporter
        ├─► VerboseReporter
        ├─► VeryVerboseReporter
        └─► QuietReporter
            │
            ▼
    Output (STDOUT)

Installation

Add this line to your application’s Gemfile:

gem "png_conform"

And then execute:

bundle install

Or install it yourself as:

gem install png_conform

CLI Usage

General

PngConform provides a command-line interface with pngcheck-compatible options.

Basic usage

png_conform check FILE [FILE...]
Example 1. Basic validation (successful)
$ png_conform check image.png
✅ OK: image.png (800x600, 8-bit/color RGB, non-interlaced)
Example 2. Basic validation (with errors)
$ png_conform check corrupted.png
❌ ERROR: corrupted.png, (PNG, 1024 bytes, 3 chunks)
  ERROR: IHDR: invalid bit depth 3 for color type 2
  ERROR: CRC error in chunk IDAT
  WARNING: Missing recommended chunk gAMA

CLI options

Verbosity options

-v, --verbose

Display chunk-level information

-vv, --very-verbose

Display detailed chunk data

-q, --quiet

Suppress all output except errors

Example 3. Verbose output example (with colors and emojis)
$ png_conform check -v image.png
📄 image.png (32768 bytes)

  ✓ 📦 IHDR at 0x0000c (13 bytes)
  ✓ 📦 gAMA at 0x00025 (4 bytes)
  ✓ 📦 cHRM at 0x00035 (32 bytes)
  ✓ 📦 IDAT at 0x00061 (65445 bytes)
  ✓ 📦 IEND at 0x10072 (0 bytes)

✓ No errors detected in image.png (5 chunks)
Example 4. Verbose with errors
$ png_conform check -v corrupted.png
📄 corrupted.png (1024 bytes)

  ✗ 📦 IHDR at 0x0000c (13 bytes)
  ✓ 📦 PLTE at 0x00025 (12 bytes)
  ✗ 📦 IDAT at 0x00035 (891 bytes)
  ✓ 📦 IEND at 0x003ec (0 bytes)

VALIDATION ERRORS:
  ❌ ERROR: IHDR: invalid bit depth 3 for color type 2
  ❌ ERROR: CRC error in chunk IDAT (expected 0x12345678, got 0x87654321)
  ⚠️  WARNING: Missing recommended chunk gAMA

❌ 2 errors, 1 warning in corrupted.png (4 chunks)
Example 5. Comprehensive YAML output (includes image info, resolution, and recommendations)
$ png_conform check --format yaml image.png
---
filename: "./spec/fixtures/pngsuite/background/bgwn6a08.png"
file_type: PNG
file_size: 202
compression_ratio: -97.3
crc_errors_count: 0
valid: true
image:
  width: 32
  height: 32
  bit_depth: 8
  color_type: 6
  color_type_name: RGBA
  interlaced: false
chunks:
  total: 5
  types:
  - IDAT
  - IEND
  - IHDR
  - bKGD
  - gAMA
resolution:
  dimensions: 32x32
  megapixels: 0.0
  dpi:
  retina:
    at_1x: 14.1x14.1pt
    at_2x: 7.1x7.1pt
    at_3x: 4.7x4.7pt
    recommended: "@1x (too small for higher densities)"
    ios:
    - Custom size
    android: ldpi or mdpi
recommendations:
- Image is too small for Retina displays - consider @2x/@3x versions
- Add pHYs chunk with DPI information for print compatibility
Example 6. JSON output example
$ png_conform check --format json image.png
{
  "filename": "image.png",
  "file_type": "PNG",
  "file_size": 32768,
  "crc_errors_count": 0,
  "compression_ratio": -89.2
}
Example 7. YAML with errors
$ png_conform check --format yaml corrupted.png
---
filename: corrupted.png
file_type: PNG
file_size: 1024
crc_errors_count: 1
valid: false
errors:
- severity: error
  message: 'IHDR: invalid bit depth 3 for color type 2'
  chunk_type: IHDR
- severity: error
  message: 'CRC error in chunk IDAT'
  chunk_type: IDAT
  expected: '0x12345678'
  actual: '0x87654321'
- severity: warning
  message: Missing recommended chunk gAMA
Example 8. JSON with errors
$ png_conform check --format json corrupted.png
{
  "filename": "corrupted.png",
  "file_type": "PNG",
  "file_size": 1024,
  "crc_errors_count": 1,
  "valid": false,
  "errors": [
    {
      "severity": "error",
      "message": "IHDR: invalid bit depth 3 for color type 2",
      "chunk_type": "IHDR"
    },
    {
      "severity": "error",
      "message": "CRC error in chunk IDAT",
      "chunk_type": "IDAT",
      "expected": "0x12345678",
      "actual": "0x87654321"
    },
    {
      "severity": "warning",
      "message": "Missing recommended chunk gAMA"
    }
  ]
}
Example 9. Disable colors
$ png_conform check --no-color image.png
OK: image.png (800x600, 8-bit/color RGB, non-interlaced)

Output options

-f, --format FORMAT

Output format: text (default), yaml, json

--no-color

Disable colored output

-c, --color

Display RGB color values (only with -p or -t)

-p, --palette

Display palette entries

-t, --text

Display text chunk contents

-7, --seven-bit

Check for 7-bit ASCII in tEXt chunks

Example 10. Palette display example
$ png_conform check -p indexed.png
OK: indexed.png (32x32, 2-bit palette, non-interlaced)
  chunk IHDR at offset 0x0000c, length 13
  chunk PLTE at offset 0x00025, length 12
    0:  (255,255,255) = gray100
    1:  (204,204,204) = gray80
    2:  (153,153,153) = gray60
    3:  (102,102,102) = gray40
  chunk IDAT at offset 0x0003d, length 147
  chunk IEND at offset 0x000d4, length 0

Profile options

--profile PROFILE

Use validation profile (minimal, web, print, archive, strict, default)

--strict

Shortcut for --profile strict

Example 11. Profile validation example
$ png_conform check --profile web image.png
WARN: image.png: missing required chunk gAMA (web profile)
WARN: image.png: missing required chunk sRGB (web profile)
OK: image.png (800x600, 8-bit/color RGB, non-interlaced)

Analysis options

--resolution

Display resolution and Retina analysis (@1x/@2x/@3x)

--optimize

Show file size optimization suggestions

--metrics

Display comprehensive metrics for CI/CD

--mobile-ready

Check mobile and Retina readiness

Example 12. Resolution and Retina analysis (shown by default)
$ png_conform check icon.png
✅ OK: icon.png, (PNG, 202 bytes, 5 chunks, 🗜️ -97.3%)

RESOLUTION ANALYSIS:
  Dimensions: 32x32 (0.0 megapixels)
  DPI: Not specified

  Retina Analysis:
    @1x: 14.1x14.1pt (Small icon)
    @2x: 7.1x7.1pt (Small icon)
    @3x: 4.7x4.7pt (Small icon)
    Recommended: @1x (too small for higher densities)
    iOS: Custom size
    Android: ldpi or mdpi

  Recommendations:
    [HIGH] Image is too small for Retina displays - consider @2x/@3x versions
    [MEDIUM] Add pHYs chunk with DPI information for print compatibility
Example 13. Optimization suggestions
$ png_conform check --optimize large-photo.png
✅ OK: large-photo.png, (PNG, 4.5 MB, 8 chunks, 🗜️ -42.1%)

OPTIMIZATION SUGGESTIONS:
  1. [HIGH] Convert from 16-bit to 8-bit depth (saves ~45% = 2.1MB)
  2. [MEDIUM] Remove 3 unnecessary chunks (tIME, pHYs, oFFs) (saves 156 bytes)
  3. [LOW] Remove interlacing for smaller file size (saves ~15% = 680KB)

  Total Potential Savings: 2.8MB (62.2%)
Example 14. Mobile and Retina readiness check
$ png_conform check --mobile-ready app-icon@2x.png
✅ OK: app-icon@2x.png, (PNG, 2.3 KB, 5 chunks, 🗜️ -88.5%)

MOBILE & RETINA READINESS:
  Status: ✓ READY

  Checks:
    Retina Ready: ✓
    Mobile Friendly: ✓
    Web Suitable: ✓

  Retina Densities:
    @1x: 44.0x44.0pt
    @2x: 22.0x22.0pt
    @3x: 14.7x14.7pt
    Recommended: @2x

  Screen Coverage:
    Mobile (375x667): 23.5% x 13.2%
    Desktop (1920x1080): 4.6% x 8.1%

  Load Time: 25ms (fast)

List profiles

Display available validation profiles:

png_conform list
Example 15. Profile listing output
$ png_conform list
Available validation profiles:

minimal
  Required chunks: IHDR, IDAT, IEND
  Optional chunks: (any)
  Prohibited chunks: (none)

web
  Required chunks: IHDR, IDAT, IEND, gAMA, sRGB
  Optional chunks: tRNS, bKGD, tEXt, iTXt, zTXt
  Prohibited chunks: (none)

print
  Required chunks: IHDR, IDAT, IEND, iCCP
  Optional chunks: gAMA, cHRM, tRNS, bKGD
  Prohibited chunks: sRGB

archive
  Required chunks: IHDR, IDAT, IEND, tIME
  Optional chunks: (any)
  Prohibited chunks: (none)

strict
  Required chunks: IHDR, IDAT, IEND
  Optional chunks: (critical chunks only)
  Prohibited chunks: (unknown chunks)

default
  Required chunks: IHDR, IDAT, IEND
  Optional chunks: (any)
  Prohibited chunks: (none)

Ruby API

General

PngConform provides a comprehensive Ruby API for programmatic validation.

Basic validation

require "png_conform"

# Validate a file
service = PngConform::Services::ValidationService.new
result = service.validate_file("image.png")

if result.valid?
  puts "File is valid"
  puts "Image: #{result.image_info.width}x#{result.image_info.height}"
  puts "Chunks: #{result.chunks.count}"
else
  puts "Validation errors:"
  result.errors.each do |error|
    puts "  #{error.severity}: #{error.message}"
  end
end

Using profiles

# Load a specific profile
profile_mgr = PngConform::Services::ProfileManager.new
profile = profile_mgr.load_profile("web")

# Validate with profile
service = PngConform::Services::ValidationService.new
result = service.validate_file("image.png", profile: profile)

# Check profile conformance
profile_result = profile_mgr.validate_file_against_profile(
  "image.png",
  "web"
)

if profile_result.profile_errors.any?
  puts "Profile violations:"
  profile_result.profile_errors.each do |error|
    puts "  #{error.message}"
  end
end

Custom reporters

# Use specific reporter
reporter = PngConform::Reporters::VerboseReporter.new
service = PngConform::Services::ValidationService.new
result = service.validate_file("image.png")

reporter.report(result)

# Create custom reporter combinations
factory = PngConform::Reporters::ReporterFactory.new
reporter = factory.create_reporter(
  verbose: true,
  show_palette: true,
  show_text: true,
  colorize: true
)

reporter.report(result)

Accessing chunk data

result = service.validate_file("image.png")

# Iterate through chunks
result.chunks.each do |chunk|
  puts "Chunk: #{chunk.type}"
  puts "  Offset: #{chunk.offset}"
  puts "  Length: #{chunk.length}"
  puts "  CRC: 0x#{chunk.crc.to_s(16).upcase}"

  # Access decoded data
  if chunk.decoded_data
    case chunk.type
    when "IHDR"
      puts "  Dimensions: #{chunk.decoded_data.width}x#{chunk.decoded_data.height}"
      puts "  Bit depth: #{chunk.decoded_data.bit_depth}"
      puts "  Color type: #{chunk.decoded_data.color_type}"
    when "gAMA"
      puts "  Gamma: #{chunk.decoded_data.gamma}"
    when "tEXt"
      puts "  Keyword: #{chunk.decoded_data.keyword}"
      puts "  Text: #{chunk.decoded_data.text}"
    end
  end
end

Chunk Validation

General

PngConform validates all standard PNG chunk types with dedicated validators.

Critical chunks

IHDR (image header)

The IHDR chunk must be the first chunk and contains image metadata.

Validation checks:

  • Width and height must be non-zero

  • Bit depth must be valid for color type

  • Color type must be 0, 2, 3, 4, or 6

  • Compression method must be 0 (deflate)

  • Filter method must be 0 (adaptive)

  • Interlace method must be 0 (none) or 1 (Adam7)

PLTE (palette)

Required for indexed-color images, optional for truecolor.

Validation checks:

  • Length must be divisible by 3 (RGB triplets)

  • Number of entries must not exceed 2^bit_depth

  • Must appear before IDAT chunks

  • Required for color type 3

  • Must not appear for grayscale images

IDAT (image data)

Contains compressed image data. Multiple IDAT chunks must be consecutive.

Validation checks:

  • IDAT chunks must be consecutive

  • Compressed data must be valid zlib format

  • Decompressed data must match expected size

  • Filter types must be valid (0-4)

IEND (image trailer)

Marks the end of the PNG file.

Validation checks:

  • Must be the last chunk

  • Data length must be 0

  • Must appear exactly once

Ancillary chunks

Color space chunks

gAMA

Gamma correction value

cHRM

Primary chromaticities and white point

sRGB

Standard RGB color space

iCCP

ICC color profile

cICP

Coding-independent code points (HDR)

Transparency chunks

tRNS

Transparency information

bKGD

Background color

Text chunks

tEXt

Uncompressed Latin-1 text

zTXt

Compressed Latin-1 text

iTXt

International UTF-8 text

Physical dimensions

pHYs

Physical pixel dimensions

sCAL

Physical scale

Time stamp

tIME

Last modification time

Other chunks

hIST

Palette histogram

sPLT

Suggested palette

sBIT

Significant bits

oFFs

Image offset

pCAL

Pixel calibration

sTER

Stereo image indicator

mDCv

Mastering display color volume (HDR)

Validation Profiles

General

Profiles define conformance requirements for different use cases.

Available profiles

minimal

Basic PNG structure validation only.

  • Required: IHDR, IDAT, IEND

  • Optional: Any chunks

  • Prohibited: None

web

Optimized for web display.

  • Required: IHDR, IDAT, IEND, gAMA, sRGB

  • Optional: tRNS, bKGD, tEXt, iTXt, zTXt

  • Prohibited: None

print

For high-quality print reproduction.

  • Required: IHDR, IDAT, IEND, iCCP

  • Optional: gAMA, cHRM, tRNS, bKGD

  • Prohibited: sRGB

archive

Long-term preservation requirements.

  • Required: IHDR, IDAT, IEND, tIME

  • Optional: Any chunks

  • Prohibited: None

strict

Only standard chunks allowed.

  • Required: IHDR, IDAT, IEND

  • Optional: Critical chunks only

  • Prohibited: Unknown chunks

default

Standard PNG validation.

  • Required: IHDR, IDAT, IEND

  • Optional: Any chunks

  • Prohibited: None

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/claricle/png_conform.

Credits

PngConform is inspired by pngcheck, originally developed by Greg Roelofs and contributors, and now maintained by the PNG Development Group.

Copyright Ribose.

The gem is available as open source under the Ribose BSD-2-Clause License.