I created this simple Captcha for Angular. Very basic Captcha Validator and Generator which works on Client Side and I believe that it should be enough for basic sites. The Captcha Converts number to Alphabets and Validates Users input. It uses HTML Canvas Element For Drawing the Captcha Image.

import { FormGroup, ValidatorFn, Validator, ValidationErrors } from '@angular/forms';
import { ElementRef, Renderer2 } from '@angular/core';

export class Captcha {

    private static numberArray = [
        { id: 1, text: 'One' },
        { id: 2, text: 'Two' },
        { id: 3, text: 'Three' },
        { id: 4, text: 'Four' },
        { id: 5, text: 'Five' },
        { id: 6, text: 'Six' },
        { id: 7, text: 'Seven' },
        { id: 8, text: 'Eight' },
        { id: 9, text: 'Nine' },
        { id: 0, text: 'Zero' }
    ];

    /**
     * Generates a Random Captcha
     */
    public static generate(): string {
        return Captcha.textify(Math.floor(Math.random() * 1000));
    }

    public static validator(question: string): ValidatorFn {
        return (control: FormGroup): { [key: string]: any } | null => {
            const answer = +control.value;
            if (Captcha.textToNumber(question) !== answer) {
                return { captcha: { value: control.value } };
            }
            return null;
        };
    }

    /**
     * Draw Captcha Question Using HTML Canvas
     * @param renderer Renderer2 Provided By Angular
     * @param element Parent Element Where Question Needs to be Drawn
     * @param text Captcha Question
     */
    public static draw(renderer: Renderer2, element: ElementRef, question: string) {
        try {
            const canvas: HTMLCanvasElement = renderer.createElement('canvas');
            const ctx: CanvasRenderingContext2D = canvas.getContext('2d');

            renderer.setAttribute(canvas, 'width', '250');
            renderer.setAttribute(canvas, 'height', '50');

            ctx.font = '30px Arial';
            ctx.strokeText(question, 5, 30);
            renderer.appendChild(element.nativeElement, canvas);
        } catch (e) {
            console.log('Error Occurred While Drawing', e);
        }
    }

    private static textify(someNumber: number): string {
        const str = someNumber.toString();
        let output = '';
        for (const char of str) {
            output += Captcha.numberArray.find(x => x.id === +char).text + '-';
        }
        return output.substr(0, output.length - 1);
    }

    private static textToNumber(someText: string): number {
        const numbers = someText.split('-');
        let output = '0';
        for (const n of numbers) {
            output += Captcha.numberArray.find(x => x.text === n).id;
        }
        return +output;
    }
}

Previous Post