Deploy: 2026-05-23 18:20:29
This commit is contained in:
@@ -99,13 +99,54 @@ class WhatsAppClient {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure the TTF fonts exist in the backend/fonts directory.
|
||||||
|
* If they don't, try to download them.
|
||||||
|
*/
|
||||||
|
private static function ensureFontsExist() {
|
||||||
|
$fontDir = __DIR__ . '/../fonts';
|
||||||
|
if (!is_dir($fontDir)) {
|
||||||
|
@mkdir($fontDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fonts = [
|
||||||
|
'Roboto-Bold.ttf' => 'https://github.com/google/fonts/raw/main/apache/roboto/static/Roboto-Bold.ttf',
|
||||||
|
'CourierPrime-Bold.ttf' => 'https://github.com/google/fonts/raw/main/ofl/courierprime/CourierPrime-Bold.ttf'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($fonts as $filename => $url) {
|
||||||
|
$path = $fontDir . '/' . $filename;
|
||||||
|
if (!file_exists($path) || @filesize($path) < 1000) {
|
||||||
|
// Try file_get_contents first
|
||||||
|
$content = @file_get_contents($url);
|
||||||
|
if ($content !== false && strlen($content) > 1000) {
|
||||||
|
@file_put_contents($path, $content);
|
||||||
|
} elseif (function_exists('curl_init')) {
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
||||||
|
$content = curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
if ($content && strlen($content) > 1000) {
|
||||||
|
@file_put_contents($path, $content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate dynamic base64-encoded image containing OTP code using GD library.
|
* Generate dynamic base64-encoded image containing OTP code using GD library.
|
||||||
*
|
*
|
||||||
* @param string $otp 4-digit OTP code
|
* @param string $otp 3-digit OTP code
|
||||||
* @return string Base64 encoded PNG image
|
* @return string Base64 encoded PNG image
|
||||||
*/
|
*/
|
||||||
public static function generateOtpImageBase64($otp) {
|
public static function generateOtpImageBase64($otp) {
|
||||||
|
// Ensure fonts exist (downloads them if missing)
|
||||||
|
self::ensureFontsExist();
|
||||||
|
|
||||||
// Create a 300x100 image
|
// Create a 300x100 image
|
||||||
$im = imagecreatetruecolor(300, 100);
|
$im = imagecreatetruecolor(300, 100);
|
||||||
if (!$im) {
|
if (!$im) {
|
||||||
@@ -116,41 +157,75 @@ class WhatsAppClient {
|
|||||||
$bgColor = imagecolorallocate($im, 240, 244, 248); // Soft grey-blue
|
$bgColor = imagecolorallocate($im, 240, 244, 248); // Soft grey-blue
|
||||||
$textColor = imagecolorallocate($im, 33, 37, 41); // Dark charcoal
|
$textColor = imagecolorallocate($im, 33, 37, 41); // Dark charcoal
|
||||||
$noiseColor = imagecolorallocate($im, 200, 210, 220); // Light noise
|
$noiseColor = imagecolorallocate($im, 200, 210, 220); // Light noise
|
||||||
|
$otpFg = imagecolorallocate($im, 13, 110, 253); // Royal blue for OTP
|
||||||
|
|
||||||
// Fill background
|
// Fill background
|
||||||
imagefill($im, 0, 0, $bgColor);
|
imagefill($im, 0, 0, $bgColor);
|
||||||
|
|
||||||
// --- 1. Draw Big OTP Text by Scaling ---
|
// --- 1. Check if TTF is available and fonts are loaded ---
|
||||||
// Create a small image for the OTP
|
$useTtf = false;
|
||||||
$otpWidth = 45; // 3 chars * 15px width roughly
|
$availableFonts = [];
|
||||||
$otpHeight = 20;
|
if (function_exists('imagettftext')) {
|
||||||
$otpIm = imagecreatetruecolor($otpWidth, $otpHeight);
|
$fontDir = __DIR__ . '/../fonts';
|
||||||
$otpBg = imagecolorallocate($otpIm, 240, 244, 248);
|
if (file_exists($fontDir . '/Roboto-Bold.ttf') && @filesize($fontDir . '/Roboto-Bold.ttf') > 1000) {
|
||||||
$otpFg = imagecolorallocate($otpIm, 13, 110, 253);
|
$availableFonts[] = $fontDir . '/Roboto-Bold.ttf';
|
||||||
imagefill($otpIm, 0, 0, $otpBg);
|
}
|
||||||
|
if (file_exists($fontDir . '/CourierPrime-Bold.ttf') && @filesize($fontDir . '/CourierPrime-Bold.ttf') > 1000) {
|
||||||
$chars = str_split($otp);
|
$availableFonts[] = $fontDir . '/CourierPrime-Bold.ttf';
|
||||||
$x = 2;
|
}
|
||||||
foreach ($chars as $char) {
|
if (!empty($availableFonts)) {
|
||||||
$y = random_int(0, 5); // Slight vertical jitter
|
$useTtf = true;
|
||||||
imagestring($otpIm, 5, $x, $y, $char, $otpFg);
|
}
|
||||||
$x += 14; // Font 5 width is approx 9px, leaving some space
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale it up by 3x onto the main image
|
$chars = str_split($otp);
|
||||||
$scale = 3;
|
|
||||||
$dstWidth = $otpWidth * $scale;
|
if ($useTtf) {
|
||||||
$dstHeight = $otpHeight * $scale;
|
// Render smooth OTP using TTF fonts
|
||||||
|
// Center the text block roughly. A 3-digit OTP with size 30px needs ~120px width.
|
||||||
// Place it randomly in the bottom right-ish area
|
$startX = random_int(85, 115);
|
||||||
$dstX = random_int(80, 150);
|
$yBase = 65; // Baseline of TTF text
|
||||||
$dstY = random_int(30, 40);
|
|
||||||
|
foreach ($chars as $char) {
|
||||||
imagecopyresampled($im, $otpIm, $dstX, $dstY, 0, 0, $dstWidth, $dstHeight, $otpWidth, $otpHeight);
|
// Alternating / random font selection per digit
|
||||||
imagedestroy($otpIm);
|
$fontFile = $availableFonts[array_rand($availableFonts)];
|
||||||
|
$angle = random_int(-15, 15); // Dynamic tilts to thwart OCR
|
||||||
|
$size = random_int(28, 34); // Dynamic size
|
||||||
|
$yOffset = random_int(-6, 6); // Vertical jitter
|
||||||
|
|
||||||
|
// Draw character
|
||||||
|
imagettftext($im, $size, $angle, $startX, $yBase + $yOffset, $otpFg, $fontFile, $char);
|
||||||
|
$startX += 42; // Spacing to next char (larger spacing to handle angle/size variations)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback: draw scaled bitmap fonts if TTF not supported
|
||||||
|
$otpWidth = 45; // 3 chars * 15px width roughly
|
||||||
|
$otpHeight = 20;
|
||||||
|
$otpIm = imagecreatetruecolor($otpWidth, $otpHeight);
|
||||||
|
$otpBg = imagecolorallocate($otpIm, 240, 244, 248);
|
||||||
|
$otpFgTemp = imagecolorallocate($otpIm, 13, 110, 253);
|
||||||
|
imagefill($otpIm, 0, 0, $otpBg);
|
||||||
|
|
||||||
|
$x = 2;
|
||||||
|
foreach ($chars as $char) {
|
||||||
|
$y = random_int(0, 5); // Slight vertical jitter
|
||||||
|
imagestring($otpIm, 5, $x, $y, $char, $otpFgTemp);
|
||||||
|
$x += 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale it up by 3x onto the main image
|
||||||
|
$scale = 3;
|
||||||
|
$dstWidth = $otpWidth * $scale;
|
||||||
|
$dstHeight = $otpHeight * $scale;
|
||||||
|
|
||||||
|
$dstX = random_int(80, 140);
|
||||||
|
$dstY = random_int(30, 40);
|
||||||
|
|
||||||
|
imagecopyresampled($im, $otpIm, $dstX, $dstY, 0, 0, $dstWidth, $dstHeight, $otpWidth, $otpHeight);
|
||||||
|
imagedestroy($otpIm);
|
||||||
|
}
|
||||||
|
|
||||||
// --- 2. Add Background Noise (Lines & Dots) ---
|
// --- 2. Add Background Noise (Lines & Dots) ---
|
||||||
// Drawing noise *after* the OTP helps to obstruct it slightly from OCR
|
|
||||||
for ($i = 0; $i < 6; $i++) {
|
for ($i = 0; $i < 6; $i++) {
|
||||||
imageline($im, random_int(0, 300), random_int(0, 100), random_int(0, 300), random_int(0, 100), $noiseColor);
|
imageline($im, random_int(0, 300), random_int(0, 100), random_int(0, 300), random_int(0, 100), $noiseColor);
|
||||||
}
|
}
|
||||||
@@ -188,3 +263,4 @@ class WhatsAppClient {
|
|||||||
return base64_encode($imageData);
|
return base64_encode($imageData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user