{***********************************************************************
 Package pl_Barcode
 This unit is part of CodeTyphon Studio (http://www.pilotlogic.com/)
***********************************************************************}

unit ubarcodes;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Controls, LResources, Graphics,
  clipbrd,
  ubasic,uqr,zint,burender,uaztec,udatamatrix;

type

TBarcodeQR_ECCLevel= (eBarcodeQR_ECCLevel_Auto=0,
                      eBarcodeQR_ECCLevel_L=1,eBarcodeQR_ECCLevel_M=2,
                      eBarcodeQR_ECCLevel_Q=3,eBarcodeQR_ECCLevel_H=4);

TBarcodeAztecRune_Value= 0..999;

{ TLazBarcodeCustomBase }

TLazBarcodeCustomBase=class(TGraphicControl)
private
  mIsPainting: Boolean;
  function GetStrictSize: Boolean;
  procedure SetStrictSize(const AValue: Boolean);
  function GetBackgroundColor: TColor;
  function GetForegroundColor: TColor;
  procedure SetBackgroundColor(const AValue: TColor);
  procedure SetForegroundColor(const AValue: TColor);
protected
  FQR: PointerTo_zint_symbol;
  FLastErrorString: UTF8String;
  FBackgroundColor: TColor;
  FForegroundColor: TColor;
  FStrictSize: Boolean;
  procedure GenerateAndInvalidate;
  procedure Paint; override;
  procedure Resize; override;
  procedure intfPaintOnCanvas(const aTargetCanvas: TCanvas; const aRect: TRect);
public
  constructor Create(AOwner: TComponent); override;
  destructor Destroy; override;
  procedure CopyToClipboard(AWidth: Integer = -1; AHeight: Integer = -1);
  procedure PaintOnCanvas(const aTargetCanvas: TCanvas; const aRect: TRect);
  procedure Generate; virtual; abstract;
published
  property StrictSize: Boolean read GetStrictSize write SetStrictSize;
  property BackgroundColor: TColor read GetBackgroundColor write SetBackgroundColor default clWhite;
  property ForegroundColor: TColor read GetForegroundColor write SetForegroundColor default clBlack;
  property Width default 88;
  property Height default 88;

  property OnPaint;
  property OnResize;
  property OnShowHint;
  property OnClick;
  property OnDblClick;
  property OnMouseDown;
  property OnMouseMove;
  property OnMouseUp;
  property OnMouseEnter;
  property OnMouseLeave;
  property OnMouseWheel;
  property OnMouseWheelDown;
  property OnMouseWheelUp;
  property OnDragDrop;
  property OnDragOver;
  property OnEndDock;
  property OnEndDrag;
  property OnStartDock;
  property OnStartDrag;
end;

{ TLazBarcodeCustomText }

TLazBarcodeCustomText=class(TLazBarcodeCustomBase)
private
  function GetText: UTF8String;
  procedure SetText(const AValue: UTF8String);
protected
  FText: UTF8String;
public
  constructor Create(AOwner: TComponent); override;
published
  property Text: UTF8String read GetText write SetText;
end;

{ TBarcodeQR }

TBarcodeQR=class(TLazBarcodeCustomText)
private
  procedure SetECCLevel(const AValue: TBarcodeQR_ECCLevel);
protected
  FECCLevel: TBarcodeQR_ECCLevel;
  procedure UpdateECCLevel;
public
  procedure Generate; override;
published
  property ECCLevel: TBarcodeQR_ECCLevel read FECCLevel write SetECCLevel default eBarcodeQR_ECCLevel_Auto;
end;

{ TBarcodeMicroQR }

TBarcodeMicroQR=class(TBarcodeQR)
private
protected
public
  procedure Generate; override;
published
end;

{ TBarcodeAztec }

TBarcodeAztec=class(TLazBarcodeCustomText)
private
protected
public
  procedure Generate; override;
published
end;

{ TBarcodeAztecRune }

TBarcodeAztecRune=class(TLazBarcodeCustomBase)
private
  procedure SetValue(const AValue: TBarcodeAztecRune_Value);
protected
  FValue: TBarcodeAztecRune_Value;
public
  procedure Generate; override;
published
  property Value: TBarcodeAztecRune_Value read FValue write SetValue;
end;

{ TBarcodeDataMatrix }

TBarcodeDataMatrix=class(TLazBarcodeCustomText)
private
protected
public
  procedure Generate; override;
published
end;

implementation

{ TBarcodeDataMatrix }

procedure TBarcodeDataMatrix.Generate;
var
  ErrorCode: integer;
begin
  if Assigned(FQR) then begin
    ZBarcode_Delete(FQR);
    FQR:=nil;
  end;
  if Length(FText)>0 then begin
    FQR:=ZBarcode_Create();
    with FQR^ do begin
      border_width:=1;
    end;
    ErrorCode:=dmatrix(FQR,@FText[1],Length(FText));
    if ErrorCode<>0 then begin
      FLastErrorString:=FQR^.errtxt;
      exit;
    end;
  end;
end;

{ TBarcodeAztecRune }

procedure TBarcodeAztecRune.SetValue(const AValue: TBarcodeAztecRune_Value);
begin
  if FValue=AValue then exit;
  FValue:=AValue;
  GenerateAndInvalidate;
end;

procedure TBarcodeAztecRune.Generate;
var
  ErrorCode: integer;
  NumberText: ansistring;
begin
  if Assigned(FQR) then begin
    ZBarcode_Delete(FQR);
    FQR:=nil;
  end;
  NumberText:=IntToStr(FValue);
  if (Length(NumberText)>0) and (Length(NumberText)<=3) then begin
    FQR:=ZBarcode_Create();
    with FQR^ do begin
      border_width:=0;
    end;
    ErrorCode:=aztec_runes(FQR,@NumberText[1],Length(NumberText));
    if ErrorCode<>0 then begin
      FLastErrorString:=FQR^.errtxt;
      exit;
    end;
  end;
end;

{ TBarcodeAztec }

procedure TBarcodeAztec.Generate;
var
  ErrorCode: integer;
begin
  if Assigned(FQR) then begin
    ZBarcode_Delete(FQR);
    FQR:=nil;
  end;
  if Length(FText)>0 then begin
    FQR:=ZBarcode_Create();
    with FQR^ do begin
      border_width:=0;
    end;
    ErrorCode:=aztec(FQR,@FText[1],Length(FText));
    if ErrorCode<>0 then begin
      FLastErrorString:=FQR^.errtxt;
      exit;
    end;
  end;
end;

{ TLazBarcodeCustomBase }

function TLazBarcodeCustomBase.GetStrictSize: Boolean;
begin
  Result:=FStrictSize;
end;

procedure TLazBarcodeCustomBase.SetStrictSize(const AValue: Boolean);
begin
  if FStrictSize<>AValue then begin
    FStrictSize:=AValue;
    GenerateAndInvalidate;
  end;
end;

function TLazBarcodeCustomBase.GetBackgroundColor: TColor;
begin
  Result:=FBackgroundColor;
end;

function TLazBarcodeCustomBase.GetForegroundColor: TColor;
begin
  Result:=FForegroundColor;
end;

procedure TLazBarcodeCustomBase.SetBackgroundColor(const AValue: TColor);
begin
  if FBackgroundColor<>AValue then begin
    FBackgroundColor:=AValue;
    GenerateAndInvalidate;
  end;
end;

procedure TLazBarcodeCustomBase.SetForegroundColor(const AValue: TColor);
begin
  if FForegroundColor<>AValue then begin
    FForegroundColor:=AValue;
    GenerateAndInvalidate;
  end;
end;

procedure TLazBarcodeCustomBase.GenerateAndInvalidate;
begin
  Generate;
  Self.Invalidate;
end;

procedure TLazBarcodeCustomBase.Paint;
begin
  if mIsPainting then exit;
  mIsPainting:=true;
  intfPaintOnCanvas(Canvas, self.ClientRect);
  mIsPainting:=false;
end;

procedure TLazBarcodeCustomBase.Resize;
begin
  inherited Resize;
  Generate;
end;

constructor TLazBarcodeCustomBase.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FBackgroundColor:=clWhite;
  FForegroundColor:=clBlack;
  FStrictSize:=true;
  self.SetInitialBounds(0,0,88,88);  // ct9999 for CodeTyphon Studio
end;

destructor TLazBarcodeCustomBase.Destroy;
begin
  if Assigned(FQR) then begin
    ZBarcode_Delete(FQR);
    FQR:=nil;
  end;
  inherited Destroy;
end;

procedure TLazBarcodeCustomBase.CopyToClipboard(AWidth, AHeight: Integer);
var
  bmp: TBitmap;
begin
  if AWidth = -1 then AWidth := Width;
  if AHeight = -1 then AHeight := Height;
  bmp := TBitmap.Create;
  try
    bmp.SetSize(AWidth, AHeight);
    bmp.Canvas.Brush.Color := clWhite;
    bmp.Canvas.FillRect(0, 0, AWidth, AHeight);
    PaintOnCanvas(bmp.Canvas, Rect(0, 0, AWidth, AHeight));
    Clipboard.Assign(bmp);
  finally
    bmp.Free;
  end;
end;

procedure TLazBarcodeCustomBase.PaintOnCanvas(const aTargetCanvas: TCanvas;
  const aRect: TRect);
begin
  //Destroy rendering
  Generate;
  //Create new rendering
  intfPaintOnCanvas(aTargetCanvas,aRect);
  //Destroy rendering, new rendering generated when paint called.
  Generate;
end;

procedure TLazBarcodeCustomBase.intfPaintOnCanvas(const aTargetCanvas: TCanvas;
  const aRect: TRect);
var
  ErrorCode: integer;
  Line: PointerTo_zint_render_line;
  BaseX,BaseY: integer;
  X,Y: integer;
  procedure ClearBackground;
  begin
    aTargetCanvas.Brush.Color:=FBackgroundColor;
    aTargetCanvas.FillRect(aRect);
  end;
begin
  if not aTargetCanvas.HandleAllocated then exit;
  if not Assigned(FQR) then begin
    ClearBackground;
    exit;
  end;
  if not Assigned(FQR^.rendered) then begin
    X:=aRect.Right-aRect.Left+1;
    Y:=aRect.Bottom-aRect.Top+1;
    if not FStrictSize then begin
      ErrorCode:=render_plot(FQR,X,Y);
    end else begin
      BaseX:=FQR^.width+FQR^.border_width*2;
      BaseY:=FQR^.rows+FQR^.border_width*2;
      ErrorCode:=render_plot(FQR,X-(X mod BaseX),(Y-(Y mod BaseY)));
    end;
    if ErrorCode<>1 then begin
      FLastErrorString:=FQR^.errtxt;
      exit;
    end else begin
      FLastErrorString:='';
    end;
  end;
  if Assigned(FQR^.rendered) then begin
    Line:=FQR^.rendered^.lines;
    ClearBackground;
    aTargetCanvas.Brush.Color:=FForegroundColor;
    while Assigned(Line) do begin
      aTargetCanvas.FillRect( round(Line^.x)+aRect.Left,round(Line^.y)+aRect.Top,
                              round(Line^.x+Line^.width)+aRect.Left,round(Line^.y+Line^.length)+aRect.Top);
      Line:=Line^.next;
    end;
  end;
end;

function TLazBarcodeCustomText.GetText: UTF8String;
begin
  Result:=FText;
end;

procedure TLazBarcodeCustomText.SetText(const AValue: UTF8String);
begin
  if FText<>AValue then begin
    FText:=AValue;
    GenerateAndInvalidate;
  end;
end;

constructor TLazBarcodeCustomText.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FText:=ClassName;
end;

{ TBarcodeQR }

procedure TBarcodeQR.SetECCLevel(const AValue: TBarcodeQR_ECCLevel);
begin
  if FECCLevel=AValue then exit;
  FECCLevel:=AValue;
  GenerateAndInvalidate;
end;

procedure TBarcodeQR.UpdateECCLevel;
begin
  case FECCLevel of
    eBarcodeQR_ECCLevel_L: FQR^.option_1:=1;
    eBarcodeQR_ECCLevel_M: FQR^.option_1:=2;
    eBarcodeQR_ECCLevel_Q: FQR^.option_1:=3;
    eBarcodeQR_ECCLevel_H: FQR^.option_1:=4;
    else begin
      FQR^.option_1:=0;
      FECCLevel:=eBarcodeQR_ECCLevel_Auto;
    end;
  end;
end;

procedure TBarcodeQR.Generate;
var
  ErrorCode: integer;
begin
  if Assigned(FQR) then begin
    ZBarcode_Delete(FQR);
    FQR:=nil;
  end;
  if Length(FText) > 0 then begin
    FQR:=ZBarcode_Create();
    with FQR^ do begin
      border_width:=4;
    end;
    UpdateECCLevel;
    ErrorCode:=qr_code(FQR,@FText[1],Length(FText));
    if ErrorCode<>0 then begin
      FLastErrorString:=FQR^.errtxt;
      exit;
    end;
  end;
end;

{ TBarcodeMicroQR }

procedure TBarcodeMicroQR.Generate;
var
  ErrorCode: integer;
begin
  if Assigned(FQR) then begin
    ZBarcode_Delete(FQR);
    FQR:=nil;
  end;
  if Length(FText) > 0 then begin
    FQR:=ZBarcode_Create();
    with FQR^ do begin
      border_width:=4;
    end;
    UpdateECCLevel;
    ErrorCode:=microqr(FQR,@FText[1],Length(FText));
    if ErrorCode<>0 then begin
      FLastErrorString:=FQR^.errtxt;
      exit;
    end;
  end;
end;


end.
