<?php
// Image output must not be polluted by warning/notice text or the JPEG breaks.
ini_set('display_errors', '0');
session_start();

class CaptchaSecurityImages {

    private $font; // Initialized in constructor to an absolute path

    // Constructor for PHP 7.x and 8.x
    public function __construct($width = '120', $height = '40', $characters = '6') {
        $this->font = __DIR__ . '/monofont.ttf';
        $this->generateCaptcha($width, $height, $characters);
    }

    // Generate random code with cryptographic security
    private function generateCode($characters) {
        $possible = '23456789bcdfghjkmnpqrstvwxyz';
        $code = '';
        $max = strlen($possible) - 1;

        for ($i = 0; $i < $characters; $i++) {
            $code .= $possible[random_int(0, $max)];
        }

        return $code;
    }

    // Generate CAPTCHA image
    private function generateCaptcha($width, $height, $characters) {
        $code = $this->generateCode($characters);
        $_SESSION['security_code'] = $code;

        // Font size
        $font_size = $height * 0.75;

        // Create image
        $image = imagecreate($width, $height);
        if (!$image) {
            die('Cannot initialize new GD image stream');
        }

        // Set colors
        $background_color = imagecolorallocate($image, 255, 255, 255); // White background
        $text_color = imagecolorallocate($image, 20, 40, 100); // Text color
        $noise_color = imagecolorallocate($image, 100, 120, 180); // Noise color

        // Generate random dots (sparse — dots barely impede OCR but obscure the letters for humans)
        for ($i = 0; $i < ($width * $height) / 15; $i++) {
            imagefilledellipse($image, random_int(0, $width), random_int(0, $height), 1, 1, $noise_color);
        }

        // Generate random lines (more disruptive to OCR than dots)
        for ($i = 0; $i < ($width * $height) / 100; $i++) {
            imageline($image, random_int(0, $width), random_int(0, $height), random_int(0, $width), random_int(0, $height), $noise_color);
        }

        // Draw each character separately with a small random rotation. Per-char
        // rotation is the single biggest cheap win against OCR; flat single-string
        // rendering is trivial for automated solvers.
        $char_spacing = ($width - 20) / max(1, $characters);
        $base_y = (int)($height * 0.72);
        foreach (str_split($code) as $i => $char) {
            $angle = random_int(-15, 15);
            $x = (int)(10 + $i * $char_spacing);
            imagettftext($image, $font_size, $angle, $x, $base_y, $text_color, $this->font, $char);
        }

        // Output the image as JPEG
        header('Content-Type: image/jpeg');
        imagejpeg($image);
        imagedestroy($image);
    }
}

// Validate input parameters and instantiate the CAPTCHA
$width = isset($_GET['width']) && is_numeric($_GET['width']) ? (int)$_GET['width'] : 120;
$height = isset($_GET['height']) && is_numeric($_GET['height']) ? (int)$_GET['height'] : 40;
$characters = isset($_GET['characters']) && is_numeric($_GET['characters']) && $_GET['characters'] > 1 ? (int)$_GET['characters'] : 6;

// Instantiate the CAPTCHA class
new CaptchaSecurityImages($width, $height, $characters);

?>