Extract Ladill QR Plus as standalone app at qr.ladill.com.
Deploy Ladill QR Plus / deploy (push) Failing after 1s
Deploy Ladill QR Plus / deploy (push) Failing after 1s
Utility QR types only (URL, WiFi, link list, business, app download) with SSO, Billing API integration, public /q resolver, and qr-plus:import for platform migration. vCard and commerce types stay on the platform. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support\Qr;
|
||||
|
||||
class QrScanReliability
|
||||
{
|
||||
/** @param array<string, mixed> $style */
|
||||
public static function contrastRatio(array $style): float
|
||||
{
|
||||
$fg = self::relativeLuminance((string) ($style['foreground'] ?? '#000000'));
|
||||
$bg = self::relativeLuminance((string) ($style['background'] ?? '#ffffff'));
|
||||
|
||||
$lighter = max($fg, $bg);
|
||||
$darker = min($fg, $bg);
|
||||
|
||||
return ($lighter + 0.05) / ($darker + 0.05);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $style
|
||||
* @return array{level: string, messages: list<string>}
|
||||
*/
|
||||
public static function assess(array $style): array
|
||||
{
|
||||
$style = QrStyleDefaults::merge($style);
|
||||
$messages = [];
|
||||
$level = 'good';
|
||||
|
||||
$contrast = self::contrastRatio($style);
|
||||
$isClassic = self::isClassicBlackOnWhite($style);
|
||||
$moduleStyle = (string) ($style['module_style'] ?? 'square');
|
||||
$moduleMeta = QrModuleStyleCatalog::optionsFor($moduleStyle);
|
||||
|
||||
if ($moduleMeta['scan_risk'] === 'medium') {
|
||||
$messages[] = 'This module style may not scan on every phone camera. Square is the safest choice.';
|
||||
$level = 'fair';
|
||||
}
|
||||
|
||||
if ($moduleMeta['scan_risk'] === 'high') {
|
||||
$messages[] = 'Decorative styles like ' . $moduleMeta['label'] . ' often fail on Samsung Camera. Use square for print and signage.';
|
||||
$level = 'poor';
|
||||
}
|
||||
|
||||
if (! $isClassic) {
|
||||
$messages[] = 'Colored QR codes often fail on Samsung Camera and other basic scanners. Black on white works everywhere.';
|
||||
$level = $level === 'good' ? 'fair' : 'poor';
|
||||
}
|
||||
|
||||
if ($contrast < 4.5) {
|
||||
$messages[] = sprintf(
|
||||
'Contrast is low (%.1f:1). Aim for at least 4.5:1 — dark foreground, white background.',
|
||||
$contrast,
|
||||
);
|
||||
$level = 'poor';
|
||||
}
|
||||
|
||||
if ($moduleMeta['scan_risk'] !== 'low' && ! $isClassic) {
|
||||
$level = 'poor';
|
||||
}
|
||||
|
||||
if ($messages === []) {
|
||||
$messages[] = 'Black square modules on white give the best compatibility with Samsung, iPhone, and printed codes.';
|
||||
}
|
||||
|
||||
return ['level' => $level, 'messages' => $messages];
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $style */
|
||||
public static function isClassicBlackOnWhite(array $style): bool
|
||||
{
|
||||
$fg = strtolower(ltrim((string) ($style['foreground'] ?? ''), '#'));
|
||||
$bg = strtolower(ltrim((string) ($style['background'] ?? ''), '#'));
|
||||
|
||||
$fg = strlen($fg) === 3 ? $fg[0] . $fg[0] . $fg[1] . $fg[1] . $fg[2] . $fg[2] : $fg;
|
||||
$bg = strlen($bg) === 3 ? $bg[0] . $bg[0] . $bg[1] . $bg[1] . $bg[2] . $bg[2] : $bg;
|
||||
|
||||
return in_array($fg, ['000000', '0f172a', '111111', '1a1a1a'], true)
|
||||
&& in_array($bg, ['ffffff', 'fff'], true);
|
||||
}
|
||||
|
||||
private static function relativeLuminance(string $hex): float
|
||||
{
|
||||
$hex = ltrim(trim($hex), '#');
|
||||
if (strlen($hex) === 3) {
|
||||
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
|
||||
}
|
||||
|
||||
if (strlen($hex) !== 6 || ! ctype_xdigit($hex)) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$channels = [];
|
||||
foreach ([0, 2, 4] as $offset) {
|
||||
$value = hexdec(substr($hex, $offset, 2)) / 255;
|
||||
$channels[] = $value <= 0.03928
|
||||
? $value / 12.92
|
||||
: (($value + 0.055) / 1.055) ** 2.4;
|
||||
}
|
||||
|
||||
return (0.2126 * $channels[0]) + (0.7152 * $channels[1]) + (0.0722 * $channels[2]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user