QR Decoder Compatibility Reference

This document provides comprehensive information about QR code decoder compatibility with SegnoMMS stylized QR codes, including known limitations and recommendations for different use cases.

Overview

SegnoMMS generates visually stylized QR codes that prioritize aesthetic appeal while maintaining functional scanability. However, some combinations of visual styling and decoder implementations may result in reduced compatibility compared to standard square-module QR codes.

This reference documents tested decoder compatibility across different configurations to help developers make informed decisions about styling choices for their specific use cases.

Error Correction Level Reference

QR codes use Reed-Solomon error correction to recover from damage or scanning issues. Higher error correction levels allow more data recovery but reduce available data capacity:

Error Correction Levels

Level

Recovery Capacity

Recommended Use

L (Low)

~7% damage recovery

Clean environments, maximum data capacity needed

M (Medium)

~15% damage recovery

Standard usage, good balance (default)

Q (Quartile)

~25% damage recovery

Stylized QR codes, moderate visual effects

H (High)

~30% damage recovery

Heavy styling, logos, outdoor use, critical applications

For stylized QR codes, use at least Level M. For connected shapes, merged modules, or centerpiece overlays, Level Q or H is strongly recommended to compensate for the visual modifications that may affect scanning.

Decoder Library Compatibility Matrix

The following decoders have been tested with SegnoMMS output:

Recommended Decoders (High Compatibility): - zxingcpp - ZXing C++ implementation with Python bindings - OpenCV QRCodeDetector - Part of opencv-python package - pyzbar - Python wrapper for ZBar library (requires system dependencies)

Mobile and Web Decoders: - Most smartphone camera apps use ZXing or similar algorithms - Web-based QR scanners vary widely in capability

Installation

# Recommended: ZXing C++ (no system dependencies)
pip install zxingcpp

# Alternative: OpenCV
pip install opencv-python

# Alternative: PyZBar (requires libzbar)
# On macOS: brew install zbar
# On Ubuntu: sudo apt-get install libzbar0
pip install pyzbar

Known Compatibility Issues

Based on systematic testing, the following configurations have known decoder compatibility limitations:

Error Correction Level Issues

Low Error Correction (Level L) - Issue: Connected shapes with ECC Level L may have insufficient error correction - Affected Configuration: error="L" with shape="connected" - Affected Decoders: zxingcpp, opencv - Recommendation: Use ECC Level M or higher for connected/merged shapes - Explanation: Visual styling reduces available error correction capacity

Compatibility Recommendations

High Compatibility Configurations

For maximum decoder compatibility, use these “safe” configurations:

# Safest configuration - works with all tested decoders
safe_config = {
    'shape': 'square',
    'scale': 10,
    'dark': '#000000',
    'light': '#FFFFFF',
    'error': 'M'  # Medium error correction
}

# Safe rounded appearance
safe_rounded = {
    'shape': 'squircle',
    'safe_mode': True,
    'scale': 10,
    'error': 'M'
}

# Safe connected modules
safe_connected = {
    'shape': 'connected',
    'safe_mode': True,
    'error': 'Q',  # Higher ECC for merging
    'scale': 12
}

Balanced Styling Configurations

These configurations provide good visual appeal with reasonable compatibility:

# Stylized but compatible
balanced_config = {
    'shape': 'rounded',
    'safe_mode': True,
    'scale': 15,  # Larger scale for better definition
    'error': 'M',
    'dark': '#1a1a2e',
    'light': '#f5f5f5'
}

# Connected modules with safety
balanced_connected = {
    'shape': 'connected',
    'connectivity': '8-way',
    'merge': 'soft',
    'safe_mode': True,
    'error': 'H',  # Highest ECC for complex styling
    'scale': 12
}

Testing and Validation

Decoder Testing Methodology

When deploying SegnoMMS QR codes, follow this testing approach:

  1. Generate test codes with your exact configuration

  2. Convert to PNG using the same process as production

  3. Test with multiple decoders (zxingcpp, opencv, mobile apps)

  4. Verify across different scales and viewing conditions

  5. Document compatibility for your specific use case

Example Testing Code

import segno
from segnomms import write
import io
from pathlib import Path
from typing import Any, Dict, List, Optional

def test_decoder_compatibility(
    config: Dict,
    test_data: str = "Test Message",
    output_dir: Optional[Path] = None
) -> Dict[str, Any]:
    """
    Test decoder compatibility for a configuration.

    Args:
        config: Dictionary of configuration options for write()
        test_data: Data to encode in the QR code
        output_dir: Optional directory for saving test files

    Returns:
        Dictionary with test results from all available decoders
    """
    import tempfile
    import shutil

    # Create QR code with specified error level
    qr = segno.make(test_data, error=config.get('error', 'M'))

    # Generate SVG
    output = io.StringIO()
    write(qr, output, **config)
    svg_content = output.getvalue()

    # Prepare output directory
    if output_dir is None:
        output_dir = Path(tempfile.mkdtemp(prefix='qr_test_'))
    else:
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

    # Save SVG
    svg_path = output_dir / "test.svg"
    with open(svg_path, 'w') as f:
        f.write(svg_content)

    # Convert SVG to PNG for testing
    # Using cairosvg for reliable conversion
    try:
        import cairosvg
        png_path = output_dir / "test.png"
        cairosvg.svg2png(
            bytestring=svg_content.encode('utf-8'),
            write_to=str(png_path),
            dpi=200,
            output_width=400,
            output_height=400
        )
    except ImportError:
        return {
            'error': 'cairosvg not installed',
            'suggestion': 'pip install cairosvg',
            'config': config
        }

    # Test with available decoders
    test_results = {
        'config': config,
        'test_data': test_data,
        'svg_path': str(svg_path),
        'png_path': str(png_path),
        'decoders': {}
    }

    # Test with zxingcpp
    try:
        import zxingcpp
        from PIL import Image

        img = Image.open(png_path)
        result = zxingcpp.read_barcode(img)

        test_results['decoders']['zxingcpp'] = {
            'available': True,
            'detected': result is not None,
            'decoded': result.text if result else None,
            'matches': result.text == test_data if result else False,
            'error': None
        }
    except ImportError:
        test_results['decoders']['zxingcpp'] = {
            'available': False,
            'error': 'zxingcpp not installed'
        }
    except Exception as e:
        test_results['decoders']['zxingcpp'] = {
            'available': True,
            'detected': False,
            'error': str(e)
        }

    # Test with OpenCV
    try:
        import cv2

        img = cv2.imread(str(png_path))
        detector = cv2.QRCodeDetector()
        data, bbox, _ = detector.detectAndDecode(img)

        test_results['decoders']['opencv'] = {
            'available': True,
            'detected': bbox is not None,
            'decoded': data if data else None,
            'matches': data == test_data if data else False,
            'error': None
        }
    except ImportError:
        test_results['decoders']['opencv'] = {
            'available': False,
            'error': 'opencv-python not installed'
        }
    except Exception as e:
        test_results['decoders']['opencv'] = {
            'available': True,
            'detected': False,
            'error': str(e)
        }

    # Test with pyzbar
    try:
        from pyzbar import pyzbar
        from PIL import Image

        img = Image.open(png_path)
        results = pyzbar.decode(img)

        if results:
            decoded_data = results[0].data.decode('utf-8')
            test_results['decoders']['pyzbar'] = {
                'available': True,
                'detected': True,
                'decoded': decoded_data,
                'matches': decoded_data == test_data,
                'error': None
            }
        else:
            test_results['decoders']['pyzbar'] = {
                'available': True,
                'detected': False,
                'decoded': None,
                'matches': False,
                'error': 'No QR code detected'
            }
    except ImportError:
        test_results['decoders']['pyzbar'] = {
            'available': False,
            'error': 'pyzbar not installed (requires system libzbar)'
        }
    except Exception as e:
        test_results['decoders']['pyzbar'] = {
            'available': True,
            'detected': False,
            'error': str(e)
        }

    # Calculate summary
    available_decoders = [name for name, result in test_results['decoders'].items()
                        if result.get('available', False)]
    successful_decoders = [name for name, result in test_results['decoders'].items()
                          if result.get('matches', False)]

    test_results['summary'] = {
        'total_decoders': len(available_decoders),
        'successful_decoders': len(successful_decoders),
        'success_rate': len(successful_decoders) / len(available_decoders)
                       if available_decoders else 0.0,
        'all_passed': len(successful_decoders) == len(available_decoders)
                     and len(available_decoders) > 0
    }

    return test_results


def print_test_results(results: Dict) -> None:
    """Pretty print decoder test results."""
    print(f"\n{'='*60}")
    print("QR Decoder Compatibility Test Results")
    print(f"{'='*60}")
    print(f"Configuration: {results['config']}")
    print(f"Test Data: {results['test_data']}")
    print(f"\nDecoder Results:")
    print(f"{'-'*60}")

    for decoder_name, decoder_result in results['decoders'].items():
        if not decoder_result.get('available', False):
            print(f"  {decoder_name}: ❌ Not available - {decoder_result.get('error', 'Unknown')}")
        elif decoder_result.get('matches', False):
            print(f"  {decoder_name}: ✅ SUCCESS - Decoded correctly")
        elif decoder_result.get('detected', False):
            print(f"  {decoder_name}: ⚠️  MISMATCH - Decoded as '{decoder_result.get('decoded')}'")
        else:
            error = decoder_result.get('error', 'Detection failed')
            print(f"  {decoder_name}: ❌ FAILED - {error}")

    print(f"\n{'-'*60}")
    summary = results['summary']
    print(f"Summary: {summary['successful_decoders']}/{summary['total_decoders']} decoders succeeded")
    print(f"Success Rate: {summary['success_rate']*100:.1f}%")
    print(f"Overall: {'✅ PASS' if summary['all_passed'] else '❌ FAIL'}")
    print(f"{'='*60}\n")


def batch_test_configurations(configs: List[Dict], test_data: str = "https://example.com") -> None:
    """Test multiple configurations and report results."""
    all_results = []

    for i, config in enumerate(configs, 1):
        print(f"\n[{i}/{len(configs)}] Testing configuration...")
        results = test_decoder_compatibility(config, test_data)
        print_test_results(results)
        all_results.append(results)

    # Print aggregate summary
    print(f"\n{'='*60}")
    print("AGGREGATE TEST RESULTS")
    print(f"{'='*60}")

    total_configs = len(all_results)
    passed_configs = sum(1 for r in all_results if r['summary']['all_passed'])

    print(f"Total Configurations Tested: {total_configs}")
    print(f"Fully Compatible: {passed_configs}")
    print(f"Partial Compatibility: {total_configs - passed_configs}")
    print(f"Overall Compatibility Rate: {passed_configs/total_configs*100:.1f}%")
    print(f"{'='*60}\n")


# Example Usage
if __name__ == "__main__":
    # Test individual configuration
    config = {
        'shape': 'circle',
        'safe_mode': True,
        'scale': 10,
        'error': 'M'
    }

    results = test_decoder_compatibility(config, "https://example.com")
    print_test_results(results)

    # Test multiple configurations
    test_configs = [
        {'shape': 'square', 'scale': 10, 'error': 'M'},
        {'shape': 'circle', 'safe_mode': True, 'scale': 10, 'error': 'M'},
        {'shape': 'rounded', 'safe_mode': True, 'scale': 15, 'error': 'M'},
        {'shape': 'connected', 'safe_mode': True, 'scale': 12, 'error': 'H'},
    ]

    batch_test_configurations(test_configs)

Performance Considerations

SVG to Bitmap Conversion

The quality of SVG→PNG/JPEG conversion significantly affects decoder compatibility:

Rendering Settings Impact: - DPI: Higher DPI (≥150) improves fine detail preservation - Antialiasing: May blur edges; consider disabling for small QR codes - Scale: Larger output dimensions generally improve compatibility - Format: PNG preserves sharp edges better than JPEG

Recommended Conversion Settings:

conversion_settings = {
    'dpi': 200,
    'width': 400,   # Minimum 400px for reliable decoding
    'height': 400,
    'background': 'white',
    'format': 'PNG'
}

Mobile Camera Considerations

Mobile QR code scanning has additional constraints:

  • Minimum size: QR codes should be at least 2cm (0.8”) for reliable mobile scanning

  • Contrast: Maintain high contrast ratios (≥7:1) for accessibility

  • Lighting: Test under various lighting conditions

  • Distance: QR codes are typically scanned from 10-50cm distance

Troubleshooting Decoder Issues

Common Problems and Solutions

“QR code not detected” - Increase scale parameter (try 12-20) - Enable safe_mode if using non-square shapes - Increase error correction level - Check contrast ratio between dark/light colors

“Detected but can’t decode content” - Increase error correction level (try ‘H’) - Reduce visual complexity (disable merging, use simpler shapes) - Test with different decoders

“Works on some devices but not others” - Test with multiple decoder implementations - Consider using “high compatibility” configuration - Document known limitations for users

“Intermittent scanning failures” - Increase quiet zone (border parameter) - Use larger scale for small QR codes - Test SVG→bitmap conversion quality

Decoder-Specific Notes

ZXing C++ - Generally most permissive with styling - Good performance with connected modules - May struggle with very small scales

OpenCV QRCodeDetector - Sensitive to edge definition - Works well with high-contrast configurations - May have issues with complex merged shapes

PyZBar - Requires system library installation - Good compatibility with standard configurations - May be sensitive to image preprocessing

Future Compatibility

Decoder Algorithm Evolution

QR decoder algorithms continue to evolve, potentially improving compatibility with stylized codes:

  • Machine learning approaches may better handle non-standard shapes

  • Improved edge detection could handle merged modules more reliably

  • Multi-scale analysis might improve small QR code recognition

However, compatibility with older devices and legacy decoders remains important for broad accessibility.

SegnoMMS Development

Future SegnoMMS versions may include:

  • Compatibility scoring during configuration validation

  • Decoder-specific optimization modes

  • Automatic safe mode recommendations

  • Enhanced SVG rendering for better bitmap conversion

Recommendations for Library Users

Production Deployment Checklist

Before deploying stylized QR codes in production:

  1. Test with target decoders (mobile apps, web scanners, etc.)

  2. Validate across different devices and operating systems

  3. Document known limitations for end users

  4. Provide fallback options (URL shorteners, manual entry)

  5. Monitor scanning success rates if possible

Configuration Selection Guidelines

Critical Applications (payment, authentication) - Use high compatibility configurations - Test extensively with target decoders - Consider multiple QR code sizes/formats

Marketing Materials (posters, business cards) - Balanced styling acceptable - Include fallback information - Test under expected viewing conditions

Decorative Applications (art, design elements) - Full styling freedom - Clearly communicate any scanning limitations - Consider QR codes as supplementary, not primary interface

Documentation and Support

When documenting QR codes for end users:

Include Scanning Instructions: - Recommended scanning apps if compatibility is limited - Optimal scanning distance and lighting - Alternative access methods (typed URLs, etc.)

Set Appropriate Expectations: - Acknowledge that stylized QR codes may require specific scanners - Provide clear fallback options - Consider user accessibility needs

Conclusion

SegnoMMS enables creation of visually appealing QR codes while maintaining good decoder compatibility for most configurations. Understanding the documented limitations allows developers to make informed decisions about styling trade-offs.

For maximum compatibility, use the recommended “safe” configurations. For applications requiring specific styling, thorough testing with target decoders is essential.

The QR code ecosystem continues to evolve, and future decoder improvements may resolve some current limitations. Until then, this compatibility reference provides guidance for reliable deployment of stylized QR codes.

Phase 4 Advanced Features Compatibility

Phase 4 features (frames, centerpieces, gradients) introduce additional compatibility considerations beyond basic shape styling. These features can significantly impact scannability and require careful testing.

Frame Shape Compatibility

Frame shapes affect decoder recognition by altering the QR code’s boundary detection:

Frame Shape Decoder Compatibility

Frame Shape

ZXing C++

OpenCV

PyZBar

Mobile Apps

Recommended Settings

square (none)

✅ Excellent

✅ Excellent

✅ Excellent

✅ Excellent

Standard settings

rounded-rect

✅ Very Good

✅ Very Good

✅ Good

⚠️ Variable

corner_radius ≤ 0.2, border ≥ 4

circle

⚠️ Good

⚠️ Good

⚠️ Fair

❌ Poor

border ≥ 5, clip_mode=’clip’

squircle

⚠️ Good

⚠️ Fair

❌ Poor

❌ Poor

border ≥ 5, simple shapes only

custom

❌ Variable

❌ Variable

❌ Poor

❌ Poor

Extensive testing required

Production Recommendations:

  • High Compatibility: Use no frame or rounded-rect with small corner radius

  • Testing Required: Circle and squircle frames need thorough validation

  • Avoid in Production: Custom frames without comprehensive decoder testing

Centerpiece (Logo Area) Compatibility

Centerpiece areas impact error correction capability and pattern recognition:

Centerpiece Size Compatibility by Error Level

Error Level

Max Safe Size

Recommended Size

Notes

L (7%)

0.05

0.03

Very limited logo space

M (15%)

0.12

0.08

Suitable for small logos

Q (25%)

0.18

0.12

Good logo visibility

H (30%)

0.25

0.15

Maximum logo area

Centerpiece Shape Compatibility:

  • Rectangle: Best compatibility, most predictable

  • Circle: Good with most decoders, test mobile apps

  • Squircle: Variable compatibility, requires testing

Critical Guidelines:

  • Always use centerpiece_margin 2 for safety buffer

  • Test centerpiece + frame combinations thoroughly

  • Consider fallback QR codes for critical applications

  • Use highest error correction level (H) for large centerpieces

Combined Features Compatibility Risk

When combining multiple Phase 4 features, compatibility risks multiply:

Warning

High Risk Combinations (Extensive Testing Required):

  • Circle frame + large centerpiece + gradient background

  • Custom frame + squircle centerpiece

  • Fade clip mode + connected shapes + centerpiece

Lower Risk Combinations:

  • Rounded-rect frame + small rect centerpiece + solid background

  • No frame + small circle centerpiece + simple shapes

PNG Conversion for Phase 4 Features

Phase 4 features require special attention during PNG conversion:

# Recommended settings for Phase 4 features
conversion_settings = {
    'dpi': 300,          # Higher DPI for frame clarity
    'width': 600,        # Larger size for centerpiece detail
    'height': 600,
    'background': 'white',
    'antialias': False,  # Sharp edges for frame detection
    'format': 'PNG'
}

Testing Protocol for Phase 4:

  1. Generate test QR codes with your exact Phase 4 configuration

  2. Convert to PNG using recommended settings

  3. Test with multiple libraries (zxingcpp, OpenCV, pyzbar)

  4. Validate on mobile devices your users will actually use

  5. Test edge cases (low light, angled scanning, small sizes)

  6. Implement fallbacks if any decoder fails