unit Barcode;

{
Barcode Component
Version 1.5 (23 Apr 1999)
Copyright 1998-99 Andreas Schmidt and friends

Freeware

for use with Delphi 2/3/4


this component is for private use only !
i'am not responsible for wrong barcodes

bug-reports, enhancements:
mailto:shmia@bizerba.de or a_j_schmidt@rocketmail.com

get latest version from
http://members.tripod.de/AJSchmidt/index.html


thanx to Nikolay Simeonov, Wolfgang Koranda, Norbert Waas,
Richard Hugues and Olivier Guilbaud.



Diese Komponente darf nur in privaten Projekten verwendet werden.
Die Weitergabe von veränderte Dateien ist nicht zulässig.
Für die Korrektheit der erzeugten Barcodes kann keine Garantie
übernommen werden.
Anregungen, Bug-Reports, Danksagungen an:
mailto:shmia@bizerba.de



History:
----------------------------------------------------------------------
Version 1.0:
- initial release
Version 1.1:
- more comments
- changed function Code_93Extended (now correct ?)
Version 1.2:
- Bugs (found by Nikolay Simeonov) removed
Version 1.3:
- EAN8/EAN13 added by Wolfgang Koranda (wkoranda@csi.com)
Version 1.4:
- Bug (found by Norbert Waas) removed
  Component must save the Canvas-properties Font,Pen and Brush
Version 1.5:
- Bug (found by Richard Hugues) removed
  Last line of barcode was 1 Pixel too wide
Version 1.6:
- new read-only property 'Width'



Todo (missing features)
-----------------------
- Wrapper Class for Quick Reports



}


interface

{$I lr_vers.inc}

uses
  SysUtils, Math, Classes, Graphics, Controls, Forms, Dialogs;

type
  TBarcodeType = (bcCode_2_5_interleaved,
    bcCode_2_5_industrial,
    bcCode_2_5_matrix,
    bcCode39,
    bcCode39Extended,
    bcCode128A,
    bcCode128B,
    bcCode128C,
    bcCode93,
    bcCode93Extended,
    bcCodeMSI,
    bcCodePostNet,
    bcCodeCodabar,
    bcCodeEAN8,
    bcCodeEAN13
    );


  TBarLineType = (white, black, black_half);  // for internal use only
  // black_half means a black line with 2/5 height (used for PostNet)


  { TBarcode }

  TBarcode = class(TComponent)
  private
    { Private-Deklarationen }
    FHeight: integer;
    FText: string;
    FTop: integer;
    FLeft: integer;
    FModul: integer;
    FRatio: double;
    FTyp: TBarcodeType;
    FCheckSum: boolean;
    FShowText: boolean;
    FAngle: double;
    FCodetext: string;

    modules: array[0..3] of shortint;


    procedure OneBarProps(code: char; out aWidth: integer; out lt: TBarLineType);

    procedure DoLines(Data: string; Canvas: TCanvas);

    function Code_2_5_interleaved: string;
    function Code_2_5_industrial: string;
    function Code_2_5_matrix: string;
    function Code_39: string;
    function Code_39Extended: string;
    function Code_128: string;
    function Code_93: string;
    function Code_93Extended: string;
    function Code_MSI: string;
    function Code_PostNet: string;
    function Code_Codabar: string;
    function Code_EAN8: string;
    function Code_EAN13: string;

    function GetTypText: string;
    procedure MakeModules;

    procedure SetModul(v: integer);

    function GetWidth: integer;
    procedure SetText(AValue: string);
    function CleanEANValue(const AValue: string; const ASize:Byte): string;

  protected
    { Protected-Deklarationen }
    function MakeData: string;

  public
    { Public-Deklarationen }
    constructor Create(aOwner: TComponent); override;
    procedure DrawBarcode(Canvas: TCanvas);
    procedure DrawText(Canvas: TCanvas);
    function BarcodeTypeChecked(AType: TBarcodeType): boolean;

    property CodeText: string read FCodetext write FCodeText;
  published
    { Published-Deklarationen }
    // Height of Barcode (Pixel)
    property Height: integer read FHeight write FHeight;
    property Text: string read FText write SetText;
    property Top: integer read FTop write FTop;
    property Left: integer read FLeft write FLeft;
    // Width of the smallest line in a Barcode
    property Modul: integer read FModul write SetModul;
    property Ratio: double read FRatio write FRatio;
    property Typ: TBarcodeType read FTyp write FTyp default bcCode_2_5_interleaved;
    // build CheckSum ?
    property Checksum: boolean read FCheckSum write FCheckSum default False;
    // 0 - 360 degree
    property Angle: double read FAngle write FAngle;

    property ShowText: boolean read FShowText write FShowText default False;
    property Width: integer read GetWidth;
  end;

// procedure Register; // Removed by TZ

implementation


{
  converts a string from '321' to the internal representation '715'
  i need this function because some pattern tables have a different
  format :

  '00111'
  converts to '05161'
}
function Convert(s: string): string;
var
  i, v: integer;
  t: string;
begin
  t := '';
  for i := 1 to Length(s) do
  begin
    v := Ord(s[i]) - 1;

    if odd(i) then
      Inc(v, 5);
    t := t + Chr(v);
  end;
  Convert := t;
end;

(*
 * Berechne die Quersumme aus einer Zahl x
 * z.B.: Quersumme von 1234 ist 10
 *)
function quersumme(x: integer): integer;
var
  sum: integer;
begin
  sum := 0;

  while x > 0 do
  begin
    sum := sum + (x mod 10);
    x := x div 10;
  end;
  Result := sum;
end;


{
  Rotate a Point by Angle 'alpha'
}
function Rotate2D(p: TPoint; alpha: double): TPoint;
var
  sinus, cosinus: extended;
begin
  SinCos(alpha, sinus, cosinus);
  Result.x := Round(p.x * cosinus + p.y * sinus);
  Result.y := Round(-p.x * sinus + p.y * cosinus);
end;

{
  Move Point a by Vector b
}
function Translate2D(a, b: TPoint): TPoint;
begin
  Result.x := a.x + b.x;
  Result.y := a.y + b.y;
end;

constructor TBarcode.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);

  FAngle := 0.0;
  FRatio := 2.0;
  FModul := 1;
  FTyp := bcCodeEAN13;
  FCheckSum := False;
  FShowText := False;
end;

function TBarcode.GetTypText: string;
const
  bcNames: array[bcCode_2_5_interleaved..bcCodeEAN13] of string =
    (
    ('2_5_interleaved'),
    ('2_5_industrial'),
    ('2_5_matrix'),
    ('Code39'),
    ('Code39 Extended'),
    ('Code128A'),
    ('Code128B'),
    ('Code128C'),
    ('Code93'),
    ('Code93 Extended'),
    ('MSI'),
    ('PostNet'),
    ('Codebar'),
    ('EAN8'),
    ('EAN13')
    );
begin
  Result := bcNames[FTyp];
end;

// set Modul Width
procedure TBarcode.SetModul(v: integer);
begin
  if (v >= 1) and (v < 50) then
    FModul := v;
end;

{
calculate the width and the linetype of a sigle bar


  Code   Line-Color      Width               Height
------------------------------------------------------------------
        '0'   white           100%                full
        '1'   white           100%*Ratio          full
        '2'   white           150%*Ratio          full
        '3'   white           200%*Ratio          full
        '5'   black           100%                full
        '6'   black           100%*Ratio          full
        '7'   black           150%*Ratio          full
        '8'   black           200%*Ratio          full
        'A'   black           100%                2/5  (used for PostNet)
        'B'   black           100%*Ratio          2/5  (used for PostNet)
        'C'   black           150%*Ratio          2/5  (used for PostNet)
        'D'   black           200%*Ratio          2/5  (used for PostNet)
}
procedure TBarcode.OneBarProps(code: char; out aWidth: integer; out lt: TBarLineType);
begin
  case code of
    '0':
    begin
      aWidth := modules[0];
      lt := white;
    end;
    '1':
    begin
      aWidth := modules[1];
      lt := white;
    end;
    '2':
    begin
      aWidth := modules[2];
      lt := white;
    end;
    '3':
    begin
      aWidth := modules[3];
      lt := white;
    end;


    '5':
    begin
      aWidth := modules[0];
      lt := black;
    end;
    '6':
    begin
      aWidth := modules[1];
      lt := black;
    end;
    '7':
    begin
      aWidth := modules[2];
      lt := black;
    end;
    '8':
    begin
      aWidth := modules[3];
      lt := black;
    end;

    'A':
    begin
      aWidth := modules[0];
      lt := black_half;
    end;
    'B':
    begin
      aWidth := modules[1];
      lt := black_half;
    end;
    'C':
    begin
      aWidth := modules[2];
      lt := black_half;
    end;
    'D':
    begin
      aWidth := modules[3];
      lt := black_half;
    end;
    else
    begin
      // something went wrong  :-(
      // mistyped pattern table
      raise Exception.CreateFmt('%s: internal Error', [self.ClassName]);
    end;
  end;
end;


function TBarcode.MakeData: string;
begin
  // calculate the with of the different lines (modules)
  MakeModules;

  // get the pattern of the barcode
  case Typ of
    bcCode_2_5_interleaved:
      Result := Code_2_5_interleaved;
    bcCode_2_5_industrial:
      Result := Code_2_5_industrial;
    bcCode_2_5_matrix:
      Result := Code_2_5_matrix;
    bcCode39:
      Result := Code_39;
    bcCode39Extended:
      Result := Code_39Extended;
    bcCode128A,
    bcCode128B,
    bcCode128C:
      Result := Code_128;
    bcCode93:
      Result := Code_93;
    bcCode93Extended:
      Result := Code_93Extended;
    bcCodeMSI:
      Result := Code_MSI;
    bcCodePostNet:
      Result := Code_PostNet;
    bcCodeCodabar:
      Result := Code_Codabar;
    bcCodeEAN8:
      Result := Code_EAN8;
    bcCodeEAN13:
      Result := Code_EAN13;
    else
      raise Exception.CreateFmt('%s: wrong BarcodeType', [self.ClassName]);
  end;

  //Showmessage(Format('Data <%s>', [Result]));
end;


function TBarcode.GetWidth: integer;
var
  Data: string;
  i: integer;
  w: integer;
  lt: TBarLineType;
begin
  Result := 0;

  // get barcode pattern
  Data := MakeData;

  for i := 1 to Length(Data) do  // examine the pattern string
  begin
    OneBarProps(Data[i], w, lt);
    Inc(Result, w);
  end;
end;

procedure TBarcode.SetText(AValue: string);
begin
  if FText=AValue then Exit;
  FText:=AValue;
  FCodeText:=AValue;
end;


////////////////////////////// EAN /////////////////////////////////////////

function getEAN(Nr: string): string;
var
  i, fak, sum: integer;
  tmp: string;
begin
  sum := 0;
  tmp := copy(nr, 1, Length(Nr) - 1);
  fak := Length(tmp);
  for i := 1 to length(tmp) do
  begin
    if (fak mod 2) = 0 then
      sum := sum + (StrToInt(tmp[i]) * 1)
    else
      sum := sum + (StrToInt(tmp[i]) * 3);
    Dec(fak);
  end;
  if (sum mod 10) = 0 then
    Result := tmp + '0'
  else
    Result := tmp + IntToStr(10 - (sum mod 10));
end;

////////////////////////////// EAN8 /////////////////////////////////////////

// Pattern for Barcode EAN Zeichensatz A
//       L1   S1   L2   S2
const
  tabelle_EAN_A: array['0'..'9', 1..4] of char =
    (
    ('2', '6', '0', '5'),    // 0
    ('1', '6', '1', '5'),    // 1
    ('1', '5', '1', '6'),    // 2
    ('0', '8', '0', '5'),    // 3
    ('0', '5', '2', '6'),    // 4
    ('0', '6', '2', '5'),    // 5
    ('0', '5', '0', '8'),    // 6
    ('0', '7', '0', '6'),    // 7
    ('0', '6', '0', '7'),    // 8
    ('2', '5', '0', '6')     // 9
    );

// Pattern for Barcode EAN Zeichensatz C
//       S1   L1   S2   L2
const
  tabelle_EAN_C: array['0'..'9', 1..4] of char =
    (
    ('7', '1', '5', '0'),    // 0
    ('6', '1', '6', '0'),    // 1
    ('6', '0', '6', '1'),    // 2
    ('5', '3', '5', '0'),    // 3
    ('5', '0', '7', '1'),    // 4
    ('5', '1', '7', '0'),    // 5
    ('5', '0', '5', '3'),    // 6
    ('5', '2', '5', '1'),    // 7
    ('5', '1', '5', '2'),    // 8
    ('7', '0', '5', '1')     // 9
    );


function TBarcode.Code_EAN8: string;
var
  i, j: integer;
begin

  FCodeText := CleanEANValue(FText, 8);

  Result := '505';   // Startcode

  for i := 1 to 4 do
    for j := 1 to 4 do
    begin
      Result := Result + tabelle_EAN_A[FCodeText[i], j];
    end;

  Result := Result + '05050';   // Trennzeichen

  for i := 5 to 8 do
    for j := 1 to 4 do
    begin
      Result := Result + tabelle_EAN_C[FCodeText[i], j];
    end;

  Result := Result + '505';   // Stopcode
end;

////////////////////////////// EAN13 ///////////////////////////////////////

// Pattern for Barcode EAN Zeichensatz B
//       L1   S1   L2   S2
const
  tabelle_EAN_B: array['0'..'9', 1..4] of char =
    (
    ('0', '5', '1', '7'),    // 0
    ('0', '6', '1', '6'),    // 1
    ('1', '6', '0', '6'),    // 2
    ('0', '5', '3', '5'),    // 3
    ('1', '7', '0', '5'),    // 4
    ('0', '7', '1', '5'),    // 5
    ('3', '5', '0', '5'),    // 6
    ('1', '5', '2', '5'),    // 7
    ('2', '5', '1', '5'),    // 8
    ('1', '5', '0', '7')     // 9
    );

// Zuordung der Paraitaetsfolgen für EAN13
const
  tabelle_ParityEAN13: array[0..9, 1..6] of char =
    (
    ('A', 'A', 'A', 'A', 'A', 'A'),    // 0
    ('A', 'A', 'B', 'A', 'B', 'B'),    // 1
    ('A', 'A', 'B', 'B', 'A', 'B'),    // 2
    ('A', 'A', 'B', 'B', 'B', 'A'),    // 3
    ('A', 'B', 'A', 'A', 'B', 'B'),    // 4
    ('A', 'B', 'B', 'A', 'A', 'B'),    // 5
    ('A', 'B', 'B', 'B', 'A', 'A'),    // 6
    ('A', 'B', 'A', 'B', 'A', 'B'),    // 7
    ('A', 'B', 'A', 'B', 'B', 'A'),    // 8
    ('A', 'B', 'B', 'A', 'B', 'A')     // 9
    );

function TBarcode.Code_EAN13: string;
var
  i, j, LK: integer;
  tmp: string;
begin

  FCodeText := CleanEanValue(FText, 13);

  LK := StrToInt(FCodeText[1]);
  tmp := copy(FCodeText, 2, 12);

  Result := '505';   // Startcode

  for i := 1 to 6 do
  begin
    case tabelle_ParityEAN13[LK, i] of
      'A':
        for j := 1 to 4 do
          Result := Result + tabelle_EAN_A[tmp[i], j];
      'B':
        for j := 1 to 4 do
          Result := Result + tabelle_EAN_B[tmp[i], j];
      'C':
        for j := 1 to 4 do
          Result := Result + tabelle_EAN_C[tmp[i], j];
    end;
  end;

  Result := Result + '05050';   // Trennzeichen

  for i := 7 to 12 do
    for j := 1 to 4 do
    begin
      Result := Result + tabelle_EAN_C[tmp[i], j];
    end;

  Result := Result + '505';   // Stopcode
end;

// Pattern for Barcode 2 of 5
const
  tabelle_2_5: array['0'..'9', 1..5] of char =
    (
    ('0', '0', '1', '1', '0'),    // 0
    ('1', '0', '0', '0', '1'),    // 1
    ('0', '1', '0', '0', '1'),    // 2
    ('1', '1', '0', '0', '0'),    // 3
    ('0', '0', '1', '0', '1'),    // 4
    ('1', '0', '1', '0', '0'),    // 5
    ('0', '1', '1', '0', '0'),    // 6
    ('0', '0', '0', '1', '1'),    // 7
    ('1', '0', '0', '1', '0'),    // 8
    ('0', '1', '0', '1', '0')     // 9
    );

function TBarcode.Code_2_5_interleaved: string;
var
  i, j: integer;
  c: char;

begin
  Result := '5050';   // Startcode

  for i := 1 to Length(FText) div 2 do
  begin
    for j := 1 to 5 do
    begin
      if tabelle_2_5[FText[i * 2 - 1], j] = '1' then
        c := '6'
      else
        c := '5';
      Result := Result + c;
      if tabelle_2_5[FText[i * 2], j] = '1' then
        c := '1'
      else
        c := '0';
      Result := Result + c;
    end;
  end;

  Result := Result + '605';    // Stopcode
end;


function TBarcode.Code_2_5_industrial: string;
var
  i, j: integer;
begin
  Result := '606050';   // Startcode

  for i := 1 to Length(FText) do
  begin
    for j := 1 to 5 do
    begin
      if tabelle_2_5[FText[i], j] = '1' then
        Result := Result + '60'
      else
        Result := Result + '50';
    end;
  end;

  Result := Result + '605060';   // Stopcode
end;

function TBarcode.Code_2_5_matrix: string;
var
  i, j: integer;
  c: char;
begin
  Result := '705050';   // Startcode

  for i := 1 to Length(FText) do
  begin
    for j := 1 to 5 do
    begin
      if tabelle_2_5[FText[i], j] = '1' then
        c := '1'
      else
        c := '0';

      // Falls i ungerade ist dann mache Lücke zu Strich
      if odd(j) then
        c := chr(Ord(c) + 5);
      Result := Result + c;
    end;
    Result := Result + '0';   // Lücke zwischen den Zeichen
  end;

  Result := Result + '70505';   // Stopcode
end;


function TBarcode.Code_39: string;

type
  TCode39 = record
    c: char;
    Data: array[0..9] of char;
    chk: shortint;
  end;

const
  tabelle_39: array[0..43] of TCode39 = (
    (c: '0'; Data: '505160605'; chk: 0),
    (c: '1'; Data: '605150506'; chk: 1),
    (c: '2'; Data: '506150506'; chk: 2),
    (c: '3'; Data: '606150505'; chk: 3),
    (c: '4'; Data: '505160506'; chk: 4),
    (c: '5'; Data: '605160505'; chk: 5),
    (c: '6'; Data: '506160505'; chk: 6),
    (c: '7'; Data: '505150606'; chk: 7),
    (c: '8'; Data: '605150605'; chk: 8),
    (c: '9'; Data: '506150605'; chk: 9),
    (c: 'A'; Data: '605051506'; chk: 10),
    (c: 'B'; Data: '506051506'; chk: 11),
    (c: 'C'; Data: '606051505'; chk: 12),
    (c: 'D'; Data: '505061506'; chk: 13),
    (c: 'E'; Data: '605061505'; chk: 14),
    (c: 'F'; Data: '506061505'; chk: 15),
    (c: 'G'; Data: '505051606'; chk: 16),
    (c: 'H'; Data: '605051605'; chk: 17),
    (c: 'I'; Data: '506051600'; chk: 18),
    (c: 'J'; Data: '505061605'; chk: 19),
    (c: 'K'; Data: '605050516'; chk: 20),
    (c: 'L'; Data: '506050516'; chk: 21),
    (c: 'M'; Data: '606050515'; chk: 22),
    (c: 'N'; Data: '505060516'; chk: 23),
    (c: 'O'; Data: '605060515'; chk: 24),
    (c: 'P'; Data: '506060515'; chk: 25),
    (c: 'Q'; Data: '505050616'; chk: 26),
    (c: 'R'; Data: '605050615'; chk: 27),
    (c: 'S'; Data: '506050615'; chk: 28),
    (c: 'T'; Data: '505060615'; chk: 29),
    (c: 'U'; Data: '615050506'; chk: 30),
    (c: 'V'; Data: '516050506'; chk: 31),
    (c: 'W'; Data: '616050505'; chk: 32),
    (c: 'X'; Data: '515060506'; chk: 33),
    (c: 'Y'; Data: '615060505'; chk: 34),
    (c: 'Z'; Data: '516060505'; chk: 35),
    (c: '-'; Data: '515050606'; chk: 36),
    (c: '.'; Data: '615050605'; chk: 37),
    (c: ' '; Data: '516050605'; chk: 38),
    (c: '*'; Data: '515060605'; chk: 0),
    (c: '$'; Data: '515151505'; chk: 39),
    (c: '/'; Data: '515150515'; chk: 40),
    (c: '+'; Data: '515051515'; chk: 41),
    (c: '%'; Data: '505151515'; chk: 42)
    );


  function FindIdx(z: char): integer;
  var
    i: integer;
  begin
    Result := -1;
    for i := 0 to High(tabelle_39) do
    begin
      if z = tabelle_39[i].c then
      begin
        Result := i;
        Break;
      end;
    end;
  end;

var
  i, idx: integer;
  vChecksum: integer;

begin
  vChecksum := 0;
  // Startcode
  Result := tabelle_39[FindIdx('*')].Data + '0';

  for i := 1 to Length(FText) do
  begin
    idx := FindIdx(FText[i]);
    if idx < 0 then
      continue;
    Result := Result + tabelle_39[idx].Data + '0';
    Inc(vChecksum, tabelle_39[idx].chk);
  end;

  // Calculate Checksum Data
  if FCheckSum then
  begin
    vChecksum := vChecksum mod 43;
    for i := 0 to High(tabelle_39) do
      if vChecksum = tabelle_39[i].chk then
      begin
        Result := Result + tabelle_39[i].Data + '0';
        exit;
      end;
  end;

  // Stopcode
  Result := Result + tabelle_39[FindIdx('*')].Data;
end;

function TBarcode.Code_39Extended: string;

const
  code39x: array[0..127] of string[2] =
    (
    ('%U'), ('$A'), ('$B'), ('$C'), ('$D'), ('$E'), ('$F'), ('$G'),
    ('$H'), ('$I'), ('$J'), ('$K'), ('$L'), ('$M'), ('$N'), ('$O'),
    ('$P'), ('$Q'), ('$R'), ('$S'), ('$T'), ('$U'), ('$V'), ('$W'),
    ('$X'), ('$Y'), ('$Z'), ('%A'), ('%B'), ('%C'), ('%D'), ('%E'),
    (' '),  ('/A'), ('/B'), ('/C'), ('/D'), ('/E'), ('/F'), ('/G'),
    ('/H'), ('/I'), ('/J'), ('/K'), ('/L'), ('/M'), ('/N'), ('/O'),
    ('0'),  ('1'),  ('2'),  ('3'),  ('4'),  ('5'),  ('6'),  ('7'),
    ('8'),  ('9'),  ('/Z'), ('%F'), ('%G'), ('%H'), ('%I'), ('%J'),
    ('%V'), ('A'),  ('B'),  ('C'),  ('D'),  ('E'),  ('F'),  ('G'),
    ('H'),  ('I'),  ('J'),  ('K'),  ('L'),  ('M'),  ('N'),  ('O'),
    ('P'),  ('Q'),  ('R'),  ('S'),  ('T'),  ('U'),  ('V'),  ('W'),
    ('X'),  ('Y'),  ('Z'),  ('%K'), ('%L'), ('%M'), ('%N'), ('%O'),
    ('%W'), ('+A'), ('+B'), ('+C'), ('+D'), ('+E'), ('+F'), ('+G'),
    ('+H'), ('+I'), ('+J'), ('+K'), ('+L'), ('+M'), ('+N'), ('+O'),
    ('+P'), ('+Q'), ('+R'), ('+S'), ('+T'), ('+U'), ('+V'), ('+W'),
    ('+X'), ('+Y'), ('+Z'), ('%P'), ('%Q'), ('%R'), ('%S'), ('%T')
    );

var
  save: string;
  i: integer;
begin
  save := FText;
  FText := '';

  for i := 1 to Length(save) do
  begin
    if Ord(save[i]) <= 127 then
      FText := FText + code39x[Ord(save[i])];
  end;
  Result := Code_39;
  FText := save;
end;


{
Code 128
}
function TBarcode.Code_128: string;
type
  TCode128 = record
    a, b: char;
    c: string[2];
    Data: string[6];
  end;

const
  tabelle_128: array[0..102] of TCode128 = (
    (a: ' '; b: ' '; c: '00'; Data: '212222'; ),
    (a: '!'; b: '!'; c: '01'; Data: '222122'; ),
    (a: '"'; b: '"'; c: '02'; Data: '222221'; ),
    (a: '#'; b: '#'; c: '03'; Data: '121223'; ),
    (a: '$'; b: '$'; c: '04'; Data: '121322'; ),
    (a: '%'; b: '%'; c: '05'; Data: '131222'; ),
    (a: '&'; b: '&'; c: '06'; Data: '122213'; ),
    (a: ''''; b: ''''; c: '07'; Data: '122312'; ),
    (a: '('; b: '('; c: '08'; Data: '132212'; ),
    (a: ')'; b: ')'; c: '09'; Data: '221213'; ),
    (a: '*'; b: '*'; c: '10'; Data: '221312'; ),
    (a: '+'; b: '+'; c: '11'; Data: '231212'; ),
    (a: ','; b: ','; c: '12'; Data: '112232'; ),
    (a: '-'; b: '-'; c: '13'; Data: '122132'; ),
    (a: '.'; b: '.'; c: '14'; Data: '122231'; ),
    (a: '/'; b: '/'; c: '15'; Data: '113222'; ),
    (a: '0'; b: '0'; c: '16'; Data: '123122'; ),
    (a: '1'; b: '1'; c: '17'; Data: '123221'; ),
    (a: '2'; b: '2'; c: '18'; Data: '223211'; ),
    (a: '3'; b: '3'; c: '19'; Data: '221132'; ),
    (a: '4'; b: '4'; c: '20'; Data: '221231'; ),
    (a: '5'; b: '5'; c: '21'; Data: '213212'; ),
    (a: '6'; b: '6'; c: '22'; Data: '223112'; ),
    (a: '7'; b: '7'; c: '23'; Data: '312131'; ),
    (a: '8'; b: '8'; c: '24'; Data: '311222'; ),
    (a: '9'; b: '9'; c: '25'; Data: '321122'; ),
    (a: ':'; b: ':'; c: '26'; Data: '321221'; ),
    (a: ';'; b: ';'; c: '27'; Data: '312212'; ),
    (a: '<'; b: '<'; c: '28'; Data: '322112'; ),
    (a: '='; b: '='; c: '29'; Data: '322211'; ),
    (a: '>'; b: '>'; c: '30'; Data: '212123'; ),
    (a: '?'; b: '?'; c: '31'; Data: '212321'; ),
    (a: '@'; b: '@'; c: '32'; Data: '232121'; ),
    (a: 'A'; b: 'A'; c: '33'; Data: '111323'; ),
    (a: 'B'; b: 'B'; c: '34'; Data: '131123'; ),
    (a: 'C'; b: 'C'; c: '35'; Data: '131321'; ),
    (a: 'D'; b: 'D'; c: '36'; Data: '112313'; ),
    (a: 'E'; b: 'E'; c: '37'; Data: '132113'; ),
    (a: 'F'; b: 'F'; c: '38'; Data: '132311'; ),
    (a: 'G'; b: 'G'; c: '39'; Data: '211313'; ),
    (a: 'H'; b: 'H'; c: '40'; Data: '231113'; ),
    (a: 'I'; b: 'I'; c: '41'; Data: '231311'; ),
    (a: 'J'; b: 'J'; c: '42'; Data: '112133'; ),
    (a: 'K'; b: 'K'; c: '43'; Data: '112331'; ),
    (a: 'L'; b: 'L'; c: '44'; Data: '132131'; ),
    (a: 'M'; b: 'M'; c: '45'; Data: '113123'; ),
    (a: 'N'; b: 'N'; c: '46'; Data: '113321'; ),
    (a: 'O'; b: 'O'; c: '47'; Data: '133121'; ),
    (a: 'P'; b: 'P'; c: '48'; Data: '313121'; ),
    (a: 'Q'; b: 'Q'; c: '49'; Data: '211331'; ),
    (a: 'R'; b: 'R'; c: '50'; Data: '231131'; ),
    (a: 'S'; b: 'S'; c: '51'; Data: '213113'; ),
    (a: 'T'; b: 'T'; c: '52'; Data: '213311'; ),
    (a: 'U'; b: 'U'; c: '53'; Data: '213131'; ),
    (a: 'V'; b: 'V'; c: '54'; Data: '311123'; ),
    (a: 'W'; b: 'W'; c: '55'; Data: '311321'; ),
    (a: 'X'; b: 'X'; c: '56'; Data: '331121'; ),
    (a: 'Y'; b: 'Y'; c: '57'; Data: '312113'; ),
    (a: 'Z'; b: 'Z'; c: '58'; Data: '312311'; ),
    (a: '['; b: '['; c: '59'; Data: '332111'; ),
    (a: '\'; b: '\'; c: '60'; Data: '314111'; ),
    (a: ']'; b: ']'; c: '61'; Data: '221411'; ),
    (a: '^'; b: '^'; c: '62'; Data: '431111'; ),
    (a: '_'; b: '_'; c: '63'; Data: '111224'; ),
    (a: ' '; b: '`'; c: '64'; Data: '111422'; ),
    (a: ' '; b: 'a'; c: '65'; Data: '121124'; ),
    (a: ' '; b: 'b'; c: '66'; Data: '121421'; ),
    (a: ' '; b: 'c'; c: '67'; Data: '141122'; ),
    (a: ' '; b: 'd'; c: '68'; Data: '141221'; ),
    (a: ' '; b: 'e'; c: '69'; Data: '112214'; ),
    (a: ' '; b: 'f'; c: '70'; Data: '112412'; ),
    (a: ' '; b: 'g'; c: '71'; Data: '122114'; ),
    (a: ' '; b: 'h'; c: '72'; Data: '122411'; ),
    (a: ' '; b: 'i'; c: '73'; Data: '142112'; ),
    (a: ' '; b: 'j'; c: '74'; Data: '142211'; ),
    (a: ' '; b: 'k'; c: '75'; Data: '241211'; ),
    (a: ' '; b: 'l'; c: '76'; Data: '221114'; ),
    (a: ' '; b: 'm'; c: '77'; Data: '413111'; ),
    (a: ' '; b: 'n'; c: '78'; Data: '241112'; ),
    (a: ' '; b: 'o'; c: '79'; Data: '134111'; ),
    (a: ' '; b: 'p'; c: '80'; Data: '111242'; ),
    (a: ' '; b: 'q'; c: '81'; Data: '121142'; ),
    (a: ' '; b: 'r'; c: '82'; Data: '121241'; ),
    (a: ' '; b: 's'; c: '83'; Data: '114212'; ),
    (a: ' '; b: 't'; c: '84'; Data: '124112'; ),
    (a: ' '; b: 'u'; c: '85'; Data: '124211'; ),
    (a: ' '; b: 'v'; c: '86'; Data: '411212'; ),
    (a: ' '; b: 'w'; c: '87'; Data: '421112'; ),
    (a: ' '; b: 'x'; c: '88'; Data: '421211'; ),
    (a: ' '; b: 'y'; c: '89'; Data: '212141'; ),
    (a: ' '; b: 'z'; c: '90'; Data: '214121'; ),
    (a: ' '; b: '{'; c: '91'; Data: '412121'; ),
    (a: ' '; b: '|'; c: '92'; Data: '111143'; ),
    (a: ' '; b: '}'; c: '93'; Data: '111341'; ),
    (a: ' '; b: '~'; c: '94'; Data: '131141'; ),
    (a: ' '; b: ' '; c: '95'; Data: '114113'; ),
    (a: ' '; b: ' '; c: '96'; Data: '114311'; ),
    (a: ' '; b: ' '; c: '97'; Data: '411113'; ),
    (a: ' '; b: ' '; c: '98'; Data: '411311'; ),
    (a: ' '; b: ' '; c: '99'; Data: '113141'; ),
    (a: ' '; b: ' '; c: '  '; Data: '114131'; ),
    (a: ' '; b: ' '; c: '  '; Data: '311141'; ),
    (a: ' '; b: ' '; c: '  '; Data: '411131'; )
    );

  StartA = '211412';
  StartB = '211214';
  StartC = '211232';
  Stop = '2331112';


  // find Code 128 Codeset A or B
  function Find_Code128AB(c: char): integer;
  var
    i: integer;
    v: char;
  begin
    for i := 0 to High(tabelle_128) do
    begin
      if FTyp = bcCode128A then
        v := tabelle_128[i].a
      else
        v := tabelle_128[i].b;

      if c = v then
      begin
        Result := i;
        exit;
      end;
    end;
    Result := -1;
  end;

var
  i, idx: integer;
  startcode, tmp: string;
  vChecksum: integer;

begin

  vChecksum := 0; // Added by TZ
  case FTyp of
    bcCode128A:
    begin
      vChecksum := 103;
      startcode := StartA;
      FCodeText := FText;
    end;
    bcCode128B:
    begin
      vChecksum := 104;
      startcode := StartB;
      FCodeText := FText;
    end;
    bcCode128C:
    begin
      vChecksum := 105;
      startcode := StartC;

      // make sure we have an even numeric only string
      FCodeText := '';
      for i := 1 to Length(FText) do
        if not (FText[i] in ['0'..'9']) then
          FCodeText := FCodeText + '0'
        else
          FCodeText := FCodeText + FText[i];

      if Odd(Length(FText)) then
        FCodeText := '0' + FText;
    end;
  end;

  Result := Convert(startcode);    // Startcode

  if FTyp = bcCode128C then
  begin
    tmp := '';
    i := 1;
    while i<Length(FCodeText) do
    begin
      tmp := tmp + chr( StrToIntDef(Copy(FCodeText, i, 2), 0) );
      inc(i,2);
    end;
  end else
    tmp := FCodeText;

  for i := 1 to Length(tmp) do
  begin
    if FTyp = bcCode128C then
      idx := Ord(tmp[i])
    else begin
      idx := Find_Code128AB(tmp[i]);
      if idx < 0 then
        idx := Find_Code128AB(' ');
    end;
    Result := Result + Convert(tabelle_128[idx].Data);
    Inc(vChecksum, idx * i);
  end;

  vChecksum := vChecksum mod 103;
  Result := Result + Convert(tabelle_128[vChecksum].Data);

  Result := Result + Convert(Stop);      // Stopcode
end;


function TBarcode.Code_93: string;
type
  TCode93 = record
    c: char;
    Data: array[0..5] of char;
  end;

const
  tabelle_93: array[0..46] of TCode93 = (
    (c: '0'; Data: '131112'),
    (c: '1'; Data: '111213'),
    (c: '2'; Data: '111312'),
    (c: '3'; Data: '111411'),
    (c: '4'; Data: '121113'),
    (c: '5'; Data: '121212'),
    (c: '6'; Data: '121311'),
    (c: '7'; Data: '111114'),
    (c: '8'; Data: '131211'),
    (c: '9'; Data: '141111'),
    (c: 'A'; Data: '211113'),
    (c: 'B'; Data: '211212'),
    (c: 'C'; Data: '211311'),
    (c: 'D'; Data: '221112'),
    (c: 'E'; Data: '221211'),
    (c: 'F'; Data: '231111'),
    (c: 'G'; Data: '112113'),
    (c: 'H'; Data: '112212'),
    (c: 'I'; Data: '112311'),
    (c: 'J'; Data: '122112'),
    (c: 'K'; Data: '132111'),
    (c: 'L'; Data: '111123'),
    (c: 'M'; Data: '111222'),
    (c: 'N'; Data: '111321'),
    (c: 'O'; Data: '121122'),
    (c: 'P'; Data: '131121'),
    (c: 'Q'; Data: '212112'),
    (c: 'R'; Data: '212211'),
    (c: 'S'; Data: '211122'),
    (c: 'T'; Data: '211221'),
    (c: 'U'; Data: '221121'),
    (c: 'V'; Data: '222111'),
    (c: 'W'; Data: '112122'),
    (c: 'X'; Data: '112221'),
    (c: 'Y'; Data: '122121'),
    (c: 'Z'; Data: '123111'),
    (c: '-'; Data: '121131'),
    (c: '.'; Data: '311112'),
    (c: ' '; Data: '311211'),
    (c: '$'; Data: '321111'),
    (c: '/'; Data: '112131'),
    (c: '+'; Data: '113121'),
    (c: '%'; Data: '211131'),
    (c: '['; Data: '121221'),   // only used for Extended Code 93
    (c: ']'; Data: '312111'),   // only used for Extended Code 93
    (c: '{'; Data: '311121'),   // only used for Extended Code 93
    (c: '}'; Data: '122211')    // only used for Extended Code 93
    );


  // find Code 93
  function Find_Code93(c: char): integer;
  var
    i: integer;
  begin
    for i := 0 to High(tabelle_93) do
    begin
      if c = tabelle_93[i].c then
      begin
        Result := i;
        exit;
      end;
    end;
    Result := -1;
  end;

var
  i, idx: integer;
  checkC, checkK,   // Checksums
  weightC, weightK: integer;
begin
  Result := Convert('111141');   // Startcode

  for i := 1 to Length(FText) do
  begin
    idx := Find_Code93(FText[i]);
    if idx < 0 then
      raise Exception.CreateFmt('%s:Code93 bad Data <%s>', [self.ClassName, FText]);
    Result := Result + Convert(tabelle_93[idx].Data);
  end;

  checkC := 0;
  checkK := 0;

  weightC := 1;
  weightK := 2;

  for i := Length(FText) downto 1 do
  begin
    idx := Find_Code93(FText[i]);

    Inc(checkC, idx * weightC);
    Inc(checkK, idx * weightK);

    Inc(weightC);
    if weightC > 20 then
      weightC := 1;
    Inc(weightK);
    if weightK > 15 then
      weightK := 1;
  end;

  Inc(checkK, checkC);

  checkC := checkC mod 47;
  checkK := checkK mod 47;

  Result := Result + Convert(tabelle_93[checkC].Data) +
    Convert(tabelle_93[checkK].Data);

  Result := Result + Convert('1111411');   // Stopcode
end;


function TBarcode.Code_93Extended: string;
const
  code93x: array[0..127] of string[2] =
    (
    (']U'), ('[A'), ('[B'), ('[C'), ('[D'), ('[E'), ('[F'), ('[G'),
    ('[H'), ('[I'), ('[J'), ('[K'), ('[L'), ('[M'), ('[N'), ('[O'),
    ('[P'), ('[Q'), ('[R'), ('[S'), ('[T'), ('[U'), ('[V'), ('[W'),
    ('[X'), ('[Y'), ('[Z'), (']A'), (']B'), (']C'), (']D'), (']E'),
    (' '),  ('{A'), ('{B'), ('{C'), ('{D'), ('{E'), ('{F'), ('{G'),
    ('{H'), ('{I'), ('{J'), ('{K'), ('{L'), ('{M'), ('{N'), ('{O'),
    ('0'),  ('1'),  ('2'),  ('3'),  ('4'),  ('5'),  ('6'),  ('7'),
    ('8'),  ('9'),  ('{Z'), (']F'), (']G'), (']H'), (']I'), (']J'),
    (']V'), ('A'),  ('B'),  ('C'),  ('D'),  ('E'),  ('F'),  ('G'),
    ('H'),  ('I'),  ('J'),  ('K'),  ('L'),  ('M'),  ('N'),  ('O'),
    ('P'),  ('Q'),  ('R'),  ('S'),  ('T'),  ('U'),  ('V'),  ('W'),
    ('X'),  ('Y'),  ('Z'),  (']K'), (']L'), (']M'), (']N'), (']O'),
    (']W'), ('}A'), ('}B'), ('}C'), ('}D'), ('}E'), ('}F'), ('}G'),
    ('}H'), ('}I'), ('}J'), ('}K'), ('}L'), ('}M'), ('}N'), ('}O'),
    ('}P'), ('}Q'), ('}R'), ('}S'), ('}T'), ('}U'), ('}V'), ('}W'),
    ('}X'), ('}Y'), ('}Z'), (']P'), (']Q'), (']R'), (']S'), (']T')
    );

var
  //      save:array[0..254] of char;
  //      old:string;
  save: string;
  i: integer;
begin
  //      CharToOem(PChar(FText), save);
  save := FText;
  FText := '';


  for i := 1 to Length(save) do
  begin
    if Ord(save[i]) <= 127 then
      FText := FText + code93x[Ord(save[i])];
  end;

  //Showmessage(Format('Text: <%s>', [FText]));

  Result := Code_93;
  FText := save;
end;


function TBarcode.Code_MSI: string;
const
  tabelle_MSI: array['0'..'9'] of string[8] =
    (
    ('51515151'),    // '0'
    ('51515160'),    // '1'
    ('51516051'),    // '2'
    ('51516060'),    // '3'
    ('51605151'),    // '4'
    ('51605160'),    // '5'
    ('51606051'),    // '6'
    ('51606060'),    // '7'
    ('60515151'),    // '8'
    ('60515160')     // '9'
    );

var
  i: integer;
  check_even, check_odd, vChecksum: integer;
begin
  Result := '60';    // Startcode
  check_even := 0;
  check_odd := 0;

  for i := 1 to Length(FText) do
  begin
    if odd(i - 1) then
      check_odd := check_odd * 10 + Ord(FText[i])
    else
      check_even := check_even + Ord(FText[i]);

    Result := Result + tabelle_MSI[FText[i]];
  end;

  vChecksum := quersumme(check_odd * 2) + check_even;

  vChecksum := vChecksum mod 10;
  if vChecksum > 0 then
    vChecksum := 10 - vChecksum;

  Result := Result + tabelle_MSI[chr(Ord('0') + vChecksum)];

  Result := Result + '515'; // Stopcode
end;


function TBarcode.Code_PostNet: string;
const
  tabelle_PostNet: array['0'..'9'] of string[10] =
    (
    ('5151A1A1A1'),    // '0'
    ('A1A1A15151'),    // '1'
    ('A1A151A151'),    // '2'
    ('A1A15151A1'),    // '3'
    ('A151A1A151'),    // '4'
    ('A151A151A1'),    // '5'
    ('A15151A1A1'),    // '6'
    ('51A1A1A151'),    // '7'
    ('51A1A151A1'),    // '8'
    ('51A151A1A1')     // '9'
    );
var
  i: integer;
begin
  Result := '51';

  for i := 1 to Length(FText) do
  begin
    Result := Result + tabelle_PostNet[FText[i]];
  end;
  Result := Result + '5';
end;


function TBarcode.Code_Codabar: string;
type
  TCodabar = record
    c: char;
    Data: array[0..6] of char;
  end;

const
  tabelle_cb: array[0..19] of TCodabar = (
    (c: '1'; Data: '5050615'),
    (c: '2'; Data: '5051506'),
    (c: '3'; Data: '6150505'),
    (c: '4'; Data: '5060515'),
    (c: '5'; Data: '6050515'),
    (c: '6'; Data: '5150506'),
    (c: '7'; Data: '5150605'),
    (c: '8'; Data: '5160505'),
    (c: '9'; Data: '6051505'),
    (c: '0'; Data: '5050516'),
    (c: '-'; Data: '5051605'),
    (c: '$'; Data: '5061505'),
    (c: ':'; Data: '6050606'),
    (c: '/'; Data: '6060506'),
    (c: '.'; Data: '6060605'),
    (c: '+'; Data: '5060606'),
    (c: 'A'; Data: '5061515'),
    (c: 'B'; Data: '5151506'),
    (c: 'C'; Data: '5051516'),
    (c: 'D'; Data: '5051615')
    );


  // find Codabar
  function Find_Codabar(c: char): integer;
  var
    i: integer;
  begin
    for i := 0 to High(tabelle_cb) do
    begin
      if c = tabelle_cb[i].c then
      begin
        Result := i;
        exit;
      end;
    end;
    Result := -1;
  end;

var
  i, idx: integer;
begin
  Result := tabelle_cb[Find_Codabar('A')].Data + '0';
  for i := 1 to Length(FText) do
  begin
    idx := Find_Codabar(FText[i]);
    Result := Result + tabelle_cb[idx].Data + '0';
  end;
  Result := Result + tabelle_cb[Find_Codabar('B')].Data;
end;

procedure TBarcode.MakeModules;
begin
  case Typ of
    bcCode_2_5_interleaved,
    bcCode_2_5_industrial,
    bcCode39,
    bcCodeEAN8,
    bcCodeEAN13,
    bcCode39Extended,
    bcCodeCodabar:
    begin
      if Ratio < 2.0 then
        Ratio := 2.0;
      if Ratio > 3.0 then
        Ratio := 3.0;
    end;

    bcCode_2_5_matrix:
    begin
      if Ratio < 2.25 then
        Ratio := 2.25;
      if Ratio > 3.0 then
        Ratio := 3.0;
    end;
    bcCode128A,
    bcCode128B,
    bcCode128C,
    bcCode93,
    bcCode93Extended,
    bcCodeMSI,
    bcCodePostNet: ;
  end;

  modules[0] := FModul;
  modules[1] := Round(FModul * FRatio);
  modules[2] := modules[1] * 3 div 2;
  modules[3] := modules[1] * 2;
end;


{
Draw the Barcode

Parameter :
'data' holds the pattern for a Barcode.
A barcode begins always with a black line and
ends with a black line.

The white Lines builds the space between the black Lines.

A black line must always followed by a white Line and vica versa.

Examples:
        '50505'   // 3 thin black Lines with 2 thin white Lines
        '606'     // 2 fat black Lines with 1 thin white Line

        '5605015' // Error


data[] : see procedure OneBarProps

}
procedure TBarcode.DoLines(Data: string; Canvas: TCanvas);

var
  i: integer;
  lt: TBarLineType;
  xadd: integer;
  w, h: integer;
  a, b, c, d,     // Edges of a line (we need 4 Point because the line
  // is a recangle
  orgin: TPoint;
  alpha: double;

begin
  xadd := 0;
  orgin.x := FLeft;
  orgin.y := FTop;
  alpha := FAngle * pi / 180.0;

  with Canvas do
  begin
    Pen.Width := 1;

    for i := 1 to Length(Data) do  // examine the pattern string
    begin
      OneBarProps(Data[i], w, lt);

      {
      case data[i] of
        '0': begin w := modules[0]; lt := white; end;
        '1': begin w := modules[1]; lt := white; end;
        '2': begin w := modules[2]; lt := white; end;
        '3': begin w := modules[3]; lt := white; end;


        '5': begin w := modules[0]; lt := black; end;
        '6': begin w := modules[1]; lt := black; end;
        '7': begin w := modules[2]; lt := black; end;
        '8': begin w := modules[3]; lt := black; end;

        'A': begin w := modules[0]; lt := black_half; end;
        'B': begin w := modules[1]; lt := black_half; end;
        'C': begin w := modules[2]; lt := black_half; end;
        'D': begin w := modules[3]; lt := black_half; end;
      else
        begin
          // something went wrong
          // mistyped pattern table
          raise Exception.CreateFmt('%s: internal Error', [self.ClassName]);
        end;
      end;
      }

      if (lt = black) or (lt = black_half) then
      begin
        Pen.Color := clBlack;
      end
      else
      begin
        Pen.Color := clWhite;
      end;
      Brush.Color := Pen.Color;

      if lt = black_half then
        H := FHeight * 2 div 5
      else
        H := FHeight;


      a.x := xadd;
      a.y := 0;

      b.x := xadd;
      b.y := H;

      // c.x := xadd+width;
      c.x := xadd + W - 1;  // 23.04.1999 Line was 1 Pixel too wide
      c.y := H;

      // d.x := xadd+width;
      d.x := xadd + W - 1;  // 23.04.1999 Line was 1 Pixel too wide
      d.y := 0;

      // a,b,c,d builds the rectangle we want to draw


      // rotate the rectangle
      a := Translate2D(Rotate2D(a, alpha), orgin);
      b := Translate2D(Rotate2D(b, alpha), orgin);
      c := Translate2D(Rotate2D(c, alpha), orgin);
      d := Translate2D(Rotate2D(d, alpha), orgin);

      // draw the rectangle
      Polygon([a, b, c, d]);

      xadd := xadd + w;
    end;
  end;
end;


procedure TBarcode.DrawBarcode(Canvas: TCanvas);
var
  Data: string;
  SaveFont: TFont;
  SavePen: TPen;
  SaveBrush: TBrush;
begin
  Savefont := TFont.Create;
  SavePen := TPen.Create;
  SaveBrush := TBrush.Create;

  // get barcode pattern
  Data := MakeData;

  try
    // store Canvas properties
    Savefont.Assign(Canvas.Font);
    SavePen.Assign(Canvas.Pen);
    SaveBrush.Assign(Canvas.Brush);

    DoLines(Data, Canvas);    // draw the barcode

    if FShowText then
      DrawText(Canvas);   // show readable Text

    // restore old Canvas properties
    Canvas.Font.Assign(savefont);
    Canvas.Pen.Assign(SavePen);
    Canvas.Brush.Assign(SaveBrush);
  finally
    Savefont.Free;
    SavePen.Free;
    SaveBrush.Free;
  end;
end;


{
  draw contents and type/name of barcode
  as human readable text at the left
  upper edge of the barcode.

  main use for this procedure is testing.

  note: this procedure changes Pen and Brush
  of the current canvas.
}
procedure TBarcode.DrawText(Canvas: TCanvas);
begin
  with Canvas do
  begin
    Font.Size := 4;
    // the fixed font size is a problem, if you
    // use very large or small barcodes

    Pen.Color := clBlack;
    Brush.Color := clWhite;
    TextOut(FLeft, FTop, FText);         // contents of Barcode
    TextOut(FLeft, FTop + 14, GetTypText); // type/name of barcode
  end;
end;

// this function returns true for those symbols that correct them selves
// in case invalid data is fed. For example feeding ABCD to 128C numeric
// only symbol, the generated barcode will be for 0000
function TBarcode.BarcodeTypeChecked(AType: TBarcodeType): boolean;
begin
  result := aType in [ bcCode128A, bcCode128B, bcCode128C, bcCodeEAN8,
                       bcCodeEAN13 ];
end;

function TBarcode.CleanEANValue(const AValue:string; const ASize: Byte): string;
var
  tmp: string;
  n,i: Integer;
begin
  tmp := AValue;
  n := Length(tmp);

  // check if there is any strange char in string
  for i:=1 to n do
    if not (tmp[i] in ['0'..'9']) then
      tmp[i] := '0';

  // enforce a ASize char string by adding a 0
  // verifier digit if necesary or calc it if
  // checksum was specified
  if n<ASize then begin
    tmp := stringofchar('0', ASize-n-1) + tmp + '0';
    // TODO: if not FCheckSum was specified
    //       resulting barcode might be invalid
    //       as a '0' checksum digit was forced.
  end;

  if FCheckSum then
    Result := getEAN(copy(tmp, 1, ASize-1) + '0')
  else
    Result := copy(tmp, 1, ASize);

end;



end.

