Skip to content

Scan safety ​

Styling makes codes harder to read: smaller modules, turned eyes, faint colors, a large logo. The scan guards run after all styling and filters, and hold each risky change within limits. What they change is reported, so you can see it.

GuardDefaultWhat it does
min_margin2raises the quiet zone to at least this many modules
max_offset0.15holds a module within this many modules of its cell
min_scale0.6keeps a module at least this share of a module, its shape's own size counted
max_scale1.25keeps a module from growing over its neighbors
max_skew20holds a module's skew to this many degrees each way
max_skip0.25lets left-out modules use at most this share of what the error correction can still restore, after a logo
outer_eye_scale[0.93, 1.0]keeps an outer eye about seven modules wide
inner_eye_scale[0.6, 1.15]keeps an inner eye in proportion
eye_rotation_warning10warns about an eye turned this many degrees or more off a quarter turn
min_contrast3.0the least WCAG contrast of any drawn color against the background
max_logo_share0.6the share of what the error correction can restore that a logo may clear

Most guards quietly clamp. Too little contrast and too large a logo cannot be clamped, so they throw a ScanSafetyException that says what is wrong, or only warn:

php
// config/qr.php
'scan_safety' => [
    'on_violation' => 'warn',   // 'throw' by default
],

Seeing what the guards did ​

Every change is a GuardAdjustment with the guard, what it did, and the module or corner:

php
$scene = Qr::url($url)->filter('mixed-size', min: 0.3)->scene();

foreach ($scene->adjustments as $adjustment) {
    echo "{$adjustment->guard}: {$adjustment->detail}";   // scale: held to 0.6 of a module
}

Each one is also dispatched as a QrGuardApplied event, and shown by the Monocle lens.

Changing the limits ​

In config/qr.php, for every code:

php
'scan_safety' => [
    'min_contrast' => 4.5,
    'max_skew' => 10,
],

or for one code:

php
use ExeQue\Qr\Rendering\ScanSafety;

Qr::url($url)->scanSafety(new ScanSafety(minContrast: 2.5, maxOffset: 0.2));

Turning guards off ​

A limit set to null turns its guard off. To turn them all off for one code:

php
Qr::url($url)->scanSafety(ScanSafety::off());

Do test such codes with a real scanner, or with Qr::read().

What scans ​

Every built-in module shape, eye and preset is checked by the test suite with a real decoder, as SVG and PNG, with a link and a Wi-Fi login. Within the guards, styled codes stay readable; turned off, it is up to you.