Monthly Archives: Dezembro 2015

Captcha com Canvas e Javascript

Há uns anos atrás fiz um captcha em PHP com o jpgraph. Funcionava bem, mas dependia de uma ferramenta que foi deixando de ser de uso livre (o jpgraph) e, como estava a correr em Linux, o uso de tipos de letras diferentes estava muito limitado.

Ontem descobri que um dos captchas que estava a usar num jogo – Domaisnquest – não estava a funcionar. Decidi deitar fora, de vez, o jpgraph e criar um novo captcha feito com um Canvas de HTML5 e javascript.

Visualização:

Ficheiro: CapCanvas.js


/*************
 * Class CapCanvas
 * Parameters
 * - w: canvas width			[number]
 * - h: canvas height			[number]
 * - n: number of digits (default 5)	[number]
 * - s: font size (default 16px)	[number]
 * - f: font-family (default random)	[text]
 * 
 */

function CapCanvas(w,h,n,s,f) {
	var ctx;
	var key;
	var cvsName = 'capcvs';
	var fonts = new Array("Arial", "Courier", "Times New Roman");
	var colors = new Array("yellow", "blue", "black", "gray", "red", "green", "salmon", "purple", "coral", "darkkhaki", "darkolivegreen", "honeydew", "turquoise", "white");
	if(n==undefined) n=5;
	if(s==undefined) s=16;
	if(f==undefined) {
		f = fonts[rand(fonts.length)];
	}

	document.write("<canvas id='"+cvsName+"' width='"+w+"' height='"+h+"' title=\"Click to change if you can't read the letters\" style='cursor:pointer;' onclick='capCanvas.chgCap();'></canvas>");
	ctx = document.getElementById(cvsName).getContext("2d");
	chgCap();


	function putLine(x1,y1,x2,y2,c) {
		ctx.strokeStyle = c;
		ctx.beginPath();
		ctx.moveTo(x1,y1);
		ctx.lineTo(x2,y2);
		ctx.stroke();
	}

	this.chgCap = chgCap;
	function chgCap() {
		var c = colors[rand(colors.length)];
		var c2,c3;
		do{
			c2 = colors[rand(colors.length)];
		}while(c==c2);
		do{
			c3 = colors[rand(colors.length)];
		}while(c==c3 || c2==c3);
		key = newKey(n);
		f = fonts[rand(fonts.length)];

		ctx.fillStyle = c;
		ctx.beginPath();
		ctx.rect(0,0,w,h);
		ctx.fill();

		ctx.lineWidth = 2;

		//Draw lines
		var p = rand(5)+5;
		var inc = Math.floor(w/p);
		for(var i=0; i<p; i++) {
			putLine(i*inc,0,(i+1)*inc,h,c2);
		}
		var mp = Math.floor(p*inc/2);
		var h2 = Math.floor(h/2);
		putLine(mp,0,0,h2,c2);
		putLine(w,0,0,h,c2);
		putLine(w,h2,mp,h,c2);

		// Text
		ctx.fillStyle = c3;
		ctx.font = s + "px '"+f+"'";
		//ctx.font = "16px 'Arial'";
		ctx.textAlign = "center";
		ctx.textBaseline = "middle";
		var tx = Math.floor(w/2);
		var ty = Math.floor(h/2);
		ctx.fillText(key, tx, ty);

	}

	this.keyCheck = keyCheck;
	function keyCheck(k) {
		return k==key;
	}

	function newKey(t) {
		var kk = "";
		// Chars I want to use
		var crs = "A B C D E F G H J K L M N P Q R S T W X Y Z 2 3 4 5 6 7 8 9".split(" ");
		while(kk.length<t) {
			var r = rand(crs.length);
			if (crs[r]!=" ") {
				kk += crs[r];
			}
		}
		return kk;
	}


	function rand(n) {
		return Math.floor(n*Math.random());
	}
}

var capCanvas = new CapCanvas(105,30);

Ao lado deste canvas deve ser colocada uma caixa de texto. Mais tarde, para verificar se a caixa tem o mesmo texto que o captcha, basta chamar a função keyCheck().

Ficheiro: captcha.html

<!DOCTYPE html>
<html>
<head>
<title>Captcha</title>
<meta charset="iso-8859-1">
<script type="text/javascript">
function ver() {
	var cap = document.getElementById("tcap").value;
	if(!capCanvas.keyCheck(cap)) {
		window.alert(
			"You must enter the 5 letters/digits. " + 
			"If you can't read the letters, please click on the image to get another one."
		);
	} else {
		window.alert("OK");
	}
}
</script>

</head>
<body>
<h3>Captcha - exemplo de utilização</h3>
<form>
<script type="text/javascript" src="CapCanvas.js"></script>
<input type="text" size="8" id="tcap" />
<input type="button" value="Verificar" onclick="ver();" />
</form>
</body>
</html>