
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@8121 8e941d3f-bd1b-0410-a28a-d453659cc2b4
674 lines
19 KiB
ObjectPascal
674 lines
19 KiB
ObjectPascal
{@@ ----------------------------------------------------------------------------
|
|
This unit implements a CAPTCHA component for Lazarus.
|
|
|
|
AUTHOR: Werner Pamler
|
|
|
|
LICENSE: LGPL with linking exception (like Lazarus LCL)
|
|
See the file COPYING.modifiedLGPL.txt, included in the Lazarus
|
|
distribution, for details about the license.
|
|
-------------------------------------------------------------------------------}
|
|
|
|
unit CaptchaCtrl;
|
|
|
|
{$mode OBJFPC}{$H+}
|
|
{$WARN 6058 off : Call to subroutine "$1" marked as inline is not inlined}
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Graphics, Controls;
|
|
|
|
type
|
|
TCaptchaChar = record
|
|
Character: String; // Character (must be a string for UTF8)
|
|
Angle: Integer; // Rotation angle of character, in degrees
|
|
Position: TPoint; // Position of character within buffer bitmap (for TextOut)
|
|
FontIndex: Integer; // Index of font to be used
|
|
Color: TColor; // Random color of the character
|
|
end;
|
|
TCaptchaCharArray = array of TCaptchaChar;
|
|
|
|
TCaptchaLine = record
|
|
StartPt: TPoint; // Random start point of the line
|
|
EndPt: TPoint; // Random end point of the line
|
|
Color: TColor; // Random line color
|
|
end;
|
|
TCaptchaLineArray = array of TCaptchaLine;
|
|
|
|
TCaptchaOption = (
|
|
coAlphaUpper, coAlphaLower, coNumeric, coCustom,
|
|
coRotated, coFont1, coFont2, coLines
|
|
);
|
|
TCaptchaOptions = set of TCaptchaOption;
|
|
TCaptchaCharsOption = coAlphaUpper..coCustom;
|
|
|
|
TNewCaptchaEvent = (nceNone, nceClick, nceDblClick);
|
|
|
|
const
|
|
DEFAULT_CAPTCHA_OPTIONS = [
|
|
coAlphaUpper, coAlphaLower, coNumeric, coCustom,
|
|
coRotated, coFont1, coFont2, coLines
|
|
];
|
|
DEFAULT_CAPTCHA_NUMCHARS = 10;
|
|
DEFAULT_CAPTCHA_NUMLINES = 30;
|
|
|
|
type
|
|
TCaptchaLabel = class(TGraphicControl)
|
|
private
|
|
FBuffer: TBitmap;
|
|
FCaptchaChars: TCaptchaCharArray;
|
|
FCaptchaLines: TCaptchaLineArray;
|
|
FValidChars: array[TCaptchaCharsOption] of string;
|
|
FFonts: array[0..1] of TFont;
|
|
FInitialized: Boolean;
|
|
FMaxAngle: Integer;
|
|
FNewCaptchaEvent: TNewCaptchaEvent;
|
|
FNumChars: Integer;
|
|
FNumLines: Integer;
|
|
FOptions: TCaptchaOptions;
|
|
function GetCaptchaText: String;
|
|
function GetFont(AIndex: Integer): TFont;
|
|
function GetValidChars(AIndex: Integer): String;
|
|
procedure SetFont(AIndex: Integer; const AValue: TFont);
|
|
procedure SetMaxAngle(const AValue: Integer);
|
|
procedure SetNumChars(const AValue: Integer);
|
|
procedure SetNumLines(const AValue: Integer);
|
|
procedure SetOptions(const AValue: TCaptchaOptions);
|
|
procedure SetValidChars(AIndex: Integer; const AValue: String);
|
|
protected
|
|
function AlmostBackgroundColor(AColor: TColor): Boolean;
|
|
procedure CreateNewCaptcha(ANumChars, ANumLines: Integer; KeepText,KeepLines: Boolean);
|
|
procedure DrawBuffer;
|
|
procedure InitAngles;
|
|
procedure InitCharPos(KeepVertPos: boolean);
|
|
procedure InitFontIndex;
|
|
procedure InitLineColors;
|
|
procedure InitLines(ACount: Integer; KeepExisting: Boolean);
|
|
procedure InitText(ACount: Integer; KeepExisting: Boolean);
|
|
procedure InitTextColors;
|
|
protected
|
|
procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer;
|
|
WithThemeSpace: Boolean); override;
|
|
procedure Click; override;
|
|
procedure DblClick; override;
|
|
procedure Paint; override;
|
|
procedure Resize; override;
|
|
procedure SetColor(AValue: TColor); override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure NewCaptcha;
|
|
function Verify(const AText: String): Boolean;
|
|
property Text: String read GetCaptchaText;
|
|
published
|
|
property CustomChars: String index ord(coCustom) read GetValidChars write SetValidChars;
|
|
property Font1: TFont index 0 read GetFont write SetFont;
|
|
property Font2: TFont index 1 read GetFont write SetFont;
|
|
property Options: TCaptchaOptions read FOptions write SetOptions default DEFAULT_CAPTCHA_OPTIONS;
|
|
property LowercaseChars: String index ord(coAlphaLower) read GetValidChars write SetValidChars;
|
|
property MaxAngle: Integer read FMaxAngle write SetMaxAngle default 60;
|
|
property NumericChars: String index ord(coNumeric) read GetValidChars write SetValidChars;
|
|
property NewCaptchaEvent: TNewCaptchaEvent read FNewCaptchaEvent write FNewCaptchaEvent default nceNone;
|
|
property NumChars: Integer read FNumChars write SetNumChars default DEFAULT_CAPTCHA_NUMCHARS;
|
|
property NumLines: Integer read FNumLines write SetNumLines default DEFAULT_CAPTCHA_NUMLINES;
|
|
property UppercaseChars: String index ord(coAlphaUpper) read GetValidChars write SetValidChars;
|
|
|
|
property Align;
|
|
property AutoSize default true;
|
|
property BorderSpacing;
|
|
property Color default clBlack;
|
|
property OnClick;
|
|
property OnDblClick;
|
|
property OnMouseDown;
|
|
property OnMouseEnter;
|
|
property OnMouseLeave;
|
|
property OnMouseUp;
|
|
end;
|
|
|
|
procedure Register;
|
|
|
|
implementation
|
|
|
|
{$R captcha_images.res}
|
|
|
|
uses
|
|
LCLIntf, Types, GraphUtil, Math, LazUTF8;
|
|
|
|
{ Component registration }
|
|
|
|
procedure Register;
|
|
begin
|
|
RegisterComponents('Misc', [TCaptchaLabel]);
|
|
end;
|
|
|
|
|
|
{ Utility functions }
|
|
|
|
function RotatePoint(const APoint: TPoint; Angle: Double): TPoint;
|
|
var
|
|
sinphi, cosphi: Double;
|
|
begin
|
|
Angle := DegToRad(Angle);
|
|
SinCos(angle, sinphi, cosphi);
|
|
Result.X := Round( cosphi * APoint.X + sinphi * APoint.Y);
|
|
Result.Y := Round(-sinphi * APoint.X + cosphi * APoint.Y);
|
|
end;
|
|
|
|
function RotateRect(const Width, Height: Integer; Angle: Double): TRect;
|
|
var
|
|
P0, P1, P2, P3: TPoint;
|
|
begin
|
|
P0 := Point(0, 0);
|
|
P1 := RotatePoint(Point(0, Height), Angle);
|
|
P2 := RotatePoint(Point(Width, 0), Angle);
|
|
P3 := RotatePoint(Point(Width, Height), Angle);
|
|
Result.Left := MinValue([P0.X, P1.X, P2.X, P3.X]);
|
|
Result.Top := MinValue([P0.Y, P1.Y, P2.Y, P3.Y]);
|
|
Result.Right := MaxValue([P0.X, P1.X, P2.X, P3.X]);
|
|
Result.Bottom := MaxValue([P0.Y, P1.Y, P2.Y, P3.Y]);
|
|
end;
|
|
|
|
|
|
{ TCaptchaLabel }
|
|
|
|
constructor TCaptchaLabel.Create(AOwner: TComponent);
|
|
begin
|
|
inherited;
|
|
with GetControlClassDefaultSize do
|
|
SetInitialBounds(0, 0, 300, 100);
|
|
AutoSize := true;
|
|
Color := clBlack;
|
|
|
|
FBuffer := TBitmap.Create;
|
|
FBuffer.PixelFormat := pf32bit;
|
|
|
|
FFonts[0] := TFont.Create;
|
|
FFonts[0].Size := 36;
|
|
|
|
FFonts[1] := TFont.Create;
|
|
{$IF DEFINED(MSWindows)}
|
|
FFonts[1].Name := 'Courier New';
|
|
{$ELSEIF DEFINED(Linux)}
|
|
FFonts[1].Name := 'FreeMono';
|
|
{$ELSEIF DEFINED(Darwin)}
|
|
Fronts[1].Name := 'Courier';
|
|
{$IFEND}
|
|
FFonts[1].Size := 36;
|
|
|
|
FOptions := DEFAULT_CAPTCHA_OPTIONS;
|
|
FMaxAngle := 60;
|
|
FNumChars := DEFAULT_CAPTCHA_NUMCHARS;
|
|
FNumLines := DEFAULT_CAPTCHA_NUMLINES;
|
|
FValidChars[coAlphaUpper] := 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
FValidChars[coAlphaLower] := 'abcdefghijklmnopqrstuvwxyz';
|
|
FValidChars[coNumeric] := '0123456789';
|
|
FValidChars[coCustom] := '';
|
|
FInitialized := false;
|
|
|
|
// Do not call Randomize at runtime to facilitate debugging.
|
|
if (csDesigning in ComponentState) then
|
|
Randomize;
|
|
end;
|
|
|
|
destructor TCaptchaLabel.Destroy;
|
|
begin
|
|
Finalize(FCaptchaChars);
|
|
Finalize(FCaptchaLines);
|
|
FreeAndNil(FFonts[0]);
|
|
FreeAndNil(FFonts[1]);
|
|
FreeAndNil(FBuffer);
|
|
inherited;
|
|
end;
|
|
|
|
function TCaptchaLabel.AlmostBackgroundColor(AColor: TColor): Boolean;
|
|
const
|
|
TOLERANCE = 64;
|
|
var
|
|
colorH, colorL, colorS: Byte;
|
|
bgColorH, bgColorL, bgColorS: Byte;
|
|
begin
|
|
ColorToHLS(ColorToRGB(AColor), colorH, colorL, colorS);
|
|
ColorToHLS(ColorToRGB(Self.Color), bgColorH, bgColorL, bgColorS);
|
|
Result := abs(colorL - bgColorL) < TOLERANCE;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.CalculatePreferredSize(
|
|
var PreferredWidth, PreferredHeight: integer;
|
|
WithThemeSpace: Boolean);
|
|
begin
|
|
inherited;
|
|
|
|
CreateNewCaptcha(FNumChars, FNumLines, true, true);
|
|
|
|
PreferredWidth := FBuffer.Width;
|
|
PreferredHeight := 0;
|
|
if (coFont1 in FOptions) then
|
|
begin
|
|
FBuffer.Canvas.Font.Assign(FFonts[0]);
|
|
PreferredHeight := FBuffer.Canvas.TextHeight('Tg');
|
|
end;
|
|
if (coFont2 in FOptions) then
|
|
begin
|
|
FBuffer.Canvas.Font.Assign(FFonts[1]);
|
|
PreferredHeight := Max(PreferredHeight, FBuffer.Canvas.TextHeight('Tg'));
|
|
end;
|
|
PreferredHeight := 3*PreferredHeight div 2;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.Click;
|
|
begin
|
|
inherited;
|
|
if FNewCaptchaEvent = nceClick then
|
|
NewCaptcha;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.CreateNewCaptcha(ANumChars, ANumLines: Integer;
|
|
KeepText, KeepLines: Boolean);
|
|
begin
|
|
if not KeepText then
|
|
FCaptchaChars := nil;
|
|
FCaptchaLines := nil;
|
|
InitText(ANumChars, KeepText);
|
|
InitTextColors; // after InitText
|
|
InitAngles;
|
|
InitCharPos(false);
|
|
InitLines(ANumLines, KeepLines); // after InitCharPos
|
|
InitLineColors; // after InitLines
|
|
DrawBuffer;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.DblClick;
|
|
begin
|
|
inherited;
|
|
if FNewCaptchaEvent = nceDblClick then
|
|
NewCaptcha;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.DrawBuffer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if not Assigned(FBuffer) then
|
|
exit;
|
|
|
|
// Fill the buffer background in the requested color.
|
|
FBuffer.Canvas.Brush.Color := Self.Color;
|
|
FBuffer.Canvas.Brush.Style := bsSolid;
|
|
FBuffer.Canvas.FillRect(0, 0, FBuffer.Width, FBuffer.Height);
|
|
|
|
// Draw the captcha characters to the buffer bitmap
|
|
if (FOptions * [coAlphaUpper, coAlphaLower, coNumeric, coCustom] <> []) and
|
|
(FOptions * [coFont1, coFont2] <> []) then
|
|
begin
|
|
FBuffer.Canvas.Brush.Style := bsClear;
|
|
for i := 0 to High(FCaptchaChars) do
|
|
with FCaptchaChars[i] do
|
|
begin
|
|
FBuffer.Canvas.Font.Assign(FFonts[FontIndex]);
|
|
FBuffer.Canvas.Font.Color := Color;
|
|
if coRotated in FOptions then
|
|
FBuffer.Canvas.Font.Orientation := Angle * 10
|
|
else
|
|
FBuffer.Canvas.Font.Orientation := 0;
|
|
FBuffer.Canvas.TextOut(Position.X, Position.Y, Character);
|
|
end;
|
|
end;
|
|
|
|
// Draw the captcha lines
|
|
if coLines in FOptions then
|
|
begin
|
|
for i := 0 to High(FCaptchaLines) do
|
|
with FCaptchaLines[i] do
|
|
begin
|
|
FBuffer.Canvas.Pen.Color := Color;
|
|
FBuffer.Canvas.Line(StartPt.X, StartPt.Y, EndPt.X, EndPt.Y);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCaptchaLabel.GetFont(AIndex: Integer): TFont;
|
|
begin
|
|
Result := FFonts[AIndex];
|
|
end;
|
|
|
|
function TCaptchaLabel.GetCaptchaText: string;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := '';
|
|
for i := 0 to High(FCaptchaChars) do
|
|
Result := Result + FCaptchaChars[i].Character;
|
|
end;
|
|
|
|
function TCaptchaLabel.GetValidChars(AIndex: Integer): String;
|
|
begin
|
|
Result := FValidChars[TCaptchaCharsOption(AIndex)];
|
|
end;
|
|
|
|
procedure TCaptchaLabel.InitAngles;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to High(FCaptchaChars) do
|
|
FCaptchaChars[i].Angle := Random(FMaxAngle*2) - FMaxAngle;
|
|
end;
|
|
|
|
{ Calculates the character positions and stores them in the ChaptchaChars array
|
|
When KeepVertPos is false, the vertical position of the characters is selected
|
|
randomly within the height of the control. Otherwise the already stored
|
|
vertical positions are used. }
|
|
procedure TCaptchaLabel.InitCharPos(KeepVertPos: Boolean);
|
|
var
|
|
x: Integer;
|
|
i: Integer;
|
|
R: TRect;
|
|
ext: TSize;
|
|
w, h: Integer;
|
|
fnt: TFont;
|
|
maxHeight: Integer;
|
|
begin
|
|
maxHeight := 0;
|
|
x := 0;
|
|
for i := 0 to High(FCaptchaChars) do
|
|
begin
|
|
// Set character font
|
|
fnt := FFonts[FCaptchaChars[i].FontIndex];
|
|
FBuffer.Canvas.Font.Assign(fnt);
|
|
|
|
// Get character size
|
|
ext := FBuffer.Canvas.TextExtent(FCaptchaChars[i].Character);
|
|
|
|
// Rotate the character and get the bounds of the enclosing rectangle.
|
|
// The rotation occurs around the upper left corner of the character.
|
|
if coRotated in FOptions then
|
|
R := RotateRect(ext.CX, ext.CY, FCaptchaChars[i].Angle)
|
|
else
|
|
// unrotated: add some extra space for better legibility
|
|
R := Rect(0, 0, ext.CX * 6 div 5, ext.CY);
|
|
w := R.Right - R.Left;
|
|
h := R.Bottom - R.Top;
|
|
|
|
// Horizontal drawing coordinate
|
|
FCaptchaChars[i].Position.X := x - R.Left;
|
|
|
|
// Vertical drawing coordinate: randomly inside control
|
|
if not KeepVertPos then
|
|
begin
|
|
if Self.Height > h then
|
|
FCaptchaChars[i].Position.Y := Max(0, Random(Height - h) - R.Top)
|
|
else
|
|
FCaptchaChars[i].Position.Y := 0;
|
|
end;
|
|
|
|
// Find max y coordinate needed to enclose the entire text
|
|
maxHeight := Max(maxHeight, FCaptchaChars[i].Position.Y + h);
|
|
// Next drawing position
|
|
x := x + w;
|
|
end;
|
|
|
|
// Set size of the bitmap buffer so that the entire captcha is enclosed.
|
|
FBuffer.SetSize(x, maxHeight);
|
|
end;
|
|
|
|
procedure TCaptchaLabel.InitFontIndex;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if FOptions * [coFont1, coFont2] = [coFont1] then
|
|
for i := 0 to High(FCaptchaChars) do
|
|
FCaptchaChars[i].FontIndex := 0
|
|
else
|
|
if FOptions * [coFont1, coFont2] = [coFont2] then
|
|
for i := 0 to High(FCaptchaChars) do
|
|
FCaptchaChars[i].FontIndex := 1
|
|
else
|
|
for i := 0 to High(FCaptchaChars) do
|
|
FCaptchaChars[i].FontIndex := Random(2);
|
|
end;
|
|
|
|
{ Pick random color for a line.
|
|
Make sure that the color is not too close to the background color. }
|
|
procedure TCaptchaLabel.InitLineColors;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
// Line colors
|
|
if (FOptions * [coLines] <> []) then
|
|
for i := 0 to High(FCaptchaLines) do
|
|
repeat
|
|
FCaptchaLines[i].Color := TColor(Random($FFFFFF));
|
|
until not AlmostBackgroundColor(FCaptchaLines[i].Color);
|
|
end;
|
|
|
|
procedure TCaptchaLabel.InitLines(ACount: Integer; KeepExisting: Boolean);
|
|
var
|
|
i, n: Integer;
|
|
begin
|
|
if coLines in FOptions then
|
|
begin
|
|
if KeepExisting then
|
|
n := Length(FCaptchaLines)
|
|
else
|
|
n := 0;
|
|
SetLength(FCaptchaLines, ACount);
|
|
for i := n to High(FCaptchaLines) do
|
|
begin
|
|
// Select random start and end points
|
|
FCaptchaLines[i].StartPt := Point(
|
|
Random(FBuffer.Width),
|
|
Random(FBuffer.Height)
|
|
);
|
|
FCaptchaLines[i].EndPt := Point(
|
|
Random(FBuffer.Width),
|
|
Random(FBuffer.Height)
|
|
);
|
|
|
|
// Select random line color
|
|
repeat
|
|
FCaptchaLines[i].Color := TColor(Random($FFFFFF));
|
|
until not AlmostBackgroundColor(FCaptchaLines[i].Color);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.InitText(ACount: Integer; KeepExisting: Boolean);
|
|
var
|
|
i, n: Integer;
|
|
validChars: String;
|
|
co: TCaptchaCharsOption;
|
|
begin
|
|
if (FOptions * [coAlphaUpper, coAlphaLower, coNumeric, coCustom] <> []) and
|
|
(FOptions * [coFont1, coFont2] <> []) then
|
|
begin
|
|
// Prepare character list for captcha
|
|
validChars := '';
|
|
for co in TCaptchaCharsOption do
|
|
if co in FOptions then
|
|
validChars := validChars + FValidChars[co];;
|
|
// Remove characters which are hard to distinguish
|
|
if FOptions * [coAlphaUpper, coAlphaLower] = [coAlphaUpper, coAlphaLower] then
|
|
begin
|
|
i := Pos('I', validChars); // Remove upper-case I
|
|
if i > 0 then Delete(validChars, i, 1);
|
|
i := Pos('l', validChars); // Remove lower-case L
|
|
if i > 0 then Delete(validChars, i, 1);
|
|
end;
|
|
if FOptions * [coAlphaUpper, coNumeric] = [coAlphaUpper, coNumeric] then
|
|
begin
|
|
i := Pos('O', validChars); // Remove upper-case O
|
|
if i > 0 then Delete(validChars, i, 1);
|
|
i := Pos('0', validChars); // Remove number zero
|
|
if i > 0 then Delete(validChars, i, 1);
|
|
end;
|
|
|
|
if KeepExisting then
|
|
n := Length(FCaptchaChars)
|
|
else
|
|
n := 0;
|
|
// Get random captcha characters, but keep previously assigned chars.
|
|
SetLength(FCaptchaChars, ACount);
|
|
for i := n to High(FCaptchaChars) do
|
|
begin
|
|
// Pick random character from the validChars. Take care of UTF8.
|
|
FCaptchaChars[i].Character := UTF8Copy(validChars, random(UTF8Length(validChars)) + 1, 1);
|
|
|
|
// Pick one of the fonts
|
|
if FOptions * [coFont1, coFont2] = [coFont1] then
|
|
FCaptchaChars[i].FontIndex := 1
|
|
else
|
|
if FOptions * [coFont1, coFont2] = [coFont2] then
|
|
FCaptchaChars[i].FontIndex := 2
|
|
else
|
|
FCaptchaChars[i].FontIndex := Random(2);
|
|
|
|
if KeepExisting then
|
|
begin
|
|
// Set random text color
|
|
repeat
|
|
FCaptchaChars[i].Color := TColor(Random($FFFFFF));
|
|
until not AlmostbackgroundColor(FCaptchaChars[i].Color);
|
|
|
|
// Set random rotation angle
|
|
if (coRotated in FOptions) then
|
|
FCaptchaChars[i].Angle := Random(FMaxAngle*2) - FMaxAngle;
|
|
end;
|
|
end;
|
|
end else
|
|
SetLength(FCaptchaChars, 0);
|
|
end;
|
|
|
|
{ Pick random color for a character.
|
|
Make sure that the color is not too close to the background color. }
|
|
procedure TCaptchaLabel.InitTextColors;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
// Character colors
|
|
if (FOptions * [coAlphaUpper, coAlphaLower, coNumeric, coCustom] <> []) then
|
|
for i := 0 to High(FCaptchaChars) do
|
|
repeat
|
|
FCaptchaChars[i].Color := TColor(Random($FFFFFF));
|
|
until not AlmostbackgroundColor(FCaptchaChars[i].Color);
|
|
end;
|
|
|
|
procedure TCaptchaLabel.NewCaptcha;
|
|
begin
|
|
CreateNewCaptcha(FNumChars, FNumLines, false, false);
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.Paint;
|
|
begin
|
|
Canvas.Draw((Width - FBuffer.Width) div 2, (Height - FBuffer.Height) div 2, FBuffer);
|
|
end;
|
|
|
|
procedure TCaptchaLabel.Resize;
|
|
begin
|
|
inherited;
|
|
if Assigned(FBuffer) and not FInitialized then
|
|
begin
|
|
CreateNewCaptcha(FNumChars, FNumLines, false, false);
|
|
FInitialized := true;
|
|
end;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.SetColor(AValue: TColor);
|
|
begin
|
|
if AValue = Color then
|
|
exit;
|
|
inherited SetColor(AValue);
|
|
InitTextColors;
|
|
InitLineColors;
|
|
DrawBuffer;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.SetFont(AIndex: Integer; const AValue: TFont);
|
|
begin
|
|
if FFonts[AIndex].IsEqual(AValue) then
|
|
exit;
|
|
FFonts[AIndex].Assign(AValue);
|
|
InitFontIndex;
|
|
InitCharPos(true);
|
|
DrawBuffer;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.SetMaxAngle(const AValue: Integer);
|
|
begin
|
|
if AValue = FMaxAngle then
|
|
exit;
|
|
FMaxAngle := AValue;
|
|
InitAngles;
|
|
InitCharPos(true);
|
|
DrawBuffer;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.SetNumChars(const AValue: Integer);
|
|
begin
|
|
if AValue = FNumChars then
|
|
exit;
|
|
FNumChars := AValue;
|
|
InitText(FNumChars, true);
|
|
InitAngles;
|
|
InitCharPos(false);
|
|
InitLines(FNumLines, false);
|
|
DrawBuffer;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.SetNumLines(const AValue: Integer);
|
|
begin
|
|
if AValue = FNumLines then
|
|
exit;
|
|
FNumLines := AValue;
|
|
InitLines(FNumLines, true);
|
|
DrawBuffer;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.SetOptions(const AValue: TCaptchaOptions);
|
|
var
|
|
oldOptions: TCaptchaOptions;
|
|
begin
|
|
if AValue = FOptions then
|
|
exit;
|
|
oldOptions := FOptions;
|
|
FOptions := AValue;
|
|
if (oldOptions * [coAlphaUpper, coAlphaLower, coNumeric, coCustom] <>
|
|
AValue * [coAlphaUpper, coAlphaLower, coNumeric, coCustom])
|
|
then
|
|
InitText(FNumChars, false);
|
|
if (oldOptions * [coFont1, coFont2] <> AValue * [coFont1, coFont2]) then
|
|
begin
|
|
InitFontIndex;
|
|
InitCharPos(false);
|
|
end;
|
|
if (oldOptions * [coRotated] <> AValue * [coRotated]) then
|
|
begin
|
|
InitAngles;
|
|
InitCharPos(true);
|
|
end;
|
|
if oldOptions * [coLines] <> AValue * [coLines] then
|
|
InitLines(FNumLines, true);
|
|
DrawBuffer;
|
|
Invalidate;
|
|
end;
|
|
|
|
procedure TCaptchaLabel.SetValidChars(AIndex: Integer; const AValue: String);
|
|
begin
|
|
if FValidChars[TCaptchaCharsOption(AIndex)] = AValue then
|
|
exit;
|
|
FValidChars[TCaptchaCharsOption(AIndex)] := AValue;
|
|
NewCaptcha;
|
|
end;
|
|
|
|
|
|
function TCaptchaLabel.Verify(const AText: String): Boolean;
|
|
begin
|
|
Result := (AText = GetCaptchaText);
|
|
end;
|
|
|
|
end.
|
|
|