// Upgraded to Delphi 2009: Sebastian Zierer

(* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is TurboPower SysTools
 *
 * The Initial Developer of the Original Code is
 * TurboPower Software
 *
 * Portions created by the Initial Developer are Copyright (C) 1996-2002
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** *)

{*********************************************************}
{* SysTools: St2DBarC.pas 4.04                           *}
{*********************************************************}
{* SysTools: Two-Dimensional Barcodes                    *}
{*********************************************************}

{$IFDEF FPC}
  {$mode DELPHI}
{$ENDIF}

//{$I StDefine.inc}

unit St2DBarC;

interface

uses
 {$IFDEF FPC}
  LCLIntf, LCLType, LMessages,
 {$ELSE}
  Windows, Messages,
 {$ENDIF}
  SysUtils,
  Classes,
  Controls,
  Graphics,
  StdCtrls,
  Math,
  ClipBrd,
  StConst;

resourcestring

  StEBadBarHeight        = 'Bar Height cannot be less than one';
  StEBadBarHeightToWidth = 'BarHeightToWidth cannot be less than one';
  StEBadBarWidth         = 'Bar Width cannot be less than one';
  StEBadCountryCode      = 'Invalid Country Code';
  StEBadNumCols          = 'Invalid Number of columns';
  StEBadNumRows          = 'Invalid number of rows';
  StEBadPostalCode       = 'Invalid Postal Code';
  StEBadServiceClass     = 'Invalid Service Class';
  StEBadQuietZone        = 'Invalid Quiet Zone';
  StECodeTooLarge        = 'Code too large for barcode';
  StEGLIOutOfRange       = 'GLI value out of range';
  StEInvalidCodeword     = 'Invalid Codeword';
  StENeedBarHeight       = 'Either BarHeight or BarHeightToWidth is required';
  StENeedHorz            = 'Horizontal size needs to be specified';
  StENeedVert            = 'Vertical size needs to be specified';

type
  { Generic 2D barcode types and constants }

  TStDataMode = (dmBinary, dmText, dmNumeric);

  { PDF417 types and constants }

  TStPDF417CodewordList = array [0..2700] of Word;
  TStPDF417ECCLevels = (ecAuto, ecLevel0, ecLevel1, ecLevel2, ecLevel3,
                        ecLevel4, ecLevel5, ecLevel6, ecLevel7, ecLevel8);

  { MaxiCode types and constants }

  TStMaxiCodeMode = (cmMode2, cmMode3, cmMode4, cmMode5, cmMode6);

const
  StMaxiCodeGaloisField    = 64;  { Galois field to work in }
  StMaxiCodeECCPoly        = 67;  { Primary polynomial - }
  StMaxMaxiCodeECCDataSize = 144; { Max amount of data }

type
  TStMaxiCodeECCData = array [0..StMaxMaxiCodeECCDataSize] of Byte;
  TStMaxiCodeECCPoly = (epPrimary, epStandard, epEnhanced);
  TStMaxiCodeECCInterleave = (imNone, imEven, imOdd);

  { E2DBarcodeError }

  E2DBarcodeError = class (Exception);

  { TStCustom2DBarcode }

  TStCustom2DBarcode = class (TGraphicControl)
    protected { private }
      FCode              : string;
      FBarWidth          : Integer;
      FBackgroundColor   : TColor;
      FCaption           : string;
      FECCLevel          : Integer;
      FExtendedSyntax    : Boolean;
      FRelativeBarHeight : Boolean;
      FBarHeightToWidth  : Integer;
      FBarHeight         : Integer;

      FQuietZone         : Integer;
      FAlignment         : TAlignment;
      FCaptionLayout     : TTextLayout;
      FBarCodeRect       : TRect;
      FUsedCodewords     : Integer;
      FFreeCodewords     : Integer;
      FUsedECCCodewords  : Integer;
      FTotalCodewords    : Integer;

    { protected }
      FBitmap            : TBitmap;

      function  CalculateBarCodeWidth (PaintableWidth : Integer) : Integer;
                virtual; abstract;
      function  CalculateBarCodeHeight (PaintableHeight : Integer) : Integer;
                virtual; abstract;
      procedure DrawBarcode; virtual; abstract;
      procedure GenerateBarcodeBitmap (BCWidth  : Integer;
                                       BCHeight : Integer);
      procedure GenerateCodewords; virtual; abstract;
      function  GetBarCodeHeight : Integer;
      function  GetBarCodeWidth : Integer;
      procedure GetCurrentResolution (var ResX : Integer; var ResY : Integer);
      function GetVersion : string;
      procedure Paint; override;
      procedure SetAlignment (const v : TAlignment);
      procedure SetBackgroundColor (const v : TColor);
      procedure SetBarHeight (const v : Integer); virtual;
      procedure SetBarHeightToWidth (const v : Integer); virtual;
      procedure SetBarWidth (const v : Integer); virtual;
      procedure SetBitmap (const v : TBitmap);
      procedure SetCaption (const v : string);
      procedure SetCaptionLayout (const v : TTextLayout);
      procedure SetCode (const v : string);
      procedure SetECCLevel (const v : Integer);
      procedure SetExtendedSyntax (const v : Boolean);
      procedure SetRelativeBarHeight (const v : Boolean); virtual;
      procedure SetQuietZone (const v : Integer);
      procedure SetVersion(const Value : string);

    public
      constructor Create (AOwner : TComponent); override;
      destructor Destroy; override;

      procedure CopyToClipboard;
      procedure CopyToClipboardRes (ResX : Integer; ResY : Integer);
      procedure PaintToCanvas (ACanvas : TCanvas; Position : TPoint);
      procedure PaintToCanvasRes (ACanvas : TCanvas; Position : TPoint;
                                  ResX : Integer; ResY : Integer);
      procedure PaintToCanvasSize (ACanvas : TCanvas; X, Y, H : Double);
      procedure PaintToDC (DC : hDC; Position : TPoint);
      procedure PaintToDCRes (DC : hDC; Position : TPoint;
                              ResX : Integer; ResY : Integer);
      procedure PaintToPrinterCanvas (ACanvas : TCanvas; Position : TPoint);
      procedure PaintToPrinterCanvasRes (ACanvas : TCanvas; Position : TPoint;
                                         ResX : Integer; ResY : Integer);
      procedure PaintToPrinterCanvasSize (ACanvas : TCanvas; X, Y, H : Double);
      procedure PaintToPrinterDC (DC : hDC; Position : TPoint);
      procedure PaintToPrinterDCRes (DC : hDC; Position : TPoint;
                                     ResX : Integer; ResY : Integer);                                     
      procedure RenderToResolution (var OutBitmap : TBitmap;
                                    ResX : Integer;
                                    ResY : Integer;
                                    var SizeX : Integer;
                                    var SizeY : Integer); virtual; abstract;
      procedure SaveToFile (const FileName : string);
      procedure SaveToFileRes (const FileName : string;
                               ResX : Integer; ResY : Integer);

      property Alignment : TAlignment read FAlignment write SetAlignment
               default taCenter;
      property BackgroundColor : TColor
               read FBackgroundColor write SetBackgroundColor default clWhite;
      property BarCodeHeight : Integer read GetBarCodeHeight;
      property BarCodeRect : TRect read FBarCodeRect;
      property BarCodeWidth : Integer read GetBarCodeWidth;
      property BarHeight : Integer read FBarHeight write SetBarHeight
               default 2;
      property BarHeightToWidth : Integer
               read FBarHeightToWidth write SetBarHeightToWidth default 4;
      property BarWidth : Integer read FBarWidth write SetBarWidth default 2;
      property Bitmap : TBitmap read FBitmap write SetBitmap stored False;
      property Caption : string read FCaption write SetCaption;
      property CaptionLayout : TTextLayout
               read FCaptionLayout write SetCaptionLayout
               default tlBottom;
      property Code : string read FCode write SetCode;
      property ECCLevel : Integer read FECCLevel write SetECCLevel default 0;
      property ExtendedSyntax : Boolean
               read FExtendedSyntax write SetExtendedSyntax default True;
      property FreeCodewords : Integer read FFreeCodewords;
      property RelativeBarHeight : Boolean
               read FRelativeBarHeight write SetRelativeBarHeight
               default False;
      property QuietZone : Integer read FQuietZone write SetQuietZone
               default 8;
      property TotalCodewords : Integer read FTotalCodewords;
      property UsedCodewords : Integer read FUsedCodewords;
      property UsedECCCodewords : Integer read FUsedECCCodewords;

      property Color default clBlack;

    published
      property Version : string read GetVersion write SetVersion stored False;
    
      { Properties }
      property Align;
      property Cursor;
      property Enabled;
      property Font;
      property ParentColor;
      property ParentFont;
      property ParentShowHint;
      property ShowHint;
      property Visible;

      { Events }
      property OnClick;
      property OnDblClick;
      property OnMouseDown;
      property OnMouseMove;
      property OnMouseUp;
  end;

  { TStPDF417Barcode }

  TStPDF417Barcode = class (TStCustom2DBarcode)
    private
      FTruncated       : Boolean;
      FCodewords       : TStPDF417CodewordList;
      FNumCodewords    : Integer;
      FNewTextCodeword : Boolean;
      FHighlight       : Boolean;
      FNumRows         : Integer;
      FNumColumns      : Integer;

    protected
      procedure AddCodeword (Value : Word);
      function  CalculateBarCodeWidth (PaintableWidth : Integer) : Integer;
                override;
      function  CalculateBarCodeHeight (PaintableHeight : Integer) : Integer;
                override;
      procedure CalculateECC (NumCodewords : Integer; ECCLen : Integer);
      procedure CalculateSize (var XSize : Integer;
                               var YSize : Integer);
      function  CodewordToBitmask (RowNumber : Integer;
                                   Codeword : Integer) : DWord;
      procedure ConvertBytesToBase900 (const S : array of byte;
                                       var A : array of integer);
      procedure ConvertToBase900 (const S : string;
                                  var A : array of integer;
                                  var LenA : integer);
      procedure DrawBarcode; override;
      procedure DrawCodeword (RowNumber : Integer;
                              ColNumber : Integer;
                              WorkBarHeight : Integer;
                              Pattern   : string); 
      procedure DrawCodewordBitmask (RowNumber : Integer;
                                     ColNumber : Integer;
                                     WorkBarHeight : Integer;
                                     Bitmask   : DWord);
      procedure DrawLeftRowIndicator (RowNumber : Integer;
                                      WorkBarHeight : Integer;
                                      NumRows : Integer;
                                      NumCols : Integer);
      procedure DrawRightRowIndicator (RowNumber : Integer;
                                       ColNumber : Integer;
                                       WorkBarHeight : Integer;
                                       NumRows : Integer;
                                       NumCols : Integer);
      procedure DrawStartPattern (RowNumber : Integer;
                                  WorkBarHeight : Integer);
      procedure DrawStopPattern (RowNumber : Integer;
                                 ColNumber : Integer;
                                 WorkBarHeight : Integer);
      procedure EncodeBinary (var Position : Integer; CodeLen : Integer);
      procedure EncodeNumeric (var Position : Integer; CodeLen : Integer);
      procedure EncodeText (var Position : Integer; CodeLen : Integer);
      procedure GenerateCodewords; override;
      procedure GetNextCharacter (var NewChar  : Integer;
                                  var Codeword : Boolean;
                                  var Position : Integer;
                                  CodeLen      : Integer);
      function  GetPDF417ECCLevel : TStPDF417ECCLevels;
      function  GetRealErrorLevel : Integer;
      function  GoodForNumericCompaction (Position : Integer;
                                          CodeLen : Integer;
                                          var Count : Integer) : Boolean;
      function  GoodForTextCompaction (Position : Integer;
                                       CodeLen : Integer;
                                       var Count : Integer) : Boolean;
      function  IsNumericString (const S : string) : boolean;
      procedure SetBarHeight (const v : Integer); override;
      procedure SetBarHeightToWidth (const v : Integer); override;
      procedure SetBarWidth (const v : Integer); override;
      procedure SetNumColumns (const v : Integer);
      procedure SetNumRows (const v : Integer);
      procedure SetPDF417ECCLevel (const v : TStPDF417ECCLevels);
      procedure SetRelativeBarHeight (const v : Boolean); override;
      procedure SetTruncated (const v : Boolean);
      procedure TextToCodewords;

    public
      constructor Create (AOwner : TComponent); override;

      procedure RenderToResolution (var OutBitmap : TBitmap;
                                    ResX : Integer;
                                    ResY : Integer;
                                    var SizeX : Integer;
                                    var SizeY : Integer); override;

    published
      property ECCLevel : TStPDF417ECCLevels
               read GetPDF417ECCLevel write SetPDF417ECCLevel default ecAuto;
      property NumColumns : Integer read FNumColumns write SetNumColumns
               default 0;
      property NumRows : Integer read FNumRows write SetNumRows
               default 0;
      property Truncated : Boolean read FTruncated write SetTruncated
               default False;

      property Alignment;
      property BackgroundColor;
      property BarCodeHeight;
      property BarCodeWidth;
      property BarHeight;
      property BarHeightToWidth;
      property BarWidth;
      property Bitmap;
      property CaptionLayout;
      property Code;
      property ExtendedSyntax;
      property Height default 81;
      property RelativeBarHeight;
      property QuietZone;
      property Width default 273;

      property Caption;
      property Color;
      property Font;
  end;

  { TStMaxiCodeBarcode }

  TStMaxiCodeBarcode = class (TStCustom2DBarcode)
    private
      FMode                : TStMaxiCodeMode;
      FCodewords           : TStMaxiCodeECCData;
      FNumCodewords        : Integer;
      FHighlight           : Boolean;
      FShowCodewords       : Boolean;
      FShowAll             : Boolean;
      FMessage             : TStMaxiCodeECCData;
      FCarrierCountryCode  : Integer;
      FCarrierPostalCode   : string;
      FCarrierServiceClass : Integer;
      FAutoScale           : Boolean;
      FHorPixelsPerMM      : Extended;
      FVerPixelsPerMM      : Extended;
      FMaxiHexWidth        : Extended;
      FMaxiHexHeight       : Extended;
      FMaxiHexVOffset      : Extended;
      FMaxiHexHOffset      : Extended;

      { Log and AnitLog data for Galois field arithmetic }
      FLog     : array [0..StMaxiCodeGaloisField] of Integer;
      FAntiLog : array [0..StMaxiCodeGaloisField] of Integer;
      
    protected
      procedure AddCodeword (Value : Integer);
      function  CalculateBarCodeWidth (PaintableWidth : Integer) : Integer;
                override;
      function  CalculateBarCodeHeight (PaintableHeight : Integer) : Integer;
                override;
      procedure DrawBarcode; override;
      procedure DrawFinder;
      procedure DrawHex (XPos, YPos : Integer);
      procedure GenerateCodewords; override;
      procedure GenerateECC;
      procedure GetNextCharacter (var NewChar  : Integer;
                                  var Codeword : Boolean;
                                  var Position : Integer;
                                  CodeLen      : Integer);
      procedure GetSizes;
      procedure GetSizesEx (ResX : Integer; ResY : Integer);
      procedure PlotCell (Row : Integer; Col : Integer);
      procedure SetAutoScale (const v : Boolean);
      procedure SetBarHeight (const v : Integer); override;
      procedure SetBarWidth (const v : Integer); override;
      procedure SetCarrierCountryCode (const v : Integer);
      procedure SetCarrierPostalCode (const v : string);
      procedure SetCarrierServiceClass (const v : Integer);
      procedure SetMode (const v : TStMaxiCodeMode);
      procedure SetHorPixelsPerMM (const v : Extended);
      procedure SetVerPixelsPerMM (const v : Extended);
      procedure TextToCodewords;

    public
      constructor Create (AOwner : TComponent); override;

      procedure RenderToResolution (var OutBitmap : TBitmap;
                                    ResX          : Integer;
                                    ResY          : Integer;
                                    var SizeX     : Integer;
                                    var SizeY     : Integer); override;

    published
      property AutoScale : Boolean
               read FAutoScale write SetAutoScale default True;
      property CarrierCountryCode : Integer
               read FCarrierCountryCode write SetCarrierCountryCode default 0;
      property CarrierPostalCode : string
               read FCarrierPostalCode write SetCarrierPostalCode;
      property CarrierServiceClass : Integer
               read FCarrierServiceClass write SetCarrierServiceClass
               default 0;
      property HorPixelsPerMM : Extended
               read FHorPixelsPerMM write SetHorPixelsPerMM;

      property Mode : TStMaxiCodeMode
               read FMode write SetMode default cmMode4;
      property VerPixelsPerMM : Extended
               read FVerPixelsPerMM write SetVerPixelsPerMM;

      property Alignment;
      property BackgroundColor;
      property BarCodeHeight;
      property BarCodeWidth;
      property BarHeight default 0;
      property BarWidth default 0;
      property Bitmap;
      property CaptionLayout;
      property Code;
      property ExtendedSyntax;
      property Height default 129;
      property QuietZone;
      property Width default 121;

      property Caption;
      property Color;
      property Font;
  end;


implementation
  { PDF417 types and constants }

type
  TStPDF417CodewordArray = array [0..2] of array [0..928] of Longint;

const

  StPDF417CellWidth = 17;

  StPDF417Codewords : TstPDF417CodewordArray =
  (($1d5c0, $1eaf0, $1f57c, $1d4e0, $1ea78, $1f53e, $1a8c0, $1d470, $1a860,
    $15040, $1a830, $15020, $1adc0, $1d6f0, $1eb7c, $1ace0, $1d678, $1eb3e,
    $158c0, $1ac70, $15860, $15dc0, $1aef0, $1d77c, $15ce0, $1ae78, $1d73e,
    $15c70, $1ae3c, $15ef0, $1af7c, $15e78, $1af3e, $15f7c, $1f5fa, $1d2e0,
    $1e978, $1f4be, $1a4c0, $1d270, $1e93c, $1a460, $1d238, $14840, $1a430,
    $1d21c, $14820, $1a418, $14810, $1a6e0, $1d378, $1e9be, $14cc0, $1a670,
    $1d33c, $14c60, $1a638, $1d31e, $14c30, $1a61c, $14ee0, $1a778, $1d3be,
    $14e70, $1a73c, $14e38, $1a71e, $14f78, $1a7be, $14f3c, $14f1e, $1a2c0,
    $1d170, $1e8bc, $1a260, $1d138, $1e89e, $14440, $1a230, $1d11c, $14420,
    $1a218, $14410, $14408, $146c0, $1a370, $1d1bc, $14660, $1a338, $1d19e,
    $14630, $1a31c, $14618, $1460c, $14770, $1a3bc, $14738, $1a39e, $1471c,
    $147bc, $1a160, $1d0b8, $1e85e, $14240, $1a130, $1d09c, $14220, $1a118,
    $1d08e, $14210, $1a10c, $14208, $1a106, $14360, $1a1b8, $1d0de, $14330,
    $1a19c, $14318, $1a18e, $1430c, $14306, $1a1de, $1438e, $14140, $1a0b0,
    $1d05c, $14120, $1a098, $1d04e, $14110, $1a08c, $14108, $1a086, $14104,
    $141b0, $14198, $1418c, $140a0, $1d02e, $1a04c, $1a046, $14082, $1cae0,
    $1e578, $1f2be, $194c0, $1ca70, $1e53c, $19460, $1ca38, $1e51e, $12840,
    $19430, $12820, $196e0, $1cb78, $1e5be, $12cc0, $19670, $1cb3c, $12c60,
    $19638, $12c30, $12c18, $12ee0, $19778, $1cbbe, $12e70, $1973c, $12e38,
    $12e1c, $12f78, $197be, $12f3c, $12fbe, $1dac0, $1ed70, $1f6bc, $1da60,
    $1ed38, $1f69e, $1b440, $1da30, $1ed1c, $1b420, $1da18, $1ed0e, $1b410,
    $1da0c, $192c0, $1c970, $1e4bc, $1b6c0, $19260, $1c938, $1e49e, $1b660,
    $1db38, $1ed9e, $16c40, $12420, $19218, $1c90e, $16c20, $1b618, $16c10,
    $126c0, $19370, $1c9bc, $16ec0, $12660, $19338, $1c99e, $16e60, $1b738,
    $1db9e, $16e30, $12618, $16e18, $12770, $193bc, $16f70, $12738, $1939e,
    $16f38, $1b79e, $16f1c, $127bc, $16fbc, $1279e, $16f9e, $1d960, $1ecb8,
    $1f65e, $1b240, $1d930, $1ec9c, $1b220, $1d918, $1ec8e, $1b210, $1d90c,
    $1b208, $1b204, $19160, $1c8b8, $1e45e, $1b360, $19130, $1c89c, $16640,
    $12220, $1d99c, $1c88e, $16620, $12210, $1910c, $16610, $1b30c, $19106,
    $12204, $12360, $191b8, $1c8de, $16760, $12330, $1919c, $16730, $1b39c,
    $1918e, $16718, $1230c, $12306, $123b8, $191de, $167b8, $1239c, $1679c,
    $1238e, $1678e, $167de, $1b140, $1d8b0, $1ec5c, $1b120, $1d898, $1ec4e,
    $1b110, $1d88c, $1b108, $1d886, $1b104, $1b102, $12140, $190b0, $1c85c,
    $16340, $12120, $19098, $1c84e, $16320, $1b198, $1d8ce, $16310, $12108,
    $19086, $16308, $1b186, $16304, $121b0, $190dc, $163b0, $12198, $190ce,
    $16398, $1b1ce, $1638c, $12186, $16386, $163dc, $163ce, $1b0a0, $1d858,
    $1ec2e, $1b090, $1d84c, $1b088, $1d846, $1b084, $1b082, $120a0, $19058,
    $1c82e, $161a0, $12090, $1904c, $16190, $1b0cc, $19046, $16188, $12084,
    $16184, $12082, $120d8, $161d8, $161cc, $161c6, $1d82c, $1d826, $1b042,
    $1902c, $12048, $160c8, $160c4, $160c2, $18ac0, $1c570, $1e2bc, $18a60,
    $1c538, $11440, $18a30, $1c51c, $11420, $18a18, $11410, $11408, $116c0,
    $18b70, $1c5bc, $11660, $18b38, $1c59e, $11630, $18b1c, $11618, $1160c,
    $11770, $18bbc, $11738, $18b9e, $1171c, $117bc, $1179e, $1cd60, $1e6b8,
    $1f35e, $19a40, $1cd30, $1e69c, $19a20, $1cd18, $1e68e, $19a10, $1cd0c,
    $19a08, $1cd06, $18960, $1c4b8, $1e25e, $19b60, $18930, $1c49c, $13640,
    $11220, $1cd9c, $1c48e, $13620, $19b18, $1890c, $13610, $11208, $13608,
    $11360, $189b8, $1c4de, $13760, $11330, $1cdde, $13730, $19b9c, $1898e,
    $13718, $1130c, $1370c, $113b8, $189de, $137b8, $1139c, $1379c, $1138e,
    $113de, $137de, $1dd40, $1eeb0, $1f75c, $1dd20, $1ee98, $1f74e, $1dd10,
    $1ee8c, $1dd08, $1ee86, $1dd04, $19940, $1ccb0, $1e65c, $1bb40, $19920,
    $1eedc, $1e64e, $1bb20, $1dd98, $1eece, $1bb10, $19908, $1cc86, $1bb08,
    $1dd86, $19902, $11140, $188b0, $1c45c, $13340, $11120, $18898, $1c44e,
    $17740, $13320, $19998, $1ccce, $17720, $1bb98, $1ddce, $18886, $17710,
    $13308, $19986, $17708, $11102, $111b0, $188dc, $133b0, $11198, $188ce,
    $177b0, $13398, $199ce, $17798, $1bbce, $11186, $13386, $111dc, $133dc,
    $111ce, $177dc, $133ce, $1dca0, $1ee58, $1f72e, $1dc90, $1ee4c, $1dc88,
    $1ee46, $1dc84, $1dc82, $198a0, $1cc58, $1e62e, $1b9a0, $19890, $1ee6e,
    $1b990, $1dccc, $1cc46, $1b988, $19884, $1b984, $19882, $1b982, $110a0,
    $18858, $1c42e, $131a0, $11090, $1884c, $173a0, $13190, $198cc, $18846,
    $17390, $1b9cc, $11084, $17388, $13184, $11082, $13182, $110d8, $1886e,
    $131d8, $110cc, $173d8, $131cc, $110c6, $173cc, $131c6, $110ee, $173ee,
    $1dc50, $1ee2c, $1dc48, $1ee26, $1dc44, $1dc42, $19850, $1cc2c, $1b8d0,
    $19848, $1cc26, $1b8c8, $1dc66, $1b8c4, $19842, $1b8c2, $11050, $1882c,
    $130d0, $11048, $18826, $171d0, $130c8, $19866, $171c8, $1b8e6, $11042,
    $171c4, $130c2, $171c2, $130ec, $171ec, $171e6, $1ee16, $1dc22, $1cc16,
    $19824, $19822, $11028, $13068, $170e8, $11022, $13062, $18560, $10a40,
    $18530, $10a20, $18518, $1c28e, $10a10, $1850c, $10a08, $18506, $10b60,
    $185b8, $1c2de, $10b30, $1859c, $10b18, $1858e, $10b0c, $10b06, $10bb8,
    $185de, $10b9c, $10b8e, $10bde, $18d40, $1c6b0, $1e35c, $18d20, $1c698,
    $18d10, $1c68c, $18d08, $1c686, $18d04, $10940, $184b0, $1c25c, $11b40,
    $10920, $1c6dc, $1c24e, $11b20, $18d98, $1c6ce, $11b10, $10908, $18486,
    $11b08, $18d86, $10902, $109b0, $184dc, $11bb0, $10998, $184ce, $11b98,
    $18dce, $11b8c, $10986, $109dc, $11bdc, $109ce, $11bce, $1cea0, $1e758,
    $1f3ae, $1ce90, $1e74c, $1ce88, $1e746, $1ce84, $1ce82, $18ca0, $1c658,
    $19da0, $18c90, $1c64c, $19d90, $1cecc, $1c646, $19d88, $18c84, $19d84,
    $18c82, $19d82, $108a0, $18458, $119a0, $10890, $1c66e, $13ba0, $11990,
    $18ccc, $18446, $13b90, $19dcc, $10884, $13b88, $11984, $10882, $11982,
    $108d8, $1846e, $119d8, $108cc, $13bd8, $119cc, $108c6, $13bcc, $119c6,
    $108ee, $119ee, $13bee, $1ef50, $1f7ac, $1ef48, $1f7a6, $1ef44, $1ef42,
    $1ce50, $1e72c, $1ded0, $1ef6c, $1e726, $1dec8, $1ef66, $1dec4, $1ce42,
    $1dec2, $18c50, $1c62c, $19cd0, $18c48, $1c626, $1bdd0, $19cc8, $1ce66,
    $1bdc8, $1dee6, $18c42, $1bdc4, $19cc2, $1bdc2, $10850, $1842c, $118d0,
    $10848, $18426, $139d0, $118c8, $18c66, $17bd0, $139c8, $19ce6, $10842,
    $17bc8, $1bde6, $118c2, $17bc4, $1086c, $118ec, $10866, $139ec, $118e6,
    $17bec, $139e6, $17be6, $1ef28, $1f796, $1ef24, $1ef22, $1ce28, $1e716,
    $1de68, $1ef36, $1de64, $1ce22, $1de62, $18c28, $1c616, $19c68, $18c24,
    $1bce8, $19c64, $18c22, $1bce4, $19c62, $1bce2, $10828, $18416, $11868,
    $18c36, $138e8, $11864, $10822, $179e8, $138e4, $11862, $179e4, $138e2,
    $179e2, $11876, $179f6, $1ef12, $1de34, $1de32, $19c34, $1bc74, $1bc72,
    $11834, $13874, $178f4, $178f2, $10540, $10520, $18298, $10510, $10508,
    $10504, $105b0, $10598, $1058c, $10586, $105dc, $105ce, $186a0, $18690,
    $1c34c, $18688, $1c346, $18684, $18682, $104a0, $18258, $10da0, $186d8,
    $1824c, $10d90, $186cc, $10d88, $186c6, $10d84, $10482, $10d82, $104d8,
    $1826e, $10dd8, $186ee, $10dcc, $104c6, $10dc6, $104ee, $10dee, $1c750,
    $1c748, $1c744, $1c742, $18650, $18ed0, $1c76c, $1c326, $18ec8, $1c766,
    $18ec4, $18642, $18ec2, $10450, $10cd0, $10448, $18226, $11dd0, $10cc8,
    $10444, $11dc8, $10cc4, $10442, $11dc4, $10cc2, $1046c, $10cec, $10466,
    $11dec, $10ce6, $11de6, $1e7a8, $1e7a4, $1e7a2, $1c728, $1cf68, $1e7b6,
    $1cf64, $1c722, $1cf62, $18628, $1c316, $18e68, $1c736, $19ee8, $18e64,
    $18622, $19ee4, $18e62, $19ee2, $10428, $18216, $10c68, $18636, $11ce8,
    $10c64, $10422, $13de8, $11ce4, $10c62, $13de4, $11ce2, $10436, $10c76,
    $11cf6, $13df6, $1f7d4, $1f7d2, $1e794, $1efb4, $1e792, $1efb2, $1c714,
    $1cf34, $1c712, $1df74, $1cf32, $1df72, $18614, $18e34, $18612, $19e74,
    $18e32, $1bef4),
   ($1f560, $1fab8, $1ea40, $1f530, $1fa9c, $1ea20, $1f518, $1fa8e, $1ea10,
    $1f50c, $1ea08, $1f506, $1ea04, $1eb60, $1f5b8, $1fade, $1d640, $1eb30,
    $1f59c, $1d620, $1eb18, $1f58e, $1d610, $1eb0c, $1d608, $1eb06, $1d604,
    $1d760, $1ebb8, $1f5de, $1ae40, $1d730, $1eb9c, $1ae20, $1d718, $1eb8e,
    $1ae10, $1d70c, $1ae08, $1d706, $1ae04, $1af60, $1d7b8, $1ebde, $15e40,
    $1af30, $1d79c, $15e20, $1af18, $1d78e, $15e10, $1af0c, $15e08, $1af06,
    $15f60, $1afb8, $1d7de, $15f30, $1af9c, $15f18, $1af8e, $15f0c, $15fb8,
    $1afde, $15f9c, $15f8e, $1e940, $1f4b0, $1fa5c, $1e920, $1f498, $1fa4e,
    $1e910, $1f48c, $1e908, $1f486, $1e904, $1e902, $1d340, $1e9b0, $1f4dc,
    $1d320, $1e998, $1f4ce, $1d310, $1e98c, $1d308, $1e986, $1d304, $1d302,
    $1a740, $1d3b0, $1e9dc, $1a720, $1d398, $1e9ce, $1a710, $1d38c, $1a708,
    $1d386, $1a704, $1a702, $14f40, $1a7b0, $1d3dc, $14f20, $1a798, $1d3ce,
    $14f10, $1a78c, $14f08, $1a786, $14f04, $14fb0, $1a7dc, $14f98, $1a7ce,
    $14f8c, $14f86, $14fdc, $14fce, $1e8a0, $1f458, $1fa2e, $1e890, $1f44c,
    $1e888, $1f446, $1e884, $1e882, $1d1a0, $1e8d8, $1f46e, $1d190, $1e8cc,
    $1d188, $1e8c6, $1d184, $1d182, $1a3a0, $1d1d8, $1e8ee, $1a390, $1d1cc,
    $1a388, $1d1c6, $1a384, $1a382, $147a0, $1a3d8, $1d1ee, $14790, $1a3cc,
    $14788, $1a3c6, $14784, $14782, $147d8, $1a3ee, $147cc, $147c6, $147ee,
    $1e850, $1f42c, $1e848, $1f426, $1e844, $1e842, $1d0d0, $1e86c, $1d0c8,
    $1e866, $1d0c4, $1d0c2, $1a1d0, $1d0ec, $1a1c8, $1d0e6, $1a1c4, $1a1c2,
    $143d0, $1a1ec, $143c8, $1a1e6, $143c4, $143c2, $143ec, $143e6, $1e828,
    $1f416, $1e824, $1e822, $1d068, $1e836, $1d064, $1d062, $1a0e8, $1d076,
    $1a0e4, $1a0e2, $141e8, $1a0f6, $141e4, $141e2, $1e814, $1e812, $1d034,
    $1d032, $1a074, $1a072, $1e540, $1f2b0, $1f95c, $1e520, $1f298, $1f94e,
    $1e510, $1f28c, $1e508, $1f286, $1e504, $1e502, $1cb40, $1e5b0, $1f2dc,
    $1cb20, $1e598, $1f2ce, $1cb10, $1e58c, $1cb08, $1e586, $1cb04, $1cb02,
    $19740, $1cbb0, $1e5dc, $19720, $1cb98, $1e5ce, $19710, $1cb8c, $19708,
    $1cb86, $19704, $19702, $12f40, $197b0, $1cbdc, $12f20, $19798, $1cbce,
    $12f10, $1978c, $12f08, $19786, $12f04, $12fb0, $197dc, $12f98, $197ce,
    $12f8c, $12f86, $12fdc, $12fce, $1f6a0, $1fb58, $16bf0, $1f690, $1fb4c,
    $169f8, $1f688, $1fb46, $168fc, $1f684, $1f682, $1e4a0, $1f258, $1f92e,
    $1eda0, $1e490, $1fb6e, $1ed90, $1f6cc, $1f246, $1ed88, $1e484, $1ed84,
    $1e482, $1ed82, $1c9a0, $1e4d8, $1f26e, $1dba0, $1c990, $1e4cc, $1db90,
    $1edcc, $1e4c6, $1db88, $1c984, $1db84, $1c982, $1db82, $193a0, $1c9d8,
    $1e4ee, $1b7a0, $19390, $1c9cc, $1b790, $1dbcc, $1c9c6, $1b788, $19384,
    $1b784, $19382, $1b782, $127a0, $193d8, $1c9ee, $16fa0, $12790, $193cc,
    $16f90, $1b7cc, $193c6, $16f88, $12784, $16f84, $12782, $127d8, $193ee,
    $16fd8, $127cc, $16fcc, $127c6, $16fc6, $127ee, $1f650, $1fb2c, $165f8,
    $1f648, $1fb26, $164fc, $1f644, $1647e, $1f642, $1e450, $1f22c, $1ecd0,
    $1e448, $1f226, $1ecc8, $1f666, $1ecc4, $1e442, $1ecc2, $1c8d0, $1e46c,
    $1d9d0, $1c8c8, $1e466, $1d9c8, $1ece6, $1d9c4, $1c8c2, $1d9c2, $191d0,
    $1c8ec, $1b3d0, $191c8, $1c8e6, $1b3c8, $1d9e6, $1b3c4, $191c2, $1b3c2,
    $123d0, $191ec, $167d0, $123c8, $191e6, $167c8, $1b3e6, $167c4, $123c2,
    $167c2, $123ec, $167ec, $123e6, $167e6, $1f628, $1fb16, $162fc, $1f624,
    $1627e, $1f622, $1e428, $1f216, $1ec68, $1f636, $1ec64, $1e422, $1ec62,
    $1c868, $1e436, $1d8e8, $1c864, $1d8e4, $1c862, $1d8e2, $190e8, $1c876,
    $1b1e8, $1d8f6, $1b1e4, $190e2, $1b1e2, $121e8, $190f6, $163e8, $121e4,
    $163e4, $121e2, $163e2, $121f6, $163f6, $1f614, $1617e, $1f612, $1e414,
    $1ec34, $1e412, $1ec32, $1c834, $1d874, $1c832, $1d872, $19074, $1b0f4,
    $19072, $1b0f2, $120f4, $161f4, $120f2, $161f2, $1f60a, $1e40a, $1ec1a,
    $1c81a, $1d83a, $1903a, $1b07a, $1e2a0, $1f158, $1f8ae, $1e290, $1f14c,
    $1e288, $1f146, $1e284, $1e282, $1c5a0, $1e2d8, $1f16e, $1c590, $1e2cc,
    $1c588, $1e2c6, $1c584, $1c582, $18ba0, $1c5d8, $1e2ee, $18b90, $1c5cc,
    $18b88, $1c5c6, $18b84, $18b82, $117a0, $18bd8, $1c5ee, $11790, $18bcc,
    $11788, $18bc6, $11784, $11782, $117d8, $18bee, $117cc, $117c6, $117ee,
    $1f350, $1f9ac, $135f8, $1f348, $1f9a6, $134fc, $1f344, $1347e, $1f342,
    $1e250, $1f12c, $1e6d0, $1e248, $1f126, $1e6c8, $1f366, $1e6c4, $1e242,
    $1e6c2, $1c4d0, $1e26c, $1cdd0, $1c4c8, $1e266, $1cdc8, $1e6e6, $1cdc4,
    $1c4c2, $1cdc2, $189d0, $1c4ec, $19bd0, $189c8, $1c4e6, $19bc8, $1cde6,
    $19bc4, $189c2, $19bc2, $113d0, $189ec, $137d0, $113c8, $189e6, $137c8,
    $19be6, $137c4, $113c2, $137c2, $113ec, $137ec, $113e6, $137e6, $1fba8,
    $175f0, $1bafc, $1fba4, $174f8, $1ba7e, $1fba2, $1747c, $1743e, $1f328,
    $1f996, $132fc, $1f768, $1fbb6, $176fc, $1327e, $1f764, $1f322, $1767e,
    $1f762, $1e228, $1f116, $1e668, $1e224, $1eee8, $1f776, $1e222, $1eee4,
    $1e662, $1eee2, $1c468, $1e236, $1cce8, $1c464, $1dde8, $1cce4, $1c462,
    $1dde4, $1cce2, $1dde2, $188e8, $1c476, $199e8, $188e4, $1bbe8, $199e4,
    $188e2, $1bbe4, $199e2, $1bbe2, $111e8, $188f6, $133e8, $111e4, $177e8,
    $133e4, $111e2, $177e4, $133e2, $177e2, $111f6, $133f6, $1fb94, $172f8,
    $1b97e, $1fb92, $1727c, $1723e, $1f314, $1317e, $1f734, $1f312, $1737e,
    $1f732, $1e214, $1e634, $1e212, $1ee74, $1e632, $1ee72, $1c434, $1cc74,
    $1c432, $1dcf4, $1cc72, $1dcf2, $18874, $198f4, $18872, $1b9f4, $198f2,
    $1b9f2, $110f4, $131f4, $110f2, $173f4, $131f2, $173f2, $1fb8a, $1717c,
    $1713e, $1f30a, $1f71a, $1e20a, $1e61a, $1ee3a, $1c41a, $1cc3a, $1dc7a,
    $1883a, $1987a, $1b8fa, $1107a, $130fa, $171fa, $170be, $1e150, $1f0ac,
    $1e148, $1f0a6, $1e144, $1e142, $1c2d0, $1e16c, $1c2c8, $1e166, $1c2c4,
    $1c2c2, $185d0, $1c2ec, $185c8, $1c2e6, $185c4, $185c2, $10bd0, $185ec,
    $10bc8, $185e6, $10bc4, $10bc2, $10bec, $10be6, $1f1a8, $1f8d6, $11afc,
    $1f1a4, $11a7e, $1f1a2, $1e128, $1f096, $1e368, $1e124, $1e364, $1e122,
    $1e362, $1c268, $1e136, $1c6e8, $1c264, $1c6e4, $1c262, $1c6e2, $184e8,
    $1c276, $18de8, $184e4, $18de4, $184e2, $18de2, $109e8, $184f6, $11be8,
    $109e4, $11be4, $109e2, $11be2, $109f6, $11bf6, $1f9d4, $13af8, $19d7e,
    $1f9d2, $13a7c, $13a3e, $1f194, $1197e, $1f3b4, $1f192, $13b7e, $1f3b2,
    $1e114, $1e334, $1e112, $1e774, $1e332, $1e772, $1c234, $1c674, $1c232,
    $1cef4, $1c672, $1cef2, $18474, $18cf4, $18472, $19df4, $18cf2, $19df2,
    $108f4, $119f4, $108f2, $13bf4, $119f2, $13bf2, $17af0, $1bd7c, $17a78,
    $1bd3e, $17a3c, $17a1e, $1f9ca, $1397c, $1fbda, $17b7c, $1393e, $17b3e,
    $1f18a, $1f39a, $1f7ba, $1e10a, $1e31a, $1e73a, $1ef7a, $1c21a, $1c63a,
    $1ce7a, $1defa, $1843a, $18c7a, $19cfa, $1bdfa, $1087a, $118fa, $139fa,
    $17978, $1bcbe, $1793c, $1791e, $138be, $179be, $178bc, $1789e, $1785e,
    $1e0a8, $1e0a4, $1e0a2, $1c168, $1e0b6, $1c164, $1c162, $182e8, $1c176,
    $182e4, $182e2, $105e8, $182f6, $105e4, $105e2, $105f6, $1f0d4, $10d7e,
    $1f0d2, $1e094, $1e1b4, $1e092, $1e1b2, $1c134, $1c374, $1c132, $1c372,
    $18274, $186f4, $18272, $186f2, $104f4, $10df4, $104f2, $10df2, $1f8ea,
    $11d7c, $11d3e, $1f0ca, $1f1da, $1e08a, $1e19a, $1e3ba, $1c11a, $1c33a,
    $1c77a, $1823a, $1867a, $18efa, $1047a, $10cfa, $11dfa, $13d78, $19ebe,
    $13d3c, $13d1e, $11cbe, $13dbe, $17d70, $1bebc, $17d38, $1be9e, $17d1c,
    $17d0e, $13cbc, $17dbc, $13c9e, $17d9e, $17cb8, $1be5e, $17c9c, $17c8e,
    $13c5e, $17cde, $17c5c, $17c4e, $17c2e, $1c0b4, $1c0b2, $18174, $18172,
    $102f4, $102f2, $1e0da, $1c09a, $1c1ba, $1813a, $1837a, $1027a, $106fa,
    $10ebe, $11ebc, $11e9e, $13eb8, $19f5e, $13e9c, $13e8e, $11e5e, $13ede,
    $17eb0, $1bf5c, $17e98, $1bf4e, $17e8c, $17e86, $13e5c, $17edc, $13e4e,
    $17ece, $17e58, $1bf2e, $17e4c, $17e46, $13e2e, $17e6e, $17e2c, $17e26,
    $10f5e, $11f5c, $11f4e, $13f58, $19fae, $13f4c, $13f46, $11f2e, $13f6e,
    $13f2c, $13f26),
   ($1abe0, $1d5f8, $153c0, $1a9f0, $1d4fc, $151e0, $1a8f8, $1d47e, $150f0,
    $1a87c, $15078, $1fad0, $15be0, $1adf8, $1fac8, $159f0, $1acfc, $1fac4,
    $158f8, $1ac7e, $1fac2, $1587c, $1f5d0, $1faec, $15df8, $1f5c8, $1fae6,
    $15cfc, $1f5c4, $15c7e, $1f5c2, $1ebd0, $1f5ec, $1ebc8, $1f5e6, $1ebc4,
    $1ebc2, $1d7d0, $1ebec, $1d7c8, $1ebe6, $1d7c4, $1d7c2, $1afd0, $1d7ec,
    $1afc8, $1d7e6, $1afc4, $14bc0, $1a5f0, $1d2fc, $149e0, $1a4f8, $1d27e,
    $148f0, $1a47c, $14878, $1a43e, $1483c, $1fa68, $14df0, $1a6fc, $1fa64,
    $14cf8, $1a67e, $1fa62, $14c7c, $14c3e, $1f4e8, $1fa76, $14efc, $1f4e4,
    $14e7e, $1f4e2, $1e9e8, $1f4f6, $1e9e4, $1e9e2, $1d3e8, $1e9f6, $1d3e4,
    $1d3e2, $1a7e8, $1d3f6, $1a7e4, $1a7e2, $145e0, $1a2f8, $1d17e, $144f0,
    $1a27c, $14478, $1a23e, $1443c, $1441e, $1fa34, $146f8, $1a37e, $1fa32,
    $1467c, $1463e, $1f474, $1477e, $1f472, $1e8f4, $1e8f2, $1d1f4, $1d1f2,
    $1a3f4, $1a3f2, $142f0, $1a17c, $14278, $1a13e, $1423c, $1421e, $1fa1a,
    $1437c, $1433e, $1f43a, $1e87a, $1d0fa, $14178, $1a0be, $1413c, $1411e,
    $141be, $140bc, $1409e, $12bc0, $195f0, $1cafc, $129e0, $194f8, $1ca7e,
    $128f0, $1947c, $12878, $1943e, $1283c, $1f968, $12df0, $196fc, $1f964,
    $12cf8, $1967e, $1f962, $12c7c, $12c3e, $1f2e8, $1f976, $12efc, $1f2e4,
    $12e7e, $1f2e2, $1e5e8, $1f2f6, $1e5e4, $1e5e2, $1cbe8, $1e5f6, $1cbe4,
    $1cbe2, $197e8, $1cbf6, $197e4, $197e2, $1b5e0, $1daf8, $1ed7e, $169c0,
    $1b4f0, $1da7c, $168e0, $1b478, $1da3e, $16870, $1b43c, $16838, $1b41e,
    $1681c, $125e0, $192f8, $1c97e, $16de0, $124f0, $1927c, $16cf0, $1b67c,
    $1923e, $16c78, $1243c, $16c3c, $1241e, $16c1e, $1f934, $126f8, $1937e,
    $1fb74, $1f932, $16ef8, $1267c, $1fb72, $16e7c, $1263e, $16e3e, $1f274,
    $1277e, $1f6f4, $1f272, $16f7e, $1f6f2, $1e4f4, $1edf4, $1e4f2, $1edf2,
    $1c9f4, $1dbf4, $1c9f2, $1dbf2, $193f4, $193f2, $165c0, $1b2f0, $1d97c,
    $164e0, $1b278, $1d93e, $16470, $1b23c, $16438, $1b21e, $1641c, $1640e,
    $122f0, $1917c, $166f0, $12278, $1913e, $16678, $1b33e, $1663c, $1221e,
    $1661e, $1f91a, $1237c, $1fb3a, $1677c, $1233e, $1673e, $1f23a, $1f67a,
    $1e47a, $1ecfa, $1c8fa, $1d9fa, $191fa, $162e0, $1b178, $1d8be, $16270,
    $1b13c, $16238, $1b11e, $1621c, $1620e, $12178, $190be, $16378, $1213c,
    $1633c, $1211e, $1631e, $121be, $163be, $16170, $1b0bc, $16138, $1b09e,
    $1611c, $1610e, $120bc, $161bc, $1209e, $1619e, $160b8, $1b05e, $1609c,
    $1608e, $1205e, $160de, $1605c, $1604e, $115e0, $18af8, $1c57e, $114f0,
    $18a7c, $11478, $18a3e, $1143c, $1141e, $1f8b4, $116f8, $18b7e, $1f8b2,
    $1167c, $1163e, $1f174, $1177e, $1f172, $1e2f4, $1e2f2, $1c5f4, $1c5f2,
    $18bf4, $18bf2, $135c0, $19af0, $1cd7c, $134e0, $19a78, $1cd3e, $13470,
    $19a3c, $13438, $19a1e, $1341c, $1340e, $112f0, $1897c, $136f0, $11278,
    $1893e, $13678, $19b3e, $1363c, $1121e, $1361e, $1f89a, $1137c, $1f9ba,
    $1377c, $1133e, $1373e, $1f13a, $1f37a, $1e27a, $1e6fa, $1c4fa, $1cdfa,
    $189fa, $1bae0, $1dd78, $1eebe, $174c0, $1ba70, $1dd3c, $17460, $1ba38,
    $1dd1e, $17430, $1ba1c, $17418, $1ba0e, $1740c, $132e0, $19978, $1ccbe,
    $176e0, $13270, $1993c, $17670, $1bb3c, $1991e, $17638, $1321c, $1761c,
    $1320e, $1760e, $11178, $188be, $13378, $1113c, $17778, $1333c, $1111e,
    $1773c, $1331e, $1771e, $111be, $133be, $177be, $172c0, $1b970, $1dcbc,
    $17260, $1b938, $1dc9e, $17230, $1b91c, $17218, $1b90e, $1720c, $17206,
    $13170, $198bc, $17370, $13138, $1989e, $17338, $1b99e, $1731c, $1310e,
    $1730e, $110bc, $131bc, $1109e, $173bc, $1319e, $1739e, $17160, $1b8b8,
    $1dc5e, $17130, $1b89c, $17118, $1b88e, $1710c, $17106, $130b8, $1985e,
    $171b8, $1309c, $1719c, $1308e, $1718e, $1105e, $130de, $171de, $170b0,
    $1b85c, $17098, $1b84e, $1708c, $17086, $1305c, $170dc, $1304e, $170ce,
    $17058, $1b82e, $1704c, $17046, $1302e, $1706e, $1702c, $17026, $10af0,
    $1857c, $10a78, $1853e, $10a3c, $10a1e, $10b7c, $10b3e, $1f0ba, $1e17a,
    $1c2fa, $185fa, $11ae0, $18d78, $1c6be, $11a70, $18d3c, $11a38, $18d1e,
    $11a1c, $11a0e, $10978, $184be, $11b78, $1093c, $11b3c, $1091e, $11b1e,
    $109be, $11bbe, $13ac0, $19d70, $1cebc, $13a60, $19d38, $1ce9e, $13a30,
    $19d1c, $13a18, $19d0e, $13a0c, $13a06, $11970, $18cbc, $13b70, $11938,
    $18c9e, $13b38, $1191c, $13b1c, $1190e, $13b0e, $108bc, $119bc, $1089e,
    $13bbc, $1199e, $13b9e, $1bd60, $1deb8, $1ef5e, $17a40, $1bd30, $1de9c,
    $17a20, $1bd18, $1de8e, $17a10, $1bd0c, $17a08, $1bd06, $17a04, $13960,
    $19cb8, $1ce5e, $17b60, $13930, $19c9c, $17b30, $1bd9c, $19c8e, $17b18,
    $1390c, $17b0c, $13906, $17b06, $118b8, $18c5e, $139b8, $1189c, $17bb8,
    $1399c, $1188e, $17b9c, $1398e, $17b8e, $1085e, $118de, $139de, $17bde,
    $17940, $1bcb0, $1de5c, $17920, $1bc98, $1de4e, $17910, $1bc8c, $17908,
    $1bc86, $17904, $17902, $138b0, $19c5c, $179b0, $13898, $19c4e, $17998,
    $1bcce, $1798c, $13886, $17986, $1185c, $138dc, $1184e, $179dc, $138ce,
    $179ce, $178a0, $1bc58, $1de2e, $17890, $1bc4c, $17888, $1bc46, $17884,
    $17882, $13858, $19c2e, $178d8, $1384c, $178cc, $13846, $178c6, $1182e,
    $1386e, $178ee, $17850, $1bc2c, $17848, $1bc26, $17844, $17842, $1382c,
    $1786c, $13826, $17866, $17828, $1bc16, $17824, $17822, $13816, $17836,
    $10578, $182be, $1053c, $1051e, $105be, $10d70, $186bc, $10d38, $1869e,
    $10d1c, $10d0e, $104bc, $10dbc, $1049e, $10d9e, $11d60, $18eb8, $1c75e,
    $11d30, $18e9c, $11d18, $18e8e, $11d0c, $11d06, $10cb8, $1865e, $11db8,
    $10c9c, $11d9c, $10c8e, $11d8e, $1045e, $10cde, $11dde, $13d40, $19eb0,
    $1cf5c, $13d20, $19e98, $1cf4e, $13d10, $19e8c, $13d08, $19e86, $13d04,
    $13d02, $11cb0, $18e5c, $13db0, $11c98, $18e4e, $13d98, $19ece, $13d8c,
    $11c86, $13d86, $10c5c, $11cdc, $10c4e, $13ddc, $11cce, $13dce, $1bea0,
    $1df58, $1efae, $1be90, $1df4c, $1be88, $1df46, $1be84, $1be82, $13ca0,
    $19e58, $1cf2e, $17da0, $13c90, $19e4c, $17d90, $1becc, $19e46, $17d88,
    $13c84, $17d84, $13c82, $17d82, $11c58, $18e2e, $13cd8, $11c4c, $17dd8,
    $13ccc, $11c46, $17dcc, $13cc6, $17dc6, $10c2e, $11c6e, $13cee, $17dee,
    $1be50, $1df2c, $1be48, $1df26, $1be44, $1be42, $13c50, $19e2c, $17cd0,
    $13c48, $19e26, $17cc8, $1be66, $17cc4, $13c42, $17cc2, $11c2c, $13c6c,
    $11c26, $17cec, $13c66, $17ce6, $1be28, $1df16, $1be24, $1be22, $13c28,
    $19e16, $17c68, $13c24, $17c64, $13c22, $17c62, $11c16, $13c36, $17c76,
    $1be14, $1be12, $13c14, $17c34, $13c12, $17c32, $102bc, $1029e, $106b8,
    $1835e, $1069c, $1068e, $1025e, $106de, $10eb0, $1875c, $10e98, $1874e,
    $10e8c, $10e86, $1065c, $10edc, $1064e, $10ece, $11ea0, $18f58, $1c7ae,
    $11e90, $18f4c, $11e88, $18f46, $11e84, $11e82, $10e58, $1872e, $11ed8,
    $18f6e, $11ecc, $10e46, $11ec6, $1062e, $10e6e, $11eee, $19f50, $1cfac,
    $19f48, $1cfa6, $19f44, $19f42, $11e50, $18f2c, $13ed0, $19f6c, $18f26,
    $13ec8, $11e44, $13ec4, $11e42, $13ec2, $10e2c, $11e6c, $10e26, $13eec,
    $11e66, $13ee6, $1dfa8, $1efd6, $1dfa4, $1dfa2, $19f28, $1cf96, $1bf68,
    $19f24, $1bf64, $19f22, $1bf62, $11e28, $18f16, $13e68, $11e24, $17ee8,
    $13e64, $11e22, $17ee4, $13e62, $17ee2, $10e16, $11e36, $13e76, $17ef6,
    $1df94, $1df92, $19f14, $1bf34, $19f12, $1bf32, $11e14, $13e34, $11e12,
    $17e74, $13e32, $17e72, $1df8a, $19f0a, $1bf1a, $11e0a, $13e1a, $17e3a,
    $1035c, $1034e, $10758, $183ae, $1074c, $10746, $1032e, $1076e, $10f50,
    $187ac, $10f48, $187a6, $10f44, $10f42, $1072c, $10f6c, $10726, $10f66,
    $18fa8, $1c7d6, $18fa4, $18fa2, $10f28, $18796, $11f68, $18fb6, $11f64,
    $10f22, $11f62, $10716, $10f36, $11f76, $1cfd4, $1cfd2, $18f94, $19fb4,
    $18f92, $19fb2, $10f14, $11f34, $10f12, $13f74, $11f32, $13f72, $1cfca,
    $18f8a, $19f9a, $10f0a, $11f1a, $13f3a, $103ac, $103a6, $107a8, $183d6,
    $107a4, $107a2, $10396, $107b6, $187d4, $187d2, $10794, $10fb4, $10792,
    $10fb2, $1c7ea));

type
  TStPDF417TextCompactionMode = (cmAlpha, cmLower, cmMixed, cmPunctuation,
                                 cmNone);
  TStPDF417TextCompactionModes = set of TStPDF417TextCompactionMode;

  TStPDF417TextCompactionData = record
    Value : Integer;
    Mode : TStPDF417TextCompactionModes;
  end;

const
  TStPDF417TextCompaction : array [0..127] of TStPDF417TextCompactionData =
    ((Value : -1; Mode : []),                                  { 000 }
     (Value : -1; Mode : []),                                  { 001 }
     (Value : -1; Mode : []),                                  { 002 }
     (Value : -1; Mode : []),                                  { 003 }
     (Value : -1; Mode : []),                                  { 004 }
     (Value : -1; Mode : []),                                  { 005 }
     (Value : -1; Mode : []),                                  { 006 }
     (Value : -1; Mode : []),                                  { 007 }
     (Value : -1; Mode : []),                                  { 008 }
     (Value : 12; Mode : [cmMixed, cmPunctuation]),            { 009 }
     (Value : 15; Mode : [cmPunctuation]),                     { 010 }
     (Value : -1; Mode : []),                                  { 011 }
     (Value : -1; Mode : []),                                  { 012 }
     (Value : 11; Mode : [cmMixed, cmPunctuation]),            { 013 }
     (Value : -1; Mode : []),                                  { 014 }
     (Value : -1; Mode : []),                                  { 015 }
     (Value : -1; Mode : []),                                  { 016 }
     (Value : -1; Mode : []),                                  { 017 }
     (Value : -1; Mode : []),                                  { 018 }
     (Value : -1; Mode : []),                                  { 019 }
     (Value : -1; Mode : []),                                  { 020 }
     (Value : -1; Mode : []),                                  { 021 }
     (Value : -1; Mode : []),                                  { 022 }
     (Value : -1; Mode : []),                                  { 023 }
     (Value : -1; Mode : []),                                  { 024 }
     (Value : -1; Mode : []),                                  { 025 }
     (Value : -1; Mode : []),                                  { 026 }
     (Value : -1; Mode : []),                                  { 027 }
     (Value : -1; Mode : []),                                  { 028 }
     (Value : -1; Mode : []),                                  { 029 }
     (Value : -1; Mode : []),                                  { 030 }
     (Value : -1; Mode : []),                                  { 031 }
     (Value : 26; Mode : [cmAlpha, cmLower, cmMixed]),         { 032 }
     (Value : 10; Mode : [cmPunctuation]),                     { 033 }
     (Value : 20; Mode : [cmPunctuation]),                     { 034 }
     (Value : 15; Mode : [cmMixed]),                           { 035 }
     (Value : 18; Mode : [cmMixed, cmPunctuation]),            { 036 }
     (Value : 21; Mode : [cmMixed]),                           { 037 }
     (Value : 10; Mode : [cmMixed]),                           { 038 }
     (Value : 28; Mode : [cmPunctuation]),                     { 039 }
     (Value : 23; Mode : [cmPunctuation]),                     { 040 }
     (Value : 24; Mode : [cmPunctuation]),                     { 041 }
     (Value : 22; Mode : [cmMixed, cmPunctuation]),            { 042 }
     (Value : 20; Mode : [cmMixed]),                           { 043 }
     (Value : 13; Mode : [cmMixed, cmPunctuation]),            { 044 }
     (Value : 16; Mode : [cmMixed, cmPunctuation]),            { 045 }
     (Value : 17; Mode : [cmMixed, cmPunctuation]),            { 046 }
     (Value : 19; Mode : [cmMixed, cmPunctuation]),            { 047 }
     (Value :  0; Mode : [cmMixed]),                           { 048 }
     (Value :  1; Mode : [cmMixed]),                           { 049 }
     (Value :  2; Mode : [cmMixed]),                           { 050 }
     (Value :  3; Mode : [cmMixed]),                           { 051 }
     (Value :  4; Mode : [cmMixed]),                           { 052 }
     (Value :  5; Mode : [cmMixed]),                           { 053 }
     (Value :  6; Mode : [cmMixed]),                           { 054 }
     (Value :  7; Mode : [cmMixed]),                           { 055 }
     (Value :  8; Mode : [cmMixed]),                           { 056 }
     (Value :  9; Mode : [cmMixed]),                           { 057 }
     (Value : 14; Mode : [cmMixed, cmPunctuation]),            { 058 }
     (Value :  0; Mode : [cmPunctuation]),                     { 059 }
     (Value :  1; Mode : [cmPunctuation]),                     { 060 }
     (Value : 23; Mode : [cmMixed]),                           { 061 }
     (Value :  2; Mode : [cmPunctuation]),                     { 062 }
     (Value : 25; Mode : [cmPunctuation]),                     { 063 }
     (Value :  3; Mode : [cmPunctuation]),                     { 064 }
     (Value :  0; Mode : [cmAlpha]),                           { 065 }
     (Value :  1; Mode : [cmAlpha]),                           { 066 }
     (Value :  2; Mode : [cmAlpha]),                           { 067 }
     (Value :  3; Mode : [cmAlpha]),                           { 068 }
     (Value :  4; Mode : [cmAlpha]),                           { 069 }
     (Value :  5; Mode : [cmAlpha]),                           { 070 }
     (Value :  6; Mode : [cmAlpha]),                           { 071 }
     (Value :  7; Mode : [cmAlpha]),                           { 072 }
     (Value :  8; Mode : [cmAlpha]),                           { 073 }
     (Value :  9; Mode : [cmAlpha]),                           { 074 }
     (Value : 10; Mode : [cmAlpha]),                           { 075 }
     (Value : 11; Mode : [cmAlpha]),                           { 076 }
     (Value : 12; Mode : [cmAlpha]),                           { 077 }
     (Value : 13; Mode : [cmAlpha]),                           { 078 }
     (Value : 14; Mode : [cmAlpha]),                           { 079 }
     (Value : 15; Mode : [cmAlpha]),                           { 080 }
     (Value : 16; Mode : [cmAlpha]),                           { 081 }
     (Value : 17; Mode : [cmAlpha]),                           { 082 }
     (Value : 18; Mode : [cmAlpha]),                           { 083 }
     (Value : 19; Mode : [cmAlpha]),                           { 084 }
     (Value : 20; Mode : [cmAlpha]),                           { 085 }
     (Value : 21; Mode : [cmAlpha]),                           { 086 }
     (Value : 22; Mode : [cmAlpha]),                           { 087 }
     (Value : 23; Mode : [cmAlpha]),                           { 088 }
     (Value : 24; Mode : [cmAlpha]),                           { 089 }
     (Value : 25; Mode : [cmAlpha]),                           { 090 }
     (Value :  4; Mode : [cmPunctuation]),                     { 091 }
     (Value :  5; Mode : [cmPunctuation]),                     { 092 }
     (Value :  6; Mode : [cmPunctuation]),                     { 093 }
     (Value : 24; Mode : [cmMixed]),                           { 094 }
     (Value :  7; Mode : [cmPunctuation]),                     { 095 }
     (Value :  8; Mode : [cmPunctuation]),                     { 096 }
     (Value :  0; Mode : [cmLower]),                           { 097 }
     (Value :  1; Mode : [cmLower]),                           { 098 }
     (Value :  2; Mode : [cmLower]),                           { 099 }
     (Value :  3; Mode : [cmLower]),                           { 100 }
     (Value :  4; Mode : [cmLower]),                           { 101 }
     (Value :  5; Mode : [cmLower]),                           { 102 }
     (Value :  6; Mode : [cmLower]),                           { 103 }
     (Value :  7; Mode : [cmLower]),                           { 104 }
     (Value :  8; Mode : [cmLower]),                           { 105 }
     (Value :  9; Mode : [cmLower]),                           { 106 }
     (Value : 10; Mode : [cmLower]),                           { 107 }
     (Value : 11; Mode : [cmLower]),                           { 108 }
     (Value : 12; Mode : [cmLower]),                           { 109 }
     (Value : 13; Mode : [cmLower]),                           { 110 }
     (Value : 14; Mode : [cmLower]),                           { 111 }
     (Value : 15; Mode : [cmLower]),                           { 112 }
     (Value : 16; Mode : [cmLower]),                           { 113 }
     (Value : 17; Mode : [cmLower]),                           { 114 }
     (Value : 18; Mode : [cmLower]),                           { 115 }
     (Value : 19; Mode : [cmLower]),                           { 116 }
     (Value : 20; Mode : [cmLower]),                           { 117 }
     (Value : 21; Mode : [cmLower]),                           { 118 }
     (Value : 22; Mode : [cmLower]),                           { 119 }
     (Value : 23; Mode : [cmLower]),                           { 120 }
     (Value : 24; Mode : [cmLower]),                           { 121 }
     (Value : 25; Mode : [cmLower]),                           { 122 }
     (Value : 26; Mode : [cmPunctuation]),                     { 123 }
     (Value : 21; Mode : [cmPunctuation]),                     { 124 }
     (Value : 27; Mode : [cmPunctuation]),                     { 125 }
     (Value :  9; Mode : [cmPunctuation]),                     { 126 }
     (Value : -1; Mode : []));                                 { 127 }

{ TStMaxiCode types and constants }

type
  TStMaxiCodeCodeSet = (csCodeSetA, csCodeSetB, csCodeSetC, csCodeSetD,
                        csCodeSetE, csNone);

const
  StMaxiCodeCodeSets : array [csCodeSetA..csCodeSetE] of
                       array [0..255] of ShortInt =
    { csCodeSetA }
      {0} {1} {2} {3} {4} {5} {6} {7} {8} {9}
    ((-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {000}
      -1, -1, -1,  0, -1, -1, -1, -1, -1, -1, {010}
      -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, {020}
      30, -1, 32, 33, 34, 35, 36, 37, 38, 39, {030}
      40, 41, 42, 43, 44, 45, 46, 47, 48, 49, {040}
      50, 51, 52, 53, 54, 55, 56, 57, 58, -1, {050}
      -1, -1, -1, -1, -1,  1,  2,  3,  4,  5, {060}
       6,  7,  8,  9, 10, 11, 12, 13, 14, 15, {070}
      16, 17, 18, 19, 20, 21, 22, 23, 24, 25, {080}
      26, -1, -1, -1, -1, -1, -1, -1, -1, -1, {090}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {100}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {110}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {120}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {130}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {140}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {150}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {160}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {170}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {180}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {190}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {200}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {210}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {220}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {230}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {240}
      -1, -1, -1, -1, -1, -1),                {250}
     { csCodeSetB }
      {0} {1} {2} {3} {4} {5} {6} {7} {8} {9}
     (-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {000}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {010}
      -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, {020}
      30, -1, 47, 53, -1, -1, -1, -1, -1, -1, {030}
      -1, -1, -1, -1, 48, 49, 50, -1, -1, -1, {040}
      -1, -1, -1, -1, -1, -1, -1, -1, 51, 37, {050}
      38, 39, 40, 41, 52, -1, -1, -1, -1, -1, {060}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {070}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {080}
      -1, 42, 43, 44, 45, 46,  0,  1,  2,  3, {090}
       4,  5,  6,  7,  8,  9, 10, 11, 12, 13, {100}
      14, 15, 16, 17, 18, 19, 20, 21, 22, 23, {110}
      24, 25, 26, 32, 54, 34, 35, 36, -1, -1, {120}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {130}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {140}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {150}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {160}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {170}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {180}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {190}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {200}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {210}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {220}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {230}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {240}
      -1, -1, -1, -1, -1, -1),                {250}
     { csCodeSetC }
      {0} {1} {2} {3} {4} {5} {6} {7} {8} {9}
     (-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {000}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {010}
      -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, {020}
      30, -1, 59, -1, -1, -1, -1, -1, -1, -1, {030}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {040}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {050}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {060}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {070}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {080}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {090}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {100}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {110}
      -1, -1, -1, -1, -1, -1, -1, -1, 48, 49, {120}
      50, 51, 52, 53, 54, 55, 56, 57, -1, -1, {130}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {140}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {150}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {160}
      37, -1, 38, -1, -1, -1, -1, 39, 40, 41, {170}
      -1, 42, -1, -1, -1, 43, 44, -1, 45, 46, {180}
      47, -1,  0,  1,  2,  3,  4,  5,  6,  7, {190}
       8,  9, 10, 11, 12, 13, 14, 15, 16, 17, {200}
      18, 19, 20, 21, 22, 23, 24, 25, 26, 32, {210}
      33, 34, 35, 36, -1, -1, -1, -1, -1, -1, {220}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {230}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {240}
      -1, -1, -1, -1, -1, -1),                {250}
     { csCodeSetD }
      {0} {1} {2} {3} {4} {5} {6} {7} {8} {9}
     (-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {000}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {010}
      -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, {020}
      30, -1, 59, -1, -1, -1, -1, -1, -1, -1, {030}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {040}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {050}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {060}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {070}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {080}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {090}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {100}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {110}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {120}
      -1, -1, -1, -1, -1, -1, -1, -1, 47, 48, {130}
      49, 50, 51, 52, 53, 54, 55, 56, 57, -1, {140}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {150}
      -1, 37, -1, -1, -1, -1, -1, -1, 38, -1, {160}
      -1, 39, -1, -1, -1, 40, 41, -1, -1, -1, {170}
      42, -1, -1, 43, 44, -1, -1, 45, -1, -1, {180}
      -1, 46, -1, -1, -1, -1, -1, -1, -1, -1, {190}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {200}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {210}
      -1, -1, -1, -1,  0,  1,  2,  3,  4,  5, {220}
       6,  7,  8,  9, 10, 11, 12, 13, 14, 15, {230}
      16, 17, 18, 19, 20, 21, 22, 23, 24, 25, {240}
      26, 32, 33, 34, 35, 36),                {250}
     { csCodeSetE }
      {0} {1} {2} {3} {4} {5} {6} {7} {8} {9}
     ( 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, {000}
      10, 11, 12, 13, 14, 15, 16, 17, 18, 19, {010}
      20, 21, 22, 23, 24, 25, 26, 30, 32, 33, {020}
      34, 35, 59, -1, -1, -1, -1, -1, -1, -1, {030}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {040}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {050}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {060}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {070}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {080}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {090}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {100}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {110}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {120}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {130}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, 48, {140}
      49, 50, 51, 52, 53, 54, 55, 56, 57, 36, {150}
      37, -1, 38, 39, 40, 41, 42, 43, -1, 44, {160}
      -1, -1, -1, 45, 46, -1, -1, -1, -1, -1, {170}
      -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, {180}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {190}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {200}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {210}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {220}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {230}
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, {240}
      -1, -1, -1, -1, -1, -1));               {250}

{ TStCustom2DBarcode }

constructor TStCustom2DBarcode.Create (AOwner : TComponent);
begin
  inherited Create (AOwner);

  FBitmap := TBitmap.Create;

  FBarWidth          := 2;
  FBarHeight         := 0;
  FBackgroundColor   := clWhite;
  FExtendedSyntax    := True;
  FQuietZone         := 8;
  FAlignment         := taCenter;
  FCaptionLayout     := tlBottom;
  Width              := 329;
  Height             := 50;
  Color              := clBlack;
  FECCLevel          := 2;
  FRelativeBarHeight := False;
  FBarHeightToWidth  := 4;
  FBarHeight         := 2;
  FCaption           := '';
  FCode              := '';
  FECCLevel          := 0;
end;

destructor TStCustom2DBarcode.Destroy;
begin
  FBitmap.Free;

  inherited Destroy;
end;

procedure TStCustom2DBarcode.CopyToClipboard;
begin
  CopyToClipboardRes (0, 0);
end;

procedure TStCustom2DBarcode.CopyToClipboardRes (ResX : Integer;
                                                 ResY : Integer);
var
 {$IFNDEF FPC}
  MetaFile       : TMetaFile;
  MetaFileCanvas : TMetaFileCanvas;
 {$ENDIF}
  RenderBMP      : TBitmap;
  SizeX          : Integer;
  SizeY          : Integer;

begin
  Clipboard.Clear;
  Clipboard.Open;
  try
    RenderBmp := TBitmap.Create;
    try
      RenderToResolution (RenderBmp, ResX, ResY, SizeX, SizeY);
      Clipboard.Assign (RenderBmp);

     {$IFNDEF FPC}
      {metafile}
      MetaFile := TMetaFile.Create;
      try
        MetaFileCanvas := TMetaFileCanvas.Create (MetaFile, 0);
        try
          MetaFile.Enhanced := True;
          MetaFile.Width := ClientWidth;
          MetaFile.Height := ClientHeight;
          MetaFileCanvas.Draw (0, 0, RenderBmp);
        finally
          MetaFileCanvas.Free;
        end;
        Clipboard.Assign (MetaFile);
      finally
        MetaFile.Free;
      end;
     {$ENDIF}
    finally
      RenderBmp.Free;
    end;
  finally
    Clipboard.Close;
  end;
end;

procedure TStCustom2DBarcode.GenerateBarcodeBitmap (BCWidth  : Integer;
                                                    BCHeight : Integer);
var
  TextHeight    : Integer;
  TextWidth     : Integer;
  XPos          : Integer;
  YPos          : Integer;
  TopOffset     : Integer;
  BottomOffset  : Integer;
  BarCodeHeight : Integer;
  BarCodeWidth  : Integer;
  RWidthOffset  : Integer;
  LWidthOffset  : Integer;
  PaintHeight   : Integer;

begin
  { Initialize the canvas }
  FBitmap.Width := BCWidth;
  FBitmap.Height := BCHeight;
  FBitmap.Canvas.Pen.Color := Color;
  FBitmap.Canvas.Brush.Color := BackgroundColor;
  FBitmap.Canvas.FillRect (Rect (0, 0, BCWidth, BCHeight));
  FBitmap.Canvas.Brush.Color := Color;

  { Calculate the size of the caption }
  FBitmap.Canvas.Font.Assign (Font);
  TextHeight := FBitmap.Canvas.TextHeight ('Yg0');
  TextWidth := FBitmap.Canvas.TextWidth (Caption);

  { determine x position of the caption }
  XPos := 0;
  case FAlignment of
    taLeftJustify :
      XPos := 0;
    taRightJustify :
      if BCWidth - TextWidth > 0 then
        XPos := BCWidth - TextWidth
      else
        XPos := 0;
    taCenter :
      if BCWidth - TextWidth > 0 then
        XPos := BCWidth div 2 - TextWidth div 2
      else
        XPos := 0;
  end;

  { determine the y position of the caption.  In addition, determine offsets
    for the barcode painting. }
  TopOffset := 0;
  BottomOffset := 0;
  YPos := 0;
  case FCaptionLayout of
    tlBottom :
      begin
        if BCHeight - 2 - TextHeight > 0 then
          YPos := BCHeight - 2 - TextHeight
        else
          YPos := 0;
        if Caption <> '' then
          BottomOffset := TextHeight + 4;
      end;
    tlTop :
      begin
        YPos := 0;
        if Caption <> '' then
          TopOffset := TextHeight + 4;
      end;
    tlCenter :
      if BCHeight - TextHeight > 0 then
        YPos := BCHeight div 2 - TextHeight div 2
      else
        YPos := 0;
  end;

  { determine the size of the barcode and calculate the rectangle the
    barcode should be painted in.  Take into account the size of the
    caption (and it's existance), and the quiet zone.}
  PaintHeight := BCHeight - QuietZone * 2 - BottomOffset - TopOffset;
  BarCodeHeight := CalculateBarCodeHeight (PaintHeight);
  BarCodeWidth := CalculateBarCodeWidth (BCWidth);
  if BarCodeHeight < PaintHeight then begin
    Inc (BottomOffset, (PaintHeight - BarCodeHeight) div 2);
    Inc (TopOffset, (PaintHeight - BarCodeHeight) div 2);
  end;

  { Position the barcode horizontally }
  LWidthOffset := QuietZone;
  RWidthOffset := QuietZone;
  if BarCodeWidth < BCWidth - QuietZone * 2 then
    case Alignment of
      taLeftJustify :
        begin
          LWidthOffset := QuietZone;
          RWidthOffset := BCWidth - BarCodeWidth - QuietZone;
        end;
      taRightJustify :
        begin
          RWidthOffset := QuietZone;
          LWidthOffset := BCWidth - BarCodeWidth - QuietZone;
        end;
      taCenter :
        begin
          LWidthOffset := (BCWidth - BarCodeWidth) div 2;
          RWidthOffset := (BCWidth - BarCodeWidth) div 2;
        end;
    end;

  { Save the barcode rectangle }
  FBarCodeRect := Rect (LWidthOffset,
                        QuietZone + TopOffset,
                        BCWidth - RWidthOffset,
                        BCHeight - QuietZone - BottomOffset);
  { Draw the barcode }
  DrawBarcode;

  FBitmap.Canvas.Brush.Color := BackgroundColor;
  { Draw the caption }
  FBitmap.Canvas.TextOut (XPos, YPos, Caption);
end;

function TStCustom2DBarcode.GetBarCodeHeight : Integer;
begin
  Result := CalculateBarCodeHeight (Height);
end;

function TStCustom2DBarcode.GetBarCodeWidth : Integer;
begin
  Result := CalculateBarCodeWidth (Width);
end;

procedure TStCustom2DBarcode.GetCurrentResolution (var ResX : Integer;
                                                   var ResY : Integer);
begin
  ResX := GetDeviceCaps (FBitmap.Canvas.Handle, LOGPIXELSX);
  ResY := GetDeviceCaps (FBitmap.Canvas.Handle, LOGPIXELSY);
end;

function TStCustom2DBarcode.GetVersion : string;
begin
  Result := StVersionStr;
end;

procedure TStCustom2DBarcode.Paint;
begin
  GenerateBarcodeBitmap (Width, Height);
  Canvas.CopyRect (Rect (0, 0, Width, Height),
                   FBitmap.Canvas,
                   Rect (0, 0, Width, Height));
end;

procedure TStCustom2DBarcode.PaintToCanvas (ACanvas  : TCanvas;
                                            Position : TPoint);
begin
  PaintToDC (ACanvas.Handle, Position);
end;

procedure TStCustom2DBarcode.PaintToCanvasRes (ACanvas  : TCanvas;
                                               Position : TPoint;
                                               ResX     : Integer;
                                               ResY     : Integer);
begin
  PaintToDCRes (ACanvas.Handle, Position, ResX, ResY);
end;

procedure TStCustom2DBarcode.PaintToCanvasSize (ACanvas : TCanvas;
                                                X, Y, H : Double);
var
  PixelsPerInchX : Integer;
  PixelsPerInchY : Integer;

begin
  {get some information about this device context}
  PixelsPerInchX := GetDeviceCaps (ACanvas.Handle, LOGPIXELSX);
  PixelsPerInchY := GetDeviceCaps (ACanvas.Handle, LOGPIXELSY);

  PaintToCanvasRes (ACanvas,
                    Point (Round (PixelsPerInchX * X),
                           Round (PixelsPerInchY * Y)),
                    Round (PixelsPerInchX * H),
                    Round (PixelsPerInchY * H));
end;

procedure TStCustom2DBarcode.PaintToDC (DC : hDC; Position : TPoint);
var
  NewResX : Integer;
  NewResY : Integer;
  
begin
  NewResX := GetDeviceCaps (DC, LOGPIXELSX);
  NewResY := GetDeviceCaps (DC, LOGPIXELSY);
  PaintToDCRes (DC, Position, NewResX, NewResY);
end;

procedure TStCustom2DBarcode.PaintToDCRes (DC : hDC; Position : TPoint;
                                           ResX : Integer; ResY : Integer);
var
  ACanvas   : TCanvas;
  R1        : TRect;
  R2        : TRect;
  RenderBmp : TBitmap;
  SizeX     : Integer;
  SizeY     : Integer;

begin
  ACanvas := TCanvas.Create;
  ACanvas.Handle := DC;
  try
    RenderBmp := TBitmap.Create;
    try
      {this is necessary because of a Delphi buglet}
      RenderBmp.Canvas.Font.PixelsPerInch := ResY;
      {use our font}
      RenderBmp.Canvas.Font := Font;

      RenderToResolution (RenderBmp, ResX, ResY, SizeX, SizeY);
      R1 := Rect (0, 0, RenderBmp.Width, RenderBmp.Height);
      R2 := Rect (Position.X, Position.Y,
                  RenderBmp.Width + Position.X,
                  RenderBmp.Height + Position.Y);

      ACanvas.CopyRect (R2, RenderBmp.Canvas, R1);
    finally
      RenderBmp.Free;
    end;
  finally
    ACanvas.Free;
  end;
end;

procedure TStCustom2DBarcode.PaintToPrinterCanvas (ACanvas  : TCanvas;
                                                   Position : TPoint);
begin
  PaintToPrinterDC (ACanvas.Handle, Position);
end;

procedure TStCustom2DBarcode.PaintToPrinterCanvasRes (ACanvas  : TCanvas;
                                                      Position : TPoint;
                                                      ResX     : Integer;
                                                      ResY     : Integer);
begin
  PaintToPrinterDCRes (ACanvas.Handle, Position, ResX, ResY);
end;

procedure TStCustom2DBarcode.PaintToPrinterCanvasSize (ACanvas : TCanvas;
                                                       X, Y, H : Double);
var
  PixelsPerInchX : Integer;
  PixelsPerInchY : Integer;

begin
  {get some information about this device context}
  PixelsPerInchX := GetDeviceCaps (ACanvas.Handle, LOGPIXELSX);
  PixelsPerInchY := GetDeviceCaps (ACanvas.Handle, LOGPIXELSY);

  PaintToPrinterCanvasRes (ACanvas,
                           Point (Round (PixelsPerInchX * X),
                                  Round (PixelsPerInchY * Y)),
                           Round (PixelsPerInchX * H),
                           Round (PixelsPerInchY * H));
end;

procedure TStCustom2DBarcode.PaintToPrinterDC (DC : hDC; Position : TPoint);
var
  NewResX : Integer;
  NewResY : Integer;

begin
  NewResX := GetDeviceCaps (DC, LOGPIXELSX);
  NewResY := GetDeviceCaps (DC, LOGPIXELSY);
  PaintToPrinterDCRes (DC, Position, NewResX, NewResY);
end;

procedure TStCustom2DBarcode.PaintToPrinterDCRes (DC       : hDC;
                                                  Position : TPoint;
                                                  ResX     : Integer;
                                                  ResY     : Integer);
var
  ACanvas   : TCanvas;
  R2        : TRect;
  Info      : PBitMapInfo;
  InfoSize  : DWORD;
  ImageSize : DWORD;
  Image     : Pointer;
  RenderBmp : TBitmap;
  SizeX     : Integer;
  SizeY     : Integer;

begin
  (* ---------- to do: fix me for Lazarus ---------
  ACanvas := TCanvas.Create;
  ACanvas.Handle := DC;
  try
    RenderBmp := TBitmap.Create;
    try
      {this is necessary because of a Delphi buglet}
      RenderBmp.Canvas.Font.PixelsPerInch := ResY;
      {use our font}
      RenderBmp.Canvas.Font.Assign (Font);

      RenderToResolution (RenderBmp, ResX, ResY, SizeX, SizeY);
      R2 := Rect (Position.X, Position.Y,
                  SizeX + Position.X,
                  SizeY + Position.Y);

      {Delphi does not allow a simple Canvas.CopyRect to the printer Canvas}
      with RenderBmp do begin
        GetDIBSizes (Handle, InfoSize, ImageSize);
        GetMem (Info, InfoSize);
        try
          GetMem (Image, ImageSize);
          try
            GetDIB (Handle, Palette, Info^, Image^);
            with Info^.bmiHeader do begin
              StretchDIBits (ACanvas.Handle,
                             R2.Left, R2.Top, SizeX, SizeY,
                             0, 0, biWidth, biHeight,
                             Image, Info^, DIB_RGB_COLORS, SRCCOPY);
            end;
          finally
             FreeMem (Image, ImageSize)
          end;
        finally
          FreeMem (Info, InfoSize);
        end;
      end;
    finally
      RenderBmp.Free;
    end;
  finally
    ACanvas.Free;
  end;
  *)
end;
                                                  
procedure TStCustom2DBarcode.SaveToFile (const FileName : string);
begin
  GenerateBarcodeBitmap (Width, Height);
  FBitmap.SaveToFile (FileName);
end;

procedure TStCustom2DBarcode.SaveToFileRes (const FileName : string;
                                            ResX : Integer; ResY : Integer);
var
  RenderBmp : TBitmap;
  SizeX     : Integer;
  SizeY     : Integer;

begin
  RenderBmp := TBitmap.Create;
  try
    RenderToResolution (RenderBmp, ResX, ResY, SizeX, SizeY);
    RenderBmp.SaveToFile (FileName);
  finally
    RenderBmp.Free;
  end;
end;                         

procedure TStCustom2DBarcode.SetAlignment (const v : TAlignment);
var
  OldAlignment : TAlignment;

begin
  if v <> FAlignment then begin
    OldAlignment := FAlignment;
    try
      FAlignment := v;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FAlignment := OldAlignment;
        try
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetBackgroundColor (const v : TColor);
var
  OldBackgroundColor : TColor;

begin
  if v <> FBackgroundColor then begin
    OldBackgroundColor := FBackgroundColor;
    try
      FBackgroundColor := v;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FBackgroundColor := OldBackgroundColor;
        try
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetBarHeight (const v : Integer);
var
  OldBarHeight : Integer;

begin
  if v <> FBarHeight then begin
    OldBarHeight := FBarHeight;
    try
      FBarHeight := v;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FBarHeight := OldBarHeight;
        try
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetBarHeightToWidth (const v : Integer);
var
  OldBarHeightToWidth : Integer;

begin
  if v <> FBarHeightToWidth then begin
    if v < 0 then
      raise E2DBarcodeError.Create (StEBadBarHeightToWidth);
    OldBarHeightToWidth := FBarHeightToWidth;
    try
      FBarHeightToWidth := v;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FBarHeightToWidth := OldBarHeightToWidth;
        try
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetBarWidth (const v : Integer);
var
  OldBarWidth : Integer;

begin
  if v <> FBarWidth then begin
    OldBarWidth := FBarWidth;
    try
      FBarWidth := v;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FBarWidth := OldBarWidth;
        try
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetBitmap (const v : TBitmap);
begin
  FBitmap.Assign (v);
  Invalidate;
end;

procedure TStCustom2DBarcode.SetCaption (const v : string);
var
  OldCaption : string;

begin
  if v <> FCaption then begin
    OldCaption := FCaption;
    try
      FCaption := v;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FCaption := OldCaption;
        try
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetCaptionLayout (const v : TTextLayout);
var
  OldCaptionLayout : TTextLayout;

begin
  if v <> FCaptionLayout then begin
    OldCaptionLayout := FCaptionLayout;
    try
      FCaptionLayout := v;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FCaptionLayout := OldCaptionLayout;
        try
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetCode (const v : string);
var
  OldCode : string;

begin
  if v <> FCode then begin
    OldCode := FCode;
    try
      FCode := v;
      GenerateCodewords;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FCode := OldCode;
        try
          GenerateCodewords;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetECCLevel (const v : Integer);
var
  OldECCLevel : Integer;

begin
  if v <> FECCLevel then begin
    OldECCLevel := FECCLevel;
    try
      FECCLevel := v;
      GenerateCodewords;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FECCLevel := OldECCLevel;
        try
          GenerateCodewords;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetExtendedSyntax (const v : Boolean);
var
  OldExtendedSyntax : Boolean;

begin
  if v <> FExtendedSyntax then begin
    OldExtendedSyntax := FExtendedSyntax;
    try
      FExtendedSyntax := v;
      GenerateCodewords;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FExtendedSyntax := OldExtendedSyntax;
        try
          GenerateCodewords;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetQuietZone (const v : Integer);
var
  OldQuietZone : Integer;

begin
  if v <> FQuietZone then begin
    if (v < 0) then
      raise E2DBarcodeError.Create (StEBadQuietZone);
    OldQuietZone := FQuietZone;
    try
      FQuietZone := v;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FQuietZone := OldQuietZone;
        try
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetRelativeBarHeight (const v : Boolean);
var
  OldRelativeBarHeight : Boolean;

begin
  if v <> FRelativeBarHeight then begin
    OldRelativeBarHeight := FRelativeBarHeight;
    try
      FRelativeBarHeight := v;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FRelativeBarHeight := OldRelativeBarHeight;
        try
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStCustom2DBarcode.SetVersion(const Value : string);
begin
end;

{ TStPDF417Barcode }

constructor TStPDF417Barcode.Create (AOwner : TComponent);
begin
  inherited Create (AOwner);

  FNumCodewords     := 1;
  FTruncated        := False;
  FHighlight        := False;
  FECCLevel         := -1;
  FNumRows          := 0;
  FNumColumns       := 0;
  FTotalCodewords   := FNumRows * FNumColumns;
  FUsedCodewords    := 0;
  FUsedECCCodewords := 0;
  FFreeCodewords    := FTotalCodewords;
  Width             := 273;
  Height            := 81;

  GenerateCodewords;
  GenerateBarcodeBitmap (Width, Height);
end;

procedure TStPDF417Barcode.AddCodeword (Value : Word);
begin
  FCodewords[FNumCodewords] := Value;
  Inc (FNumCodewords);
end;

function TStPDF417Barcode.CalculateBarCodeWidth (
                                        PaintableWidth : Integer) : Integer;
var
  XSize : Integer;
  YSize : Integer;

begin
  CalculateSize (XSize, YSize);
  if Truncated then
    Result := (XSize + 2) * 17 * BarWidth + BarWidth
  else
    Result := (XSize + 4) * 17 * BarWidth + BarWidth;
end;

function TStPDF417Barcode.CalculateBarCodeHeight (
                                        PaintableHeight : Integer) : Integer;
var
  XSize : Integer;
  YSize : Integer;
  
begin
  CalculateSize (XSize, YSize);
  if RelativeBarHeight then
    Result := PaintableHeight
  else if BarHeightToWidth <> 0 then
    Result := (BarHeightToWidth * BarWidth) * YSize
  else
    Result := BarHeight * YSize;
end;

procedure TStPDF417Barcode.CalculateECC (NumCodewords : Integer;
                                         ECCLen       : Integer);

const
  StMods  : array [0..64] of array [0..64] of Integer = 
    ((  0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (917,  27,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (890, 351, 200,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (809, 723, 568, 522,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (566, 155, 460, 919, 427,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (766,  17, 803,  19, 285, 861,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (437, 691, 784, 597, 537, 925,  76,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (379, 428, 653, 646, 284, 436, 308, 237, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (205, 441, 501, 362, 289, 257, 622, 527, 
      567,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (612, 266, 691, 818, 841, 826, 244,  64, 
      457, 377,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (904, 602, 327,  68,  15, 213, 825, 708, 
      565,  45, 462,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (851,  69,   7, 388, 127, 347, 684, 646, 
      201, 757, 864, 597,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (692, 394, 184, 204, 678, 592, 322, 583, 
      606, 384, 342, 713, 764,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (215, 105, 833, 691, 915, 478, 354, 274, 
      286, 241, 187, 154, 677, 669,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (642, 868, 147, 575, 550,  74,  80,   5, 
      230, 664, 904, 109, 476, 829, 460,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     ( 65, 176,  42, 295, 428, 442, 116, 295, 
      132, 801, 524, 599, 755, 232, 562, 274, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (192,  70,  98,  55, 733, 916, 510, 163, 
      437, 843,  61, 259, 650, 430, 298, 115, 
      425,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (573, 760, 756, 233, 321, 560, 202, 312, 
      297, 120, 739, 275, 855,  37, 624, 315, 
      577, 279,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (787, 754, 821, 371,  17, 508, 201, 806, 
      177, 506, 407, 491, 249, 923, 181,  75, 
      170, 200, 250,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (500, 632, 880, 710, 375, 274, 258, 717, 
      176, 802, 109, 736, 540,  64,  45, 152, 
       12, 647, 448, 712,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (568, 259, 193, 165, 347, 691, 310, 610, 
      624, 693, 763, 716, 422, 553, 681, 425, 
      129, 534, 781, 519, 108,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (772,   6,  76, 519, 563, 875,  66, 678,
      578, 716, 927, 296, 633, 244, 155, 928, 
      432, 838,  95,  55,  78, 665,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (455, 538,  32, 581, 473, 772, 462, 194, 
      251, 503, 631,   1, 630, 247, 843, 101, 
      749, 457, 143, 597, 294,  93,  78,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (433, 747, 273, 806, 697, 585, 200, 249, 
      628, 555, 713,  54, 608, 322,  54, 135, 
      385, 701, 308, 238, 166, 128, 819, 142, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (367,  39, 208, 439, 454, 104, 608,  55, 
      916, 912, 314, 375, 760, 141, 169, 287, 
      765, 374, 492, 348, 251, 320, 732, 899, 
      847,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (169, 764, 847, 131, 858, 325, 454, 441, 
      245, 699, 893, 446, 830, 159, 121, 269, 
      608, 331, 760, 477,  93, 788, 544, 887, 
      284, 443,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (504, 710, 383, 531, 151, 694, 636, 175, 
      269,  93,  21, 463, 671, 438, 433, 857, 
      610, 560, 165, 531, 100, 357, 688, 114,
      149, 825, 694,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (580, 925, 461, 840, 560,  93, 427, 203, 
      563,  99, 586, 201, 557, 339, 277, 321, 
      712, 470, 920,  65, 509, 525, 879, 378, 
      452,  72, 222, 720,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (808, 318, 478,  42, 706, 500, 264,  14, 
      397, 261, 862,  33, 864,  62, 462, 305, 
      509, 231, 316, 800, 465, 452, 738, 126, 
      239,   9, 845, 241, 656,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (563, 235, 604, 915, 635, 324, 392, 364, 
      683, 541,  89, 655, 211, 194, 136, 453, 
      104,  12, 390, 487, 484, 794, 549, 471, 
       26, 910, 498, 383, 138, 926,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (757, 764, 673, 108, 706, 886,  76, 234, 
      695, 196,  66, 270,   8, 252, 612, 825, 
      660, 679, 860, 898, 204, 861, 371, 142, 
      358, 380, 528, 379, 120, 757, 347,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (410,  63, 330, 685, 390, 231, 133, 803, 
      320, 571, 800, 593, 147, 263, 494, 273, 
      517, 193, 284, 687, 742, 677, 742, 536, 
      321, 640, 586, 176, 525, 922, 575, 361, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (575, 871, 311, 454, 504, 870, 199, 768, 
      634, 362, 548, 855, 529, 384, 830, 923, 
      222,  85, 841,  59, 518, 590, 358, 110, 
      695, 864, 699, 581, 642, 175, 836, 855, 
      709, 274, 686, 244,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  5,  10, 156, 729, 684, 324,  60, 264, 
       99, 261,  89, 460, 742, 208, 699, 670, 
      512, 404, 726, 389, 492, 287, 894, 571, 
       41, 203, 353, 256, 243, 784, 385, 555, 
      595, 734, 714, 565, 205, 706, 316, 115, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (285,  82, 730, 339, 436, 572, 271, 103, 
      758, 231, 560,  31, 213, 272, 267, 569, 
      773,   3,  21, 446, 706, 413,  97, 376, 
       60, 714, 436, 417, 405, 632,  25, 109, 
      876, 470, 915, 157, 840, 764,  64, 678, 
      848, 659,  36, 476,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (669, 912, 896, 252, 338, 162, 414, 632, 
      626, 252, 869, 185, 444,  82, 920, 783, 
      565, 875, 126, 877, 524, 603, 189, 136, 
      373, 540, 649, 271, 836, 540, 199, 323, 
      888, 486,  92, 849, 162, 701, 178, 926, 
      498, 575, 765, 422, 450, 302, 354, 710, 
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (187,  57,  15, 317, 835, 593,   8, 158, 
       95, 145,  37, 659, 576, 386, 884, 913, 
      495, 869, 908, 296, 437, 215,  33, 883, 
      877, 477, 712, 578, 349,  13, 174, 839, 
      914, 107, 260,  40, 532, 210, 395, 905, 
      163, 785, 693, 627, 393, 687, 112, 481, 
      717, 297,  37, 483,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (163, 726, 626, 653, 414, 537, 467, 579, 
      729, 396, 142, 598, 860, 774, 518, 461, 
      136, 687, 827, 614, 841, 468, 207, 481, 
      649, 910, 497, 686, 186, 235, 845, 863, 
      821, 711, 663, 534, 393, 756, 467, 224, 
      442, 520, 210, 732, 864, 729, 433, 735, 
       70, 184, 278,  97, 492,  17,   2, 338, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0,
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     ( 77, 611, 467, 704, 555, 579, 802, 773, 
      303, 518, 560, 196, 314, 102,   5, 845, 
      248, 125, 836, 923,  88, 630, 886, 619, 
       37, 141, 409, 229,  77, 658, 450, 449, 
       93, 651, 276, 501, 166,  75, 630, 701, 
      388,  72, 830, 166, 187, 131, 711, 577, 
      834, 147, 361, 517,  76, 581,  45, 495, 
      366, 278, 781,  61,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (  0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0, 
        0,   0,   0,   0,   0,   0,   0,   0,   0),
     (543, 264, 623, 843, 381,   4, 629, 840, 
      771, 280,  97, 404,  83, 717, 733, 648, 
      502, 488, 201, 651, 158, 605, 352, 517, 
      535, 225, 594, 460,  31, 519,  35, 440, 
      184, 283, 762, 672, 400, 511, 376, 543, 
      822, 858, 609, 430, 172, 462, 476, 723, 
      612, 381, 877, 733, 505, 107, 287, 610, 
      106, 453, 771, 862,  93,   6, 422, 539,   0));

  StMods128 : array [0..127] of Integer =
    (521, 310, 864, 547, 858, 580, 296, 379,
      53, 779, 897, 444, 400, 925, 749, 415,
     822,  93, 217, 208, 928, 244, 583, 620,
     246, 148, 447, 631, 292, 908, 490, 704,
     516, 258, 457, 907, 594, 723, 674, 292,
     272,  96, 684, 432, 686, 606, 860, 569,
     193, 219, 129, 186, 236, 287, 192, 775,
     278, 173,  40, 379, 712, 463, 646, 776,
     171, 491, 297, 763, 156, 732,  95, 270,
     447,  90, 507,  48, 228, 821, 808, 898,
     784, 663, 627, 378, 382, 262, 380, 602,
     754, 336,  89, 614,  87, 432, 670, 616,
     157, 374, 242, 726, 600, 269, 375, 898,
     845, 454, 354, 130, 814, 587, 804,  34,
     211, 330, 539, 297, 827, 865,  37, 517,
     834, 315, 550,  86, 801,   4, 108, 539);

  StMods256 : array [0..255] of Integer =
    (524, 894,  75, 766, 882, 857,  74, 204,
      82, 586, 708, 250, 905, 786, 138, 720, 
     858, 194, 311, 913, 275, 190, 375, 850, 
     438, 733, 194, 280, 201, 280, 828, 757, 
     710, 814, 919,  89,  68, 569,  11, 204, 
     796, 605, 540, 913, 801, 700, 799, 137, 
     439, 418, 592, 668, 353, 859, 370, 694, 
     325, 240, 216, 257, 284, 549, 209, 884, 
     315,  70, 329, 793, 490, 274, 877, 162, 
     749, 812, 684, 461, 334, 376, 849, 521, 
     307, 291, 803, 712,  19, 358, 399, 908, 
     103, 511,  51,   8, 517, 225, 289, 470, 
     637, 731,  66, 255, 917, 269, 463, 830, 
     730, 433, 848, 585, 136, 538, 906,  90, 
       2, 290, 743, 199, 655, 903, 329,  49, 
     802, 580, 355, 588, 188, 462,  10, 134, 
     628, 320, 479, 130, 739,  71, 263, 318, 
     374, 601, 192, 605, 142, 673, 687, 234, 
     722, 384, 177, 752, 607, 640, 455, 193, 
     689, 707, 805, 641,  48,  60, 732, 621, 
     895, 544, 261, 852, 655, 309, 697, 755, 
     756,  60, 231, 773, 434, 421, 726, 528, 
     503, 118,  49, 795,  32, 144, 500, 238,
     836, 394, 280, 566, 319,   9, 647, 550, 
      73, 914, 342, 126,  32, 681, 331, 792, 
     620,  60, 609, 441, 180, 791, 893, 754, 
     605, 383, 228, 749, 760, 213,  54, 297, 
     134,  54, 834, 299, 922, 191, 910, 532, 
     609, 829, 189,  20, 167,  29, 872, 449, 
      83, 402,  41, 656, 505, 579, 481, 173, 
     404, 251, 688,  95, 497, 555, 642, 543, 
     307, 159, 924, 558, 648,  55, 497,  10);

  StMods512 : array [0..511] of Integer =
    (352,  77, 373, 504,  35, 599, 428, 207,
     409, 574, 118, 498, 285, 380, 350, 492,
     197, 265, 920, 155, 914, 299, 229, 643,
     294, 871, 306,  88,  87, 193, 352, 781,
     846,  75, 327, 520, 435, 543, 203, 666,
     249, 346, 781, 621, 640, 268, 794, 534,
     539, 781, 408, 390, 644, 102, 476, 499,
     290, 632, 545,  37, 858, 916, 552,  41,
     542, 289, 122, 272, 383, 800, 485,  98,
     752, 472, 761, 107, 784, 860, 658, 741,
     290, 204, 681, 407, 855,  85,  99,  62,
     482, 180,  20, 297, 451, 593, 913, 142,
     808, 684, 287, 536, 561,  76, 653, 899,
     729, 567, 744, 390, 513, 192, 516, 258,
     240, 518, 794, 395, 768, 848,  51, 610,
     384, 168, 190, 826, 328, 596, 786, 303,
     570, 381, 415, 641, 156, 237, 151, 429,
     531, 207, 676, 710,  89, 168, 304, 402,
      40, 708, 575, 162, 864, 229,  65, 861,
     841, 512, 164, 477, 221,  92, 358, 785,
     288, 357, 850, 836, 827, 736, 707,  94,
       8, 494, 114, 521,   2, 499, 851, 543, 
     152, 729, 771,  95, 248, 361, 578, 323, 
     856, 797, 289,  51, 684, 466, 533, 820, 
     669,  45, 902, 452, 167, 342, 244, 173, 
      35, 463, 651,  51, 699, 591, 452, 578, 
      37, 124, 298, 332, 552,  43, 427, 119, 
     662, 777, 475, 850, 764, 364, 578, 911, 
     283, 711, 472, 420, 245, 288, 594, 394, 
     511, 327, 589, 777, 699, 688,  43, 408, 
     842, 383, 721, 521, 560, 644, 714, 559, 
      62, 145, 873, 663, 713, 159, 672, 729,
     624,  59, 193, 417, 158, 209, 563, 564, 
     343, 693, 109, 608, 563, 365, 181, 772, 
     677, 310, 248, 353, 708, 410, 579, 870, 
     617, 841, 632, 860, 289, 536,  35, 777, 
     618, 586, 424, 833,  77, 597, 346, 269, 
     757, 632, 695, 751, 331, 247, 184,  45, 
     787, 680,  18,  66, 407, 369,  54, 492, 
     228, 613, 830, 922, 437, 519, 644, 905, 
     789, 420, 305, 441, 207, 300, 892, 827, 
     141, 537, 381, 662, 513,  56, 252, 341, 
     242, 797, 838, 837, 720, 224, 307, 631, 
      61,  87, 560, 310, 756, 665, 397, 808, 
     851, 309, 473, 795, 378,  31, 647, 915, 
     459, 806, 590, 731, 425, 216, 548, 249, 
     321, 881, 699, 535, 673, 782, 210, 815, 
     905, 303, 843, 922, 281,  73, 469, 791, 
     660, 162, 498, 308, 155, 422, 907, 817, 
     187,  62,  16, 425, 535, 336, 286, 437, 
     375, 273, 610, 296, 183, 923, 116, 667, 
     751, 353,  62, 366, 691, 379, 687, 842, 
      37, 357, 720, 742, 330,   5,  39, 923, 
     311, 424, 242, 749, 321,  54, 669, 316, 
     342, 299, 534, 105, 667, 488, 640, 672, 
     576, 540, 316, 486, 721, 610,  46, 656, 
     447, 171, 616, 464, 190, 531, 297, 321,
     762, 752, 533, 175, 134,  14, 381, 433,
     717,  45, 111,  20, 596, 284, 736, 138,
     646, 411, 877, 669, 141, 919,  45, 780,
     407, 164, 332, 899, 165, 726, 600, 325,
     498, 655, 357, 752, 768, 223, 849, 647,
      63, 310, 863, 251, 366, 304, 282, 738,
     675, 410, 389, 244,  31, 121, 303, 263);

var
  BaseReg  : array [0..800] of DWord;
  CoeffReg : array [0..800] of DWord;
  i        : Integer;
  j        : Integer;
  TInt     : Integer;
  Temp     : DWord;
  Wrap     : DWord;

begin
  if ECClen < 128 then
    for i := 0 to ECCLen - 1 do
      CoeffReg[i] := StMods[ECClen][i]
  else begin
    if ECClen = 128 then
      for i := 0 to ECCLen - 1 do
        CoeffReg[i] := StMods128[i]
    else if ECClen = 256 then
      for i := 0 to ECCLen - 1 do
        CoeffReg[i] := StMods256[i]
    else if ECClen = 512 then
      for i := 0 to ECCLen - 1 do
        CoeffReg[i] := StMods512[i];
  end;

  for i := 0 to ECCLen - 1 do
    BaseReg[i] := 0;

  for i := NumCodewords to NumCodewords + ECCLen - 1 do
    FCodewords[i] := 0;

  for i := 0 to NumCodewords - 1 do begin
    wrap := (BaseReg[ECClen - 1] + FCodewords[i]) mod 929;
    for j := ECCLen - 1 downto 1 do begin
      temp := (CoeffReg[ECClen - 1 - j] * wrap) mod 929;
      temp := (929 - temp) mod 929;
      BaseReg[j] := (BaseReg[j - 1] + temp) mod 929;
    end;
    temp := (CoeffReg[ECClen - 1] * wrap) mod 929;      
    temp := (929 - temp) mod 929;
    BaseReg[0]:= temp;
  end;

  for j := 0 to ECCLen - 1 do
    BaseReg[j] := (929 - BaseReg[j]) mod 929;

  for j := 0 to ECCLen - 1 do begin
    tint := BaseReg[ECClen - 1 - j];
    FCodewords [NumCodewords + j] := tint;
  end;
end;

procedure TStPDF417Barcode.CalculateSize (var XSize : Integer;
                                          var YSize : Integer);
var
  i                 : Integer;
  NumErrorCodewords : Integer;
  ErrorLevel        : Integer;
  j                 : Integer;
                                            
begin
  { Set the error correction level automatically if needed }
  ErrorLevel := GetRealErrorLevel;

  NumErrorCodewords := Trunc (Power (2, ErrorLevel + 1));

  XSize := NumColumns;
  YSize := NumRows;

  FTotalCodewords := XSize * YSize;

  { Adjust the size if necessary }
  if (NumRows <= 0) or (NumColumns <= 0) then begin
    if NumRows > 0 then begin
      i := 1;
      while i <= 30 do begin
        if i * NumRows - NumErrorCodewords > FNumCodewords then
          Break;
        Inc (i);
      end;
      FTotalCodewords := YSize * 30;
      XSize := i;
    end else if NumColumns > 0 then begin
      i := 3;
      while i <= 90 do begin
        if i * NumColumns - NumErrorCodewords > FNumCodewords then
          Break;
        Inc (i);
      end;
      YSize := i;
      FTotalCodewords := XSize * 90;
    end else begin
      i := 1;
      j := 3;
      while (i * j - NumErrorCodewords < FNumCodewords) do begin
        if j < 90 then
          Inc (j);
        if (i < 30) and (i * j - NumErrorCodewords < FNumCodewords) then
          Inc (i);
        if (j >= 90) and (i >= 30) then
          Break;
      end;
      XSize := i;
      YSize := J;
      FTotalCodewords := 900;
    end;
  end;
end;

function TStPDF417Barcode.CodewordToBitmask (RowNumber : Integer;
                                             Codeword  : Integer) : DWord;
begin
  if (Codeword < 0) or (CodeWord > 929) then
    raise E2DBarcodeError.Create (StEInvalidCodeword);
  Result := StPDF417Codewords[RowNumber mod 3][Codeword];
end;

procedure TStPDF417Barcode.ConvertBytesToBase900 (const S : array of Byte;
                                                  var A   : array of Integer);
var
  i        : Integer;
  D        : array [0..5] of Byte;
  Dividend : Integer;
  Digits   : array [0..4] of Integer;
  SP       : Integer;

begin
//  Assert(length(S) = 6,
//    'ConvertBytesToBase900: there should be 6 bytes in the input byte array');
//  Assert(length(A) = 5,
//    'ConvertBytesToBase900: there should be 5 elements in the output digit array');

  {copy the array of bytes}
  for i := 0 to 5 do
    D[i] := S[i];

  {loop until the entire base 256 value has been converted to an array
   of base 900 digits (6 base 256 digits will convert to 5 base 900
   digits)}
  SP := 0;
  while (SP < 5) do begin
    Dividend := 0;
    for i := 0 to 5 do begin
     {notes: at the start of the loop, Dividend will always be in the
               range 0..899--it starts out as zero and the final
               statement in the loop forces it into that range
             the first calculation sets Dividend to 0..230399
             the second calc sets D[i] to 0..255 (with no possibility
               of overflow)
             the third calc sets Dividend to 0..899 again}
      Dividend := (Dividend shl 8) + D[i];
      D[i] := Dividend div 900;
      Dividend := Dividend mod 900;
    end;

    Digits[SP] := Dividend;
    inc(SP);
  end;

  {pop the base 900 digits and enter them into the array of integers}
  i := 0;
  while (SP > 0) do begin
    dec(SP);
    A[i] := Digits[SP];
    inc(i);
  end;
end;

procedure TStPDF417Barcode.ConvertToBase900 (const S  : string;
                                             var A    : array of Integer;
                                             var LenA : Integer);
var
  D          : string;
  i          : Integer;
  LenD       : Integer;
  Dividend   : Integer;
  Rem        : Integer;
  Done       : Boolean;
  FirstDigit : Integer;
  Digits     : array [0..14] of Integer;
                             // 15 base 900 digits = 45 base 10 digits
  SP         : Integer;
  
begin
  {Assert: S must be non-empty
           it must contain just the ASCII characters '0' to '9' (so no
             leading/trailing spaces either)
           it must have a maximum length of 45}
  Assert(IsNumericString(S), 'ConvertToBase900: S should be a numeric string');

  {grab the string and calculate its length}
  D := S;
  LenD := length(D);

  {convert the string from ASCII characters into binary digits and in
   the process calculate the first non-zero digit}
  FirstDigit := 0;
  for i := LenD downto 1 do begin
    D[i] := char(ord(D[i]) - ord('0'));
    if (D[i] <> #0) then
      FirstDigit := i;
  end;

  {if the input string comprises just zero digits, return}
  if (FirstDigit = 0) then begin
    LenA := 0;
    Exit;
  end;

  {prepare the stack of base 900 digits}
  SP := 0;

  {loop until the entire base 10 string has been converted to an array
   of base 900 digits}
  Done := false;
  while not Done do begin

    {if we can switch to using standard integer arithmetic, do so}
    if ((LenD - FirstDigit) <= 8) then begin

      {convert the remaining digits to a binary integer}
      Dividend := 0;
      for i := FirstDigit to LenD do
        Dividend := (Dividend * 10) + ord(D[i]);

      {calculate the remaining base 900 digits using the standard
       radix conversion algorithm; push onto the digit stack}
      while (Dividend <> 0) do begin
        Digits[SP] := Dividend mod 900;
        inc(SP);
        Dividend := Dividend div 900;
      end;

      {we've finished}
      Done := true;
    end

    {otherwise operate directly on the base 10 string}
    else begin

      {calculate the remainder base 100}
      Rem := ord(D[LenD]);
      dec(LenD);
      Rem := Rem + (ord(D[LenD]) * 10);
      dec(LenD);

      {calculate the quotient and remainder of the remaining digits,
       dividing by 9}
      Dividend := 0;
      for i := FirstDigit to LenD do begin
        Dividend := (Dividend * 10) + ord(D[i]);
        D[i] := char(Dividend div 9);
        Dividend := Dividend mod 9;
      end;

      {push the base 900 digit onto the stack: it's the remainder base
       9 multiplied by 100, plus the remainder base 100}
      Digits[SP] := (Dividend * 100) + Rem;
      inc(SP);

      {if the first digit is now zero, advance the index to the first
       non-zero digit}
      if (D[FirstDigit] = '0') then
        inc(FirstDigit);
    end;
  end;

  {pop the base 900 digits and enter them into the array of integers}
  i := 0;
  while (SP > 0) do begin
    dec(SP);
    A[i] := Digits[SP];
    inc(i);
  end;
  LenA := i;
end;

procedure TStPDF417Barcode.DrawBarcode;
var
  XSize             : Integer;
  YSize             : Integer;
  i                 : Integer;
  j                 : Integer;
  WorkBarHeight     : Integer;
  CodewordPos       : Integer;
  ErrorLevel        : Integer;
  NumErrorCodewords : Integer;

const
  SymbolPadding = 900;

begin
  { Set the error correction level automatically if needed }
  ErrorLevel := GetRealErrorLevel;

  NumErrorCodewords := Trunc (Power (2, ErrorLevel + 1));

  CalculateSize (XSize, YSize);

  { The first codewords is always the length }
  if FNumCodewords +
     (XSize * YSize - FNumCodewords - NumErrorCodewords) < 0 then
    raise E2DBarcodeError.Create (StECodeTooLarge);
  FCodewords[0] := FNumCodewords +
                  (XSize * YSize - FNumCodewords - NumErrorCodewords);

  CodewordPos := 1; { The first codeword is always the length }

  WorkBarHeight := (BarCodeRect.Bottom - BarCodeRect.Top) div YSize;

  for i := 0 to YSize - 1 do begin
    if FHighlight then
      FBitmap.Canvas.Brush.Color := $ffbbff;
    DrawStartPattern (i, WorkBarHeight);
    if FHighlight then
      FBitmap.Canvas.Brush.Color := $ffffbb;
    DrawLeftRowIndicator (i, WorkBarHeight, YSize, XSize);
    for j := 0 to XSize - 1 do begin
      if (i = 0) and (j = 0) then begin
        if FHighlight then
          FBitmap.Canvas.Brush.Color := $bbffff;
        { Length }
        DrawCodeWordBitmask (i, j + 2, WorkBarHeight,
                             CodeWordToBitmask (i, FNumCodewords +
                      (XSize * YSize - FNumCodewords - NumErrorCodewords)))
      end else if CodewordPos < FNumCodewords then begin
        if FHighlight then
          FBitmap.Canvas.Brush.Color := $bbbbff; 
        { Data }
        DrawCodeWordBitmask (i, j + 2, WorkBarHeight,
                             CodewordToBitmask (i, FCodewords[CodewordPos]));
        Inc (CodewordPos);
      end else if CodewordPos >= XSize * YSize - NumErrorCodeWords then begin
        if FHighlight then
          FBitmap.Canvas.Brush.Color := $ffbbbb; 
        { Error Correction Codes }
        DrawCodeWordBitmask (i, j + 2, WorkBarHeight,
                             CodewordToBitmask (i, FCodewords[CodewordPos]));
        Inc (CodewordPos);
      end else begin
        if FHighlight then
          FBitmap.Canvas.Brush.Color := $bbffbb; 
        { Padding }
        DrawCodewordBitmask (i, j + 2, WorkBarHeight,
                             CodewordToBitmask (i, SymbolPadding));
        Inc (CodewordPos);
      end;
    end;
    if FHighlight then
      FBitmap.Canvas.Brush.Color := $bbddff;
    if Truncated then
      DrawStopPattern (i, XSize + 2, WorkBarHeight)
    else begin
      DrawRightRowIndicator (i, XSize + 2, WorkBarHeight, YSize, XSize);
      if FHighlight then
        FBitmap.Canvas.Brush.Color := $ddaaff; 
      DrawStopPattern (i, XSize + 3, WorkBarHeight);
    end;
  end;
end;

procedure TStPDF417Barcode.DrawCodeword (RowNumber     : Integer;
                                         ColNumber     : Integer;
                                         WorkBarHeight : Integer;
                                         Pattern       : string);

  function GetColumnPosition (ColNumber : Integer) : Integer;
  begin
    Result := ColNumber * StPDF417CellWidth * BarWidth;
  end;

var
  i         : Integer;
  CurPos    : Integer;
  NewPos    : Integer;
  DrawBlock : Boolean;

begin
  if FHighlight then begin
    FBitmap.Canvas.FillRect (
        Rect (BarCodeRect.Left + (GetColumnPosition (ColNumber)),
              BarCodeRect.Top + RowNumber * WorkBarHeight,
              BarCodeRect.Left + 17 * BarWidth + GetColumnPosition (ColNumber),
              BarCodeRect.Top + (RowNumber + 1) * WorkBarHeight));
    FBitmap.Canvas.Brush.Color := Color;
  end;

  CurPos := 0;
  DrawBlock := True;
  for i := 1 to Length (Pattern) do begin
    NewPos := StrToInt (Copy (Pattern, i, 1)) * BarWidth;
    if DrawBlock then
      FBitmap.Canvas.Rectangle (
          BarCodeRect.Left + CurPos + GetColumnPosition (ColNumber),
          BarCodeRect.Top + RowNumber * WorkBarHeight,
          BarCodeRect.Left + CurPos + NewPos + GetColumnPosition (ColNumber),
          BarCodeRect.Top + (RowNumber + 1) * WorkBarHeight);
    CurPos := CurPos + NewPos;
    DrawBlock := not DrawBlock;
  end;
end;

procedure TStPDF417Barcode.DrawCodewordBitmask (RowNumber     : Integer;
                                                ColNumber     : Integer;
                                                WorkBarHeight : Integer;
                                                Bitmask       : DWord);

  function GetColumnPosition (ColNumber : Integer) : Integer;
  begin
    Result := ColNumber * StPDF417CellWidth * BarWidth;
  end;

var
  i : Integer;

begin
  if FHighlight then begin
    FBitmap.Canvas.FillRect (
        Rect (BarCodeRect.Left + (GetColumnPosition (ColNumber)),
              BarCodeRect.Top + RowNumber * WorkBarHeight,
              BarCodeRect.Left + 17 * BarWidth + GetColumnPosition (ColNumber),
              BarCodeRect.Top + (RowNumber + 1) * WorkBarHeight));
    FBitmap.Canvas.Brush.Color := Color;
  end;

  for i := 16 downto 0 do
    if ((BitMask shr i) and $00001) <> 0 then
      FBitmap.Canvas.Rectangle (
          BarCodeRect.Left + (16 - i) * BarWidth +
          GetColumnPosition (ColNumber),
          BarCodeRect.Top + RowNumber * WorkBarHeight,
          BarCodeRect.Left + (17 - i) * BarWidth +
          GetColumnPosition (ColNumber),
          BarCodeRect.Top + (RowNumber + 1) * WorkBarHeight);
end;

procedure TStPDF417Barcode.DrawLeftRowIndicator (RowNumber     : Integer;
                                                 WorkBarHeight : Integer;
                                                 NumRows       : Integer;
                                                 NumCols       : Integer);
var
  CodeWord   : Integer;
  ErrorLevel : Integer;
  
begin
  ErrorLevel := GetRealErrorLevel;
  CodeWord := 0;
  if RowNumber mod 3 = 0 then
    CodeWord := ((RowNumber div 3) * 30) + ((NumRows - 1) div 3)
  else if RowNumber mod 3 = 1 then
    CodeWord := ((RowNumber div 3) * 30) + ((NumRows - 1) mod 3) +
                 (3 * ErrorLevel)
  else if RowNumber mod 3 = 2 then
    CodeWord := (( RowNumber div 3) * 30) + (NumCols - 1);    
  DrawCodeWordBitmask (RowNumber, 1, WorkBarHeight,
                       CodewordToBitmask (RowNumber, Codeword));
end;

procedure TStPDF417Barcode.DrawRightRowIndicator (RowNumber     : Integer;
                                                  ColNumber     : Integer;
                                                  WorkBarHeight : Integer;
                                                  NumRows       : Integer;
                                                  NumCols       : Integer);
var
  Codeword   : Integer;
  ErrorLevel : Integer;
  
begin
  ErrorLevel := GetRealErrorLevel;
  CodeWord := 0;
  if RowNumber mod 3 = 0 then
    Codeword := ((RowNumber div 3) * 30) + (NumCols - 1)  
  else if RowNumber mod 3 = 1 then
    Codeword := ((RowNumber div 3) * 30) + ((NumRows - 1) div 3)
  else if RowNumber mod 3 = 2 then
    Codeword := ((RowNumber div 3) * 30) + ((NumRows - 1) mod 3) +
                (3 * ErrorLevel);
  DrawCodeWordBitmask (RowNumber, ColNumber, WorkBarHeight,
                       CodewordToBitmask (RowNumber, Codeword));
end;

procedure TStPDF417Barcode.DrawStartPattern (RowNumber     : Integer;
                                             WorkBarHeight : Integer);
begin
  DrawCodeword (RowNumber, 0, WorkBarHeight, '81111113');
end;

procedure TStPDF417Barcode.DrawStopPattern (RowNumber     : Integer;
                                            ColNumber     : Integer;
                                            WorkBarHeight : Integer);
begin
  if Truncated then
    DrawCodeWord (RowNumber, ColNumber, WorkBarHeight, '1')
  else
    DrawCodeWord (RowNumber, ColNumber, WorkBarHeight, '711311121');
end;

procedure TStPDF417Barcode.EncodeBinary (var Position : Integer;
                                         CodeLen      : Integer);

  function CountBytes (Position : Integer; CodeLen : Integer) : Integer;
  var
    Done  : Boolean;
    Dummy : Integer;
    
  begin
    Result := 0;
    Done := False;
    while not done do begin
      if (Result < CodeLen) and 
         (not GoodForNumericCompaction (Position + Result, CodeLen, Dummy)) and
         (not GoodForTextCompaction (Position + Result, CodeLen, Dummy)) then
        Inc (Result)
      else
        Done := True;
    end;
  end;

var
  MultipleOfSix  : Boolean;
  BinaryDataSize : Integer;
  i              : Integer;
  j              : Integer;
  A              : array [0..6] of Integer; 

const
  Even6Bytes = 924;
  Odd6Bytes  = 901;

begin
  BinaryDataSize := CountBytes (Position, CodeLen);
  if BinaryDataSize mod 6 = 0 then
    MultipleOfSix := True
  else
    MultipleOfSix := False;
  if MultipleOfSix then
    AddCodeword (Even6Bytes)
  else
    AddCodeword (Odd6Bytes);

  i := 0;
  while i < BinaryDataSize do
    if BinaryDataSize - i < 6 then begin
      AddCodeword (Word (Code[Position + i]));
      Inc (i);
    end else begin
      ConvertBytesToBase900 ([Byte (Code[Position + i]),
                              Byte (Code[Position + i + 1]),
                              Byte (Code[Position + i + 2]),
                              Byte (Code[Position + i + 3]),
                              Byte (Code[Position + i + 4]),
                              Byte (Code[Position + i + 5])], A);
      for j := 1 to 5 do
        AddCodeword (A[j - 1]);                                        {!!.dg}
      Inc (i, 6);
    end;
  Inc (Position, BinaryDataSize);                                      {!!.dg}
end;

procedure TStPDF417Barcode.EncodeNumeric (var Position : Integer;
                                          CodeLen      : Integer);

  function CollectDigits (var Position : Integer;
                          CodeLen      : Integer) : string;
  var
    StartPos : Integer;

  const
    MaxDigitChunk = 44;

  begin
    Result := '';
    StartPos := Position;
    while (Position <= CodeLen) and (Position - StartPos < MaxDigitChunk) and
          (Code[Position] >= '0') and (Code[Position] <= '9') do begin
      Inc (Position);
    end;
    if Position - StartPos > 0 then
      Result := '1' + Copy (Code, StartPos, Position - StartPos); 
  end;

var
  NumericString : string;
  A             : array [0..44] of Integer; 
  LenA          : Integer;
  i             : Integer;
  
const
  NumericLatch = 902;

begin
  AddCodeword (NumericLatch);
  repeat
    NumericString := CollectDigits (Position, CodeLen);
    if NumericString <> '' then begin
      ConvertToBase900 (NumericString, A, LenA);
      for i := 0 to LenA do
        AddCodeword (A[i]);
    end;
  until NumericString = '';
end;

procedure TStPDF417Barcode.EncodeText (var Position : Integer;
                                       CodeLen      : Integer);

  function SelectBestTextMode (
        CurChar : TStPDF417TextCompactionData) : TStPDF417TextCompactionMode;
  begin
    if cmAlpha in CurChar.Mode then
      Result := cmAlpha
    else if cmLower in CurChar.Mode then
      Result := cmLower
    else if cmMixed in CurChar.Mode then
      Result := cmMixed
    else if cmPunctuation in CurChar.Mode then
      Result := cmPunctuation
    else
      Result := cmNone;
  end;

  procedure AddTextCharacter (Value : Word);
  begin
    if FNewTextCodeword then
      FCodewords[FNumCodewords] := 30 * Value
    else begin
      FCodewords[FNumCodewords] := FCodewords[FNumCodewords] + Value;
      Inc (FNumCodewords);
    end;
    FNewTextCodeword := not FNewTextCodeword;
  end;

  function ChangeTextSubmode (CurrentMode : TStPDF417TextCompactionMode;
                              NewMode : TStPDF417TextCompactionMode;
                              UseShift : Boolean) : TStPDF417TextCompactionMode;
  const
    LatchAlphaToLower       = 27;
    LatchAlphaToMixed       = 28;
    ShiftAlphaToPunctuation = 29;
    ShiftLowerToAlpha       = 27;
    LatchLowerToMixed       = 28;
    ShiftLowertoPunctuation = 29;
    LatchMixedToPunctuation = 25;
    LatchMixedToLower       = 27;
    LatchMixedToAlpha       = 28;
    ShiftMixedToPunctuation = 29;
    LatchPunctuationToAlpha = 29;
      
  begin
    if UseShift then
      Result := CurrentMode
    else
      Result := NewMode;
        
    case CurrentMode of
      cmAlpha :
        case NewMode of
          cmLower :
            begin
              { Alpha to Lower.  No shift }
              AddTextCharacter (LatchAlphaToLower);
              if UseShift then
                Result := NewMode;
             end;
          cmMixed :
            begin
              { Alpha to Numeric.  No shift }
              AddTextCharacter (LatchAlphaToMixed);
              if UseShift then
                Result := NewMode;
            end;
          cmPunctuation :
            { Alpha to Punctuation }
            if UseShift then
              AddTextCharacter (ShiftAlphaToPunctuation)
            else begin
              AddTextCharacter (LatchAlphaToMixed);
              AddTextCharacter (LatchMixedToPunctuation);
            end;
        end;

      cmLower :
        case NewMode of
          cmAlpha :
            { Lower to Alpha }
            if UseShift then
              AddTextCharacter (ShiftLowerToAlpha)
            else begin
              AddTextCharacter (LatchLowerToMixed);
              AddTextCharacter (LatchMixedToAlpha);
            end;
          cmMixed :
            begin
              { Lower to Mixed.  No shift }
              AddTextCharacter (LatchLowerToMixed);
              if UseShift then
                Result := NewMode;
            end;
          cmPunctuation :
            { Lower to Punctuation }
            if UseShift then
              AddTextCharacter (ShiftLowerToPunctuation)
            else begin
              AddTextCharacter (LatchLowerToMixed);
              AddTextCharacter (LatchMixedToPunctuation);
            end;
        end;
          
      cmMixed :
        case NewMode of
          cmAlpha :
            begin
              { Mixed to Alpha.  No shift }
              AddTextCharacter (LatchMixedToAlpha);
              if UseShift then
                Result := NewMode;
            end;
          cmLower :
            begin
              { Mixed to Lower.  No shift }
              AddTextCharacter (LatchMixedToLower);
              if UseShift then
                Result := NewMode;
            end;
          cmPunctuation :
            { Mixed to Punctuation }
            if UseShift then
              AddTextCharacter (ShiftMixedToPunctuation)
            else
              AddTextCharacter (LatchMixedToPunctuation);
        end;
      cmPunctuation :
        case NewMode of
          cmAlpha :
            begin
              { Punctuation to Alpha.  No shift }
              AddTextCharacter (LatchPunctuationToAlpha);
              if UseShift then
                Result := NewMode;
            end;
          cmLower :
            begin
              { Punctuation to Lower.  No shift }
              AddTextCharacter (LatchPunctuationToAlpha);
              AddTextCharacter (LatchAlphaToLower);
              if UseShift then
                Result := NewMode;
            end;
          cmMixed :
            begin
              { Punctuation to Mixed.  No shift }
              AddTextCharacter (LatchPunctuationToAlpha);
              AddTextCharacter (LatchAlphaToMixed);
              if UseShift then
                Result := NewMode;
            end;
        end;
    end;
  end;

var
  CurrentTextSubmode : TStPDF417TextCompactionMode;
  CurChar            : TStPDF417TextCompactionData;
  UseShift           : Boolean;
  Done               : Boolean;
  Dummy              : Integer;
  NewChar            : Integer;
  Codeword           : Boolean;

const
  EndingPadChar      = 29;

begin
  { Initialize and get the first character }
  FNewTextCodeword := True;
  CurrentTextSubmode := cmAlpha;
  Done := False;

  { get characters until it is necessary to step out of text mode }
  while (Position <= CodeLen) and (CurChar.Value >= 0) and
        (not Done) do begin
    if (Position <= CodeLen) then begin
      GetNextCharacter (NewChar, Codeword, Position, CodeLen);
      CurChar := TStPDF417TextCompaction[NewChar];
    end;

    if Codeword then begin
      { If the text contains an odd number of letters, follow it with a
        trailing 29 }
      if not FNewTextCodeword then
        AddTextCharacter (EndingPadChar);
      FNewTextCodeword := True;
      { Add the codeword }
      AddCodeword (NewChar)
    end else begin
      { Check if the text submode for the current character is different than
        the current text submode }
      if not (CurrentTextSubmode in CurChar.Mode) then begin
        { if the text submode is different, see if it remains different.  If
          it does, use a latch, otherwise just shift }
        if Position < CodeLen then begin
          if not (CurrentTextSubmode in
             TStPDF417TextCompaction[Integer (Code[Position + 1])].Mode) then
            UseShift := False
          else
            UseShift := True;
        end else
          UseShift := True;

        { Add the shift or latch to the text codewords }
        CurrentTextSubmode := ChangeTextSubmode (CurrentTextSubmode,
                                                 SelectBestTextMode (CurChar),
                                                 UseShift);
      end;

      { Add the character to the codeword array }
      AddTextCharacter (CurChar.Value);
    end;
    { If this is a digit and it looks like a good time to switch to
      numeric mode, do so }
    if GoodForNumericCompaction (Position, CodeLen, Dummy) then
      Done := True;
  end;

  { If the text contains an odd number of letters, follow it with a
    trailing 29 }
  if not FNewTextCodeword then
    AddTextCharacter (EndingPadChar);
end;

procedure TStPDF417Barcode.GenerateCodewords;
var
  ErrorLevel        : Integer;
  NumErrorCodewords : Integer;
  XSize             : Integer;
  YSize             : Integer;

begin
  TextToCodewords;

  ErrorLevel := GetRealErrorLevel;

  NumErrorCodewords := Trunc (Power (2, ErrorLevel + 1));

  CalculateSize (XSize, YSize);

  FUsedCodewords := FNumCodewords;
  FUsedECCCodewords := NumErrorCodewords;
  FFreeCodewords := FTotalCodewords - FUsedCodewords;
  
  { The first codewords is always the length }
  if FNumCodewords +
     (XSize * YSize - FNumCodewords - NumErrorCodewords) < 0 then
    raise E2DBarcodeError.Create (StECodeTooLarge);
  FCodewords[0] := FNumCodewords +
                  (XSize * YSize - FNumCodewords - NumErrorCodewords);

  if NumErrorCodeWords + FNumCodeWords <= XSize * YSize then
    CalculateECC (XSize * YSize - NumErrorCodeWords, NumErrorCodewords)
  else
    raise E2DBarcodeError.Create (StECodeTooLarge);
end;

procedure TStPDF417Barcode.GetNextCharacter (var NewChar  : Integer;
                                             var Codeword : Boolean;
                                             var Position : Integer;
                                             CodeLen      : Integer);
var
  WorkNum : Integer;
                                               
begin
  NewChar := 0;
  Codeword := False;

  if Position <= CodeLen then begin
    if (FCode[Position] = '\') and
       (Position < CodeLen) then begin 
      case FCode[Position + 1] of
        '0'..'9' : begin
          try
            NewChar := StrToInt (Copy (FCode, Position + 1, 3));
            Inc (Position, 4);
          except
            NewChar := 0;
            Inc (Position, 4);
          end;
        end;
        'C', 'c' : begin
          try
            Codeword := True;
            NewChar := StrToInt (Copy (FCode, Position + 2, 3));
            Inc (Position, 5);
          except
            NewChar := 0;
            Inc (Position, 5);
          end;
        end;
        'G', 'g' : begin
          WorkNum := StrToInt (Copy (FCode, Position + 1, 6));
          Inc (Position, 8);
          if (WorkNum >= 0) and (WorkNum <= 899) then begin
            AddCodeword (927);
            Codeword := True;
            NewChar := WorkNum;
          end else if (WorkNum >= 900) and (WorkNum < 810900) then begin
            AddCodeword (926);
            AddCodeword ((WorkNum div 900) - 1);
            Codeword := True;
            NewChar := WorkNum mod 900;
          end else if (WorkNum >= 810900) and (WorkNum < 811800) then begin
            AddCodeword (925);
            Codeword := True;
            NewChar := WorkNum;
          end else
            raise E2DBarcodeError.Create (StEGLIOutOfRange);
        end;
        'X', 'x' : begin
          try
            NewChar := StrToInt ('$' + Copy (FCode, Position + 2, 2));
            Inc (Position, 4);
          except
            NewChar := 0;
            Inc (Position, 4);
          end;
        end;
        '\' : begin
          NewChar := Byte (FCode[Position]);
          Inc (Position, 2);
        end;
        else begin
          NewChar := Byte (FCode[Position]);
          Inc (Position);
        end;
      end;   
    end else begin
      NewChar := Byte (FCode[Position]);
      Inc (Position);
    end;
  end;
end;

function TStPDF417Barcode.GetPDF417ECCLevel : TStPDF417ECCLevels;
begin
  case FECCLevel of
    0 : Result := ecLevel0;
    1 : Result := ecLevel1;
    2 : Result := ecLevel2;
    3 : Result := ecLevel3;
    4 : Result := ecLevel4;
    5 : Result := ecLevel5;
    6 : Result := ecLevel6;
    7 : Result := ecLevel7;
    8 : Result := ecLevel8;
  else
    Result := ecAuto;
  end;
end;

function TStPDF417Barcode.GetRealErrorLevel : Integer;
begin
  if (FECCLevel < 0) then begin
    if FNumCodeWords < 41 then
      Result := 2
    else if FNumCodeWords < 161 then
      Result := 3
    else if FNumCodeWords < 321 then
      Result := 4
    else
      Result := 5;
  end else
    Result := FECCLevel
end;

function TStPDF417Barcode.GoodForNumericCompaction (
                                               Position  : Integer;
                                               CodeLen   : Integer;
                                               var Count : Integer) : Boolean;
const
  BytesNeeded = 13;

begin
  Result := False;
  Count := 0;
  while (Position + Count < CodeLen) and
        (Code[Position + Count] >= '0') and
        (Code[Position + Count] <= '9') do
    Inc (Count);
  if Count > BytesNeeded then
    Result := True;
end;

function TStPDF417Barcode.GoodForTextCompaction (
                                             Position  : Integer;
                                             CodeLen   : Integer;
                                             var Count : Integer) : Boolean;

  function IsGoodTextValue (const v : Char) : Boolean;                 {!!.01}
  begin                                                                {!!.01}
    if v > #127 then                                                   {!!.01}
      Result := False                                                  {!!.01}
    else if TStPDF417TextCompaction[Integer (v)].Value >= 0 then       {!!.01}
      Result := True                                                   {!!.01}
    else                                                               {!!.01}
      Result := False;                                                 {!!.01}
  end;                                                                 {!!.01}

const
  BytesNeeded = 5;

begin
  Result := False;
  Count := 0;
  while (Position + Count < CodeLen) and                               {!!.01}
        (IsGoodTextValue (Code[Position + Count])) and                 {!!.01}
        (Count <= BytesNeeded) do                                      {!!.01}
    Inc (Count);
  if (Count > BytesNeeded) or
     ((Position + Count >= CodeLen) and (Count > 0)) then
    Result := True;
end;

procedure TStPDF417Barcode.RenderToResolution (var OutBitmap : TBitmap;
                                               ResX          : Integer;
                                               ResY          : Integer;
                                               var SizeX     : Integer;
                                               var SizeY     : Integer);
var
  OldBarWidth : Integer;
  OldWidth    : Integer;
  OldHeight   : Integer;
  CurResX     : Integer;
  CurResY     : Integer;
  MultX       : Extended;
  MultY       : Extended;

begin
  OldBarWidth := BarWidth;
  OldWidth := Width;
  OldHeight := Height;
  SizeX := Width;
  SizeY := Height;
  try
    if (ResX <> 0) and (ResY <> 0) then begin
      GetCurrentResolution (CurResX, CurResY);
      MultX := ResX / CurResX;
      MultY := ResY / CurResY;
      FBarWidth := Trunc (FBarWidth * MultX);
      FBitmap.Width := Trunc (FBitmap.Width * MultX);
      FBitmap.Height := Trunc (FBitmap.Height * MultY);
      SizeX := FBitmap.Width;
      SizeY := FBitmap.Height;
    end;
    FBitmap.Canvas.Font.PixelsPerInch := OutBitmap.Canvas.Font.PixelsPerInch;
    GenerateBarcodeBitmap (FBitmap.Width, FBitmap.Height);
    OutBitmap.Width := SizeX;
    OutBitmap.Height := SizeY;
    OutBitmap.Canvas.CopyRect (Rect (0, 0, SizeX, SizeY), FBitmap.Canvas,
                               Rect (0, 0, SizeX, SizeY));
  finally
    FBarWidth := OldBarWidth;
    FBitmap.Width := OldWidth;
    FBitmap.Height := OldHeight;
    GenerateBarcodeBitmap (Width, Height);
  end;
end;

procedure TStPDF417Barcode.SetBarHeight (const v : Integer);
begin
  if (v < 1) and (BarHeightToWidth = 0) and (not RelativeBarHeight) then
    raise E2DBarcodeError.Create (StENeedBarHeight);
  if v < 0 then
    raise E2DBarcodeError.Create (StEBadBarWidth);
  inherited SetBarHeight (v);
end;

procedure TStPDF417Barcode.SetBarHeightToWidth (const v : Integer);
begin
  if (v = 0) and (BarHeight = 0) and (not RelativeBarHeight) then
    raise E2DBarcodeError.Create (StENeedBarHeight);
  inherited SetBarHeightToWidth (v);
end;

procedure TStPDF417Barcode.SetBarWidth (const v : Integer);
begin
  if v < 1 then
    raise E2DBarcodeError.Create (StEBadBarHeight);
  inherited SetBarWidth (v);
end;

procedure TStPDF417Barcode.SetPDF417ECCLevel (const v : TStPDF417ECCLevels);
var
  NewLevel : Integer;
  OldLevel : Integer;

begin
  NewLevel := -1;
  case v of
    ecAuto   : NewLevel := -1;
    ecLevel0 : NewLevel := 0;
    ecLevel1 : NewLevel := 1;
    ecLevel2 : NewLevel := 2;
    ecLevel3 : NewLevel := 3;
    ecLevel4 : NewLevel := 4;
    ecLevel5 : NewLevel := 5;
    ecLevel6 : NewLevel := 6;
    ecLevel7 : NewLevel := 7;
    ecLevel8 : NewLevel := 8;
  end;

  if NewLevel <> FECCLevel then begin
    OldLevel := FECCLevel;
    try
      FECCLevel := NewLevel;
      GenerateCodewords;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FECCLevel := OldLevel;
        try
          GenerateCodewords;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStPDF417Barcode.SetRelativeBarHeight (const v : Boolean); 
begin
  if (not v) and (BarHeightToWidth = 0) and (BarHeight = 0) then
    raise E2DBarcodeError.Create (StENeedBarHeight);
  inherited SetRelativeBarHeight (v);
end;

procedure TStPDF417Barcode.SetTruncated (const v : Boolean);
var
  OldTruncated : Boolean;

begin
  if v <> FTruncated then begin
    OldTruncated := FTruncated;
    try
      FTruncated := v;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FTruncated := OldTruncated;
        try
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

function TStPDF417Barcode.IsNumericString (const S : string) : boolean;
var
  i      : integer;
  LenS : integer;
  
begin
  {note: an assertion test for ConvertToBase900}
  Result := false;
  LenS := length(S);
  if (LenS = 0) or (LenS > 45) then
    Exit;
  for i := 1 to LenS do
    if not (('0' <= S[i]) and (S[i] <= '9')) then
      Exit;
  Result := true;
end;

procedure TStPDF417Barcode.SetNumColumns (const v : Integer);
var
  OldNumColumns : Integer;

begin
  if (v < 0) or (v > 30) then
    raise E2DBarcodeError.Create (StEBadNumCols);
  if v <> FNumColumns then begin
    OldNumColumns := FNumColumns;
    try
      if v < 0 then
        FNumColumns := 0
      else
        FNumColumns := v;
      GenerateCodewords;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FNumColumns := OldNumColumns;
        try
          GenerateCodewords;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStPDF417Barcode.SetNumRows (const v : Integer);
var
  OldNumRows : Integer;

begin
  if (v < 0) or (v > 90) then
    raise E2DBarcodeError.Create (StEBadNumRows);
  if v <> FNumRows then begin
    OldNumRows := FNumRows;
    try
      if v < 0 then
        FNumRows := 0
      else
        FNumRows := v;
      GenerateCodewords;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FNumRows := OldNumRows;
        try
          GenerateCodewords;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStPDF417Barcode.TextToCodewords;
var
  i                  : Integer;
  CodeLen            : Integer;
  CurrentMode        : TStDataMode;
  Count              : Integer;
  First              : Boolean;
  
const
  TextCompaction     = 900;
  PadCodeword        = 900;

begin
  First := True;
  for i := 0 to 2700 do
    FCodewords[i] := PadCodeword;
  FNumCodewords := 1; { There will always be a length codeword }
  i := 1;

  CodeLen := Length (Code); 
  if CodeLen = 0 then
    Exit;

  if GoodForNumericCompaction (i, CodeLen, Count) then
    CurrentMode := dmNumeric
  else if GoodForTextCompaction (i, CodeLen, Count) then
    CurrentMode := dmText
  else
    CurrentMode := dmBinary;

  while i < CodeLen do begin
    case CurrentMode of
      dmBinary :
        EncodeBinary (i, CodeLen);
      dmText :
        if First then
          EncodeText (i, CodeLen);
      dmNumeric :
        EncodeNumeric (i, CodeLen);
    end;

    if GoodForNumericCompaction (i, CodeLen, Count) then
      CurrentMode := dmNumeric
    else if GoodForTextCompaction (i, CodeLen, Count) then begin
      if not First then
        AddCodeword (TextCompaction); 
      CurrentMode := dmText;
      EncodeText (i, CodeLen);                                         {!!.01}
    end else
      CurrentMode := dmBinary;
    First := False;
  end;
end;

{ TStMaxiCodeBarcode }

constructor TStMaxiCodeBarcode.Create (AOwner : TComponent);
begin
  inherited Create (AOwner);

  FMode                := cmMode4;
  FHighlight           := False;
  FShowCodewords       := False;
  FShowAll             := False;
  FAutoScale           := True;
  FBarWidth            := 0;
  FBarHeight           := 0;
  FHorPixelsPerMM      := 4;
  FVerPixelsPerMM      := 4;
  FMaxiHexWidth        := 9;
  FMaxiHexHeight       := 9;
  FMaxiHexVOffset      := -2;
  FMaxiHexHOffset      := 4;
  FCarrierCountryCode  := 0;
  FCarrierServiceClass := 0;
  FCarrierPostalCode   := '000000000';
  
  GetSizes;

  Width                := 121;
  Height               := 129;

  GenerateCodewords;
  GenerateBarcodeBitmap (Width, Height);
end;

procedure TStMaxiCodeBarcode.AddCodeword (Value : Integer);
begin
  if FNumCodewords <= 144 then
    FMessage[FNumCodewords] := Value;
  Inc (FNumCodewords);
end;

function TStMaxiCodeBarcode.CalculateBarCodeWidth (
                                        PaintableWidth : Integer) : Integer;
begin
  Result := Round (30 * FMaxiHexWidth + FMaxiHexHOffset);
end;

function TStMaxiCodeBarcode.CalculateBarCodeHeight (
                                        PaintableHeight : Integer) : Integer;
begin
  Result := Round (33 * FMaxiHexHeight + 33 * FMaxiHexVOffset);
end;

procedure TStMaxiCodeBarcode.DrawBarcode;

  function IsBitOn (Value : Byte; Bit : Byte) : Boolean;
  begin
    Result := ((Value shr Bit) and $01) <> $00;
  end;

const
  {
    The MaxBits array is arranged to match the hex layout of the MaxiCode
    Barcode.
    
    -2  identifies the (light) module at the center of the finder pattern,
    -1  identifies modules which are always dark,
     0  identifies modules which are always light, and
    Positive numbers indicate the bitnumber of the cell.
  }
  MaxBits : array [0..32] of array [0..29] of Integer =
(( 122,121,128,127,134,133,140,139,146,145,152,151,158,157,164,163,170,169,176,175,182,181,188,187,194,193,200,199, -1, -1   ),
 (   124,123,130,129,136,135,142,141,148,147,154,153,160,159,166,165,172,171,178,177,184,183,190,189,196,195,202,201,817,  0 ),
 ( 126,125,132,131,138,137,144,143,150,149,156,155,162,161,168,167,174,173,180,179,186,185,192,191,198,197,204,203,819,818   ),
 (   284,283,278,277,272,271,266,265,260,259,254,253,248,247,242,241,236,235,230,229,224,223,218,217,212,211,206,205,820,  0 ),
 ( 286,285,280,279,274,273,268,267,262,261,256,255,250,249,244,243,238,237,232,231,226,225,220,219,214,213,208,207,822,821   ),
 (   288,287,282,281,276,275,270,269,264,263,258,257,252,251,246,245,240,239,234,233,228,227,222,221,216,215,210,209,823,  0 ),
 ( 290,289,296,295,302,301,308,307,314,313,320,319,326,325,332,331,338,337,344,343,350,349,356,355,362,361,368,367,825,824   ),
 (   292,291,298,297,304,303,310,309,316,315,322,321,328,327,334,333,340,339,346,345,352,351,358,357,364,363,370,369,826,  0 ),
 ( 294,293,300,299,306,305,312,311,318,317,324,323,330,329,336,335,342,341,348,347,354,353,360,359,366,365,372,371,828,827   ),
 (   410,409,404,403,398,397,392,391, 80, 79, -1, -1, 14, 13, 38, 37,  3,  0, 45, 44,110,109,386,385,380,379,374,373,829,  0 ),
 ( 412,411,406,405,400,399,394,393, 82, 81, 41, -1, 16, 15, 40, 39,  4,  0,  0, 46,112,111,388,387,382,381,376,375,831,830   ),
 (   414,413,408,407,402,401,396,395, 84, 83, 42,  0,  0,  0,  0,  0,  6,  5, 48, 47,114,113,390,389,384,383,378,377,832,  0 ),
 ( 416,415,422,421,428,427,104,103, 56, 55, 17,  0,  0,  0,  0,  0,  0,  0, 21, 20, 86, 85,434,433,440,439,446,445,834,833   ),
 (   418,417,424,423,430,429,106,105, 58, 57,  0,  0,  0,  0,  0,  0,  0,  0, 23, 22, 88, 87,436,435,442,441,448,447,835,  0 ),
 ( 420,419,426,425,432,431,108,107, 60, 59,  0,  0,  0,  0,  0,  0,  0,  0,  0, 24, 90, 89,438,437,444,443,450,449,837,836   ),
 (   482,481,476,475,470,469, 49, -1, 31,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1, 54, 53,464,463,458,457,452,451,838,  0 ),
 ( 484,483,478,477,472,471, 50,  0, -1,  0,  0,  0,  0,  0, -2,  0,  0,  0,  0,  0, -1,  0,466,465,460,459,454,453,840,839   ),
 (   486,485,480,479,474,473, 52, 51, 32,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  2, -1, 43,468,467,462,461,456,455,841,  0 ),
 ( 488,487,494,493,500,499, 98, 97, 62, 61,  0,  0,  0,  0,  0,  0,  0,  0,  0, 27, 92, 91,506,505,512,511,518,517,843,842   ),
 (   490,489,496,495,502,501,100, 99, 64, 63,  0,  0,  0,  0,  0,  0,  0,  0, 29, 28, 94, 93,508,507,514,513,520,519,844,  0 ),
 ( 492,491,498,497,504,503,102,101, 66, 65, 18,  0,  0,  0,  0,  0,  0,  0, 19, 30, 96, 95,510,509,516,515,522,521,846,845   ),
 (   560,559,554,553,548,547,542,541, 74, 73, 33,  0,  0,  0,  0,  0,  0, 11, 68, 67,116,115,536,535,530,529,524,523,847,  0 ),
 ( 562,561,556,555,550,549,544,543, 76, 75, -1,  0,  8,  7, 36, 35, 12, -1, 70, 69,118,117,538,537,532,531,526,525,849,848   ),
 (   564,563,558,557,552,551,546,545, 78, 77, -1, 34, 10,  9, 26, 25,  0, -1, 72, 71,120,119,540,539,534,533,528,527,850,  0 ),
 ( 566,565,572,571,578,577,584,583,590,589,596,595,602,601,608,607,614,613,620,619,626,625,632,631,638,637,644,643,852,851   ),
 (   568,567,574,573,580,579,586,585,592,591,598,597,604,603,610,609,616,615,622,621,628,627,634,633,640,639,646,645,853,  0 ),
 ( 570,569,576,575,582,581,588,587,594,593,600,599,606,605,612,611,618,617,624,623,630,629,636,635,642,641,648,647,855,854   ),
 (   728,727,722,721,716,715,710,709,704,703,698,697,692,691,686,685,680,679,674,673,668,667,662,661,656,655,650,649,856,  0 ),
 ( 730,729,724,723,718,717,712,711,706,705,700,699,694,693,688,687,682,681,676,675,670,669,664,663,658,657,652,651,858,857   ),
 (   732,731,726,725,720,719,714,713,708,707,702,701,696,695,690,689,684,683,678,677,672,671,666,665,660,659,654,653,859,  0 ),
 ( 734,733,740,739,746,745,752,751,758,757,764,763,770,769,776,775,782,781,788,787,794,793,800,799,806,805,812,811,861,860   ),
 (   736,735,742,741,748,747,754,753,760,759,766,765,772,771,778,777,784,783,790,789,796,795,802,801,808,807,814,813,862,  0 ),
 ( 738,737,744,743,750,749,756,755,762,761,768,767,774,773,780,779,786,785,792,791,798,797,804,803,810,809,816,815,864,863   ));

  ColorTable : array [0..11] of TColor = ($bbffff, $ffbbff, $ffffbb, $bbbbff,
                                          $bbffbb, $ffbbbb, $a0cbff, $a0ffcb,
                                          $ffa0cb, $cba0ff, $ffcba0, $cbffa0);
var
  i         : Integer;
  j         : Integer;
  XPos      : Integer;
  YPos      : Integer;
  RowOffset : Extended;
  ByteNum   : Integer;
  BitOffset : Integer;

begin

  FBitmap.Canvas.Brush.Color := Color;
  FBitmap.Canvas.Pen.Width := 1;

  YPos := 0;
  RowOffset := 0;

  for i := 0 to 32 do begin
    for j := 0 to 29 do begin
      XPos := Round (j * FMaxiHexWidth + RowOffset);
      if FHighlight then begin
        FBitmap.Canvas.Pen.Color := Color;
        FBitmap.Canvas.Brush.Color := Color;
      end;

      ByteNum := MaxBits[i, j];
      if ByteNum = -1 then begin
        if FHighlight then
          FBitmap.Canvas.Pen.Color := $505050;
        DrawHex (XPos, YPos);
        if FHighlight then
          FBitmap.Canvas.Pen.Color := Color;
      end else if ByteNum > 0 then begin
        BitOffset := ((ByteNum - 1) mod 6);
        ByteNum := (ByteNum - 1) div 6 {+ 1}; { Codeword 1 is the mode }
        if FHighlight then begin
          if not FShowCodewords then
            case ByteNum of
              0 : FBitmap.Canvas.Pen.Color := ColorTable[0];
              1..9 : FBitmap.Canvas.Pen.Color := ColorTable[1];
              10..19 : FBitmap.Canvas.Pen.Color := ColorTable[2];
              20..87 : FBitmap.Canvas.Pen.Color := ColorTable[3];
              89..103 : FBitmap.Canvas.Pen.Color := ColorTable[4];
              104..144 : FBitmap.Canvas.Pen.Color := ColorTable[5];
            end
          else
            FBitmap.Canvas.Pen.Color := ColorTable[ByteNum mod 12];
          FBitmap.Canvas.Brush.Color := FBitmap.Canvas.Pen.Color;
          DrawHex (XPos, YPos);
          FBitmap.Canvas.Pen.Color := Color;
          FBitmap.Canvas.Brush.Color := Color;
        end;
        if IsBitOn (FCodewords[ByteNum], 5 - BitOffset) then 
          DrawHex (XPos, YPos)
        else if FShowAll then
          DrawHex (XPos, YPos);
      end;
    end;
    RowOffset := FMaxiHexHOffset - RowOffset;
    YPos := Round (((i + 1) * FMaxiHexHeight) * 0.8660254);
  end;

  DrawFinder;
end;

procedure TStMaxiCodeBarcode.DrawFinder;
var
  CenterX : Integer;
  CenterY : Integer;

begin
  CenterX := Round (BarCodeRect.Left + 14.5 * FMaxiHexWidth);
  CenterY := BarCodeRect.Top + Round (16.5 * FMaxiHexHeight * 0.8660254);
  FBitmap.Canvas.Brush.Color := BackgroundColor;
  FBitmap.Canvas.Pen.Width := Round (FMaxiHexWidth + FMaxiHexVOffset);
  FBitmap.Canvas.Ellipse (
      CenterX - Round (FMaxiHexWidth)  * 4,
      CenterY - Round (FMaxiHexHeight) * 4,
      CenterX + Round (FMaxiHexWidth)  * 4,
      CenterY + Round (FMaxiHexHeight) * 4);
  FBitmap.Canvas.Ellipse (
      CenterX - Round (FMaxiHexWidth  * 2.5),
      CenterY - Round (FMaxiHexHeight * 2.5),
      CenterX + Round (FMaxiHexWidth  * 2.5),
      CenterY + Round (FMaxiHexHeight * 2.5));
  FBitmap.Canvas.Ellipse (
      CenterX - Round (FMaxiHexWidth),
      CenterY - Round (FMaxiHexHeight),
      CenterX + Round (FMaxiHexWidth),
      CenterY + Round (FMaxiHexHeight));

  if FHighlight then begin
    FBitmap.Canvas.Pen.Width := 1;
    FBitmap.Canvas.Pen.Color := clRed;
    FBitmap.Canvas.MoveTo (CenterX, 0);
    FBitmap.Canvas.LineTo (CenterX, Height);
    FBitmap.Canvas.MoveTo (0, CenterY);
    FBitmap.Canvas.LineTo (Width, CenterY);
  end;

  FBitmap.Canvas.Pen.Width := 1;
  FBitmap.Canvas.Brush.Color := Color;
end;

procedure TStMaxiCodeBarcode.DrawHex (XPos, YPos : Integer);
var
  XOffset   : Integer;
  YOffset   : Integer;
  HexWidth  : Integer;
  HexHeight : Integer;
  Border    : Extended;

begin
  XOffset := BarCodeRect.Left + XPos;
  YOffset := BarCodeRect.Top + YPos;
  Border := ((FMaxiHexWidth +  1) / 8) + 1;
  if FMaxiHexWidth >= 4 then begin
    XOffset := Round (XOffset + Border);
    YOffset := Round (YOffset + Border);
    HexWidth := Round (FMaxiHexWidth - Border);
    HexHeight := Round (FMaxiHexHeight - Border);
  end else begin
    HexWidth := Round (FMaxiHexWidth);
    HexHeight := Round (FMaxiHexHeight);
  end;

  if (HexWidth < 4) or (HexHeight < 4) then
    { Ellipses look better at poorer resolutions }
    FBitmap.Canvas.Ellipse (XOffset, YOffset,
                            XOffset + HexWidth, YOffset + HexHeight)
  else begin
    { Better resolution, draw a hex }
    FBitmap.Canvas.MoveTo (XOffset + HexWidth div 2,
                           YOffset);
    FBitmap.Canvas.LineTo (XOffset + HexWidth,
                           YOffset + HexHeight div 4);
    FBitmap.Canvas.LineTo (XOffset + HexWidth,
                           YOffset + HexHeight - (HexHeight div 4));
    FBitmap.Canvas.LineTo (XOffset + HexWidth div 2,
                           YOffset + HexHeight);
    FBitmap.Canvas.LineTo (XOffset,
                           YOffset + HexHeight - (HexHeight div 4));
    FBitmap.Canvas.LineTo (XOffset,
                           YOffset + HexHeight div 4);
    FBitmap.Canvas.LineTo (XOffset + HexWidth div 2,
                           YOffset);

    FBitmap.Canvas.FloodFill (XOffset + HexWidth div 2,
                              YOffset + HexWidth div 2,
                              BackgroundColor,
                              fsSurface);
  end;
end;

procedure TStMaxiCodeBarcode.GenerateCodewords;
begin
  TextToCodewords;
end;

procedure TStMaxiCodeBarcode.GenerateECC;
 { Calculate the ECC codes for MaxiCode }

  function GFSum (a : Integer; b : Integer) : Integer;
  { Sum of two numbers in Galois field arithmetic }
  begin
    Result := a xor b;
  end;

  function GfDifference (a : Integer; b : Integer) : Integer;
  { difference between two numbers in Galois field arithmetic (included for
    completeness) }
  begin
    Result := a xor b;
  end;

  function GFProduct (a : Integer; b : Integer) : Integer;
  { Product of two numbers in Galois field arithmetic }
  begin
    if (a = 0) or (b = 0) then
      Result := 0
    else
      Result := FAntiLog[(FLog[a] + FLog[b]) mod (StMaxiCodeGaloisField - 1)];
  end;

  function GFQuotient (a : Integer; b : Integer) : Integer;
  { Division of two numbers in Galois field arithmetic (included for
    completeness ) }
  begin
    if b = 0 then
      Result := 1 - StMaxiCodeGaloisField
    else if a = 0 then
      Result := 0
    else
      Result := FAntiLog[(FLog[a] - FLog[b] +
                (StMaxiCodeGaloisField - 1)) mod (StMaxiCodeGaloisField - 1)];
  end;

  procedure FillLogArrays (StMaxiCodeGaloisField : Integer;
                           StMaxiCodeECCPoly : Integer);
  { Populate the log and antilog tables for Galois field arithmetic }
  var
    i : Integer;

  begin
    FLog[0] := 1 - StMaxiCodeGaloisField;
    FAntiLog[0] := 1;
    for i := 1 to StMaxiCodeGaloisField - 1 do begin
      FAntiLog[i] := FAntiLog[i - 1] * 2;
      if FAntiLog[i] >= StMaxiCodeGaloisField then
        FAntiLog[i] := FAntiLog[i] xor StMaxiCodeECCPoly;
      FLog[FAntiLog[i]] := i;
    end;
  end;

  procedure CalculateECCCodes (var Data   : TStMaxiCodeECCData;
                               Polynomial : TStMaxiCodeECCPoly;
                               IStart     : TStMaxiCodeECCInterleave);
  { Calculate the Reed-Solomon error correcting codes (ECC) for MaxiCode.
    Basically, this is the equivalent of taking the Data as a series of
    coefficients to a polynomial (that has the lowest power the same as the
    highest power of the generating polynomial) and dividing it by the
    generating polynomial using Galois field arithmetic.  Get the remainder of
    this division and use that as the Reed Solomon error correcting codes }

  const
    { Generating polynomials }
    GPrimary : array [0..10] of Integer =
      (46, 44, 49, 3, 2, 57, 42, 39, 28, 31, 1);
    GEnhanced : array [0..28] of Integer =
      (28, 11, 20, 7, 43, 9, 41, 34, 49, 46, 37, 40, 55, 34, 45, 61, 13, 23,
       29, 22, 10, 35, 55, 41, 10, 53, 45, 22, 1);
    GStandard : array [0..20] of Integer =
      (59, 23, 19, 31, 33, 38, 17, 22, 48, 15, 36, 57, 37, 22, 8, 27, 33, 11,
       44, 23, 1);

  var
    BRegisters   : TStMaxiCodeECCData; { Works space for calculating RS ECC }
    DataPos      : Integer;            { Position for data read/writes }
    i            : Integer;
    j            : Integer;
    SumFromLast  : Integer;            { Result of input data + Last BReg }
    GenPolyMult  : Integer;            { Input data (SumFromLast) * gen poly }
    NumCodewords : Integer;            { Number of ECC codewords to generate }
    Interleaved  : Boolean;            { Read all data or alternate chars }
    StartingPos  : Integer;            { Where to start reading from }
    DataLength   : Integer;            { Amount of data to read }
    OutDataPos   : Integer;            { Where to write ECC to }

  begin
    { Intialize where to get data, write data, what poly to use, etc.. based
     from the Polynomial used and whether or not the even characters or
     odd characters are being encoded. }
    case Polynomial of
      epStandard :
        { Standard Error Correction }
        begin
          NumCodewords := 20;
          Interleaved := True;
          if IStart = imOdd then begin
            StartingPos := 20;
            OutDataPos := 104;
        end else begin
            StartingPos := 21;
            OutDataPos := 105;
          end;
          DataLength := 42;
        end;
      epEnhanced :
        begin
          { Enhanced Error Correction }
          NumCodewords := 28;
          Interleaved := True;
          if IStart = imOdd then begin
            StartingPos := 20;
            OutDataPos := 88;
          end else begin
            StartingPos := 21;
            OutDataPos := 89;
          end;
          DataLength := 34;
        end
      else begin
        { Primary Message }
        NumCodewords := 10;
        Interleaved := False;
        StartingPos := 0;
        OutDataPos := 10;
        DataLength := 10; 
      end;
    end;

    { Initialize all the BRegisters }
    for i := 0 to StMaxMaxiCodeECCDataSize do
      BRegisters[i] := 0;

    { Calculate the Log and AntiLog tables }
    FillLogArrays (StMaxiCodeGaloisField, StMaxiCodeECCPoly);

    DataPos := StartingPos;

    { Divide the polynomials and store the results in the BRegisters }
    for i := 0 to DataLength - 1 do begin
      SumFromLast := GFSum (BRegisters[NumCodewords - 1], Data[DataPos]);
      for j := NumCodewords - 1 downto 0 do begin
        case Polynomial of
          epStandard :
            GenPolyMult := GFProduct (SumFromLast, GStandard[j]);
          epEnhanced :
            GenPolyMult := GFProduct (SumFromLast, GEnhanced[j]);
        else
          GenPolyMult := GFProduct (SumFromLast, GPrimary[j]);
        end;
        if j > 0 then
          BRegisters[j] := GFSum (BRegisters[j - 1], GenPolyMult)
        else
          BRegisters[j] := GenPolyMult;
      end;
      if Interleaved then
        Inc (DataPos, 2)
      else
        Inc (DataPos);
    end;

    { Write the ECC values back into the data }
    DataPos := OutDataPos;
    for i := NumCodewords - 1 downto 0 do begin
      Data[DataPos] := BRegisters[i];
      if Interleaved then
        Inc (DataPos, 2)
      else
        Inc (DataPos);
    end;
  end;

begin
  { Calculate ECC codes for MaxiCode }

  CalculateECCCodes (FCodewords, epPrimary,  imNone); 
  if Mode = cmMode5 then begin
    CalculateECCCodes (FCodewords, epEnhanced, imEven);
    CalculateECCCodes (FCodewords, epEnhanced, imOdd);
  end else begin
    CalculateECCCodes (FCodewords, epStandard, imEven);
    CalculateECCCodes (FCodewords, epStandard, imOdd);
  end;
end;

procedure TStMaxiCodeBarcode.GetNextCharacter (var NewChar  : Integer;
                                               var Codeword : Boolean;
                                               var Position : Integer;
                                               CodeLen      : Integer);
var
  WorkNum : Integer;
                                                 
begin
  NewChar := 0;
  Codeword := False;

  if Position <= CodeLen then begin   
    if (FCode[Position] = '\') and
       (Position < CodeLen) then begin
      case FCode[Position + 1] of
        '0'..'9' : begin
          try
            NewChar := StrToInt (Copy (FCode, Position + 1, 3));
            Inc (Position, 4);
          except
            NewChar := 0;
            Inc (Position, 4);
          end;
        end;
        'C', 'c' : begin
          try
            Codeword := True;
            NewChar := StrToInt (Copy (FCode, Position + 2, 2));
            Inc (Position, 4);
          except
            NewChar := 0;
            Inc (Position, 4);
          end;
        end;
        'E', 'e' : begin
          if UpperCase (Copy (FCode, Position + 1, 3)) = 'EOT' then begin
            NewChar := 4;
            Inc (Position, 4);
          end else
            try
              WorkNum := StrToInt (Copy (FCode, Position + 1, 6));
              AddCodeword (27);
              Codeword := True;
              Inc (Position, 8);
              if (WorkNum >= 0) and (WorkNum <= 31) then begin
                NewChar := WorkNum;
              end else if (WorkNum >= 32) and (WorkNum <= 1023) then begin
                AddCodeword ($20 or (WorkNum div 64));
                NewChar := WorkNum mod 64;
              end else if (WorkNum >= 1024) and (WorkNum <= 32767) then begin
                AddCodeword ($30 or (WorkNum div 4096));
                WorkNum := WorkNum mod 4096;
                AddCodeword (WorkNum div 64);
                NewChar := WorkNum mod 64;
              end else if (WorkNum >= 32768) and (WorkNum <= 999999) then begin
                AddCodeword ($38 or (WorkNum div 262144));
                WorkNum := WorkNum mod 262144;
                AddCodeword (WorkNum div 64);
                WorkNum := WorkNum mod 4096;
                AddCodeword (WorkNum div 64);
                NewChar := WorkNum mod 64;
              end else
                raise E2DBarcodeError.Create (StEGLIOutOfRange);
            except
              on EConvertError do begin
                NewChar := Byte (FCode[Position]);
                Inc (Position);
              end;
            end;
        end;
        'F', 'f', 'G', 'g', 'N', 'n', 'R', 'r' : begin
          if Position < CodeLen - 1 then begin 
            if (FCode[Position + 2] = 'S') or
               (FCode[Position + 2] = 's') then begin
              case FCode[Position + 1] of
                'F', 'f' : NewChar := 28;
                'G', 'g' : NewChar := 29;
                'N', 'n' : begin
                  NewChar := 31;
                  Codeword := True;
                end;
                'R', 'r' : NewChar := 30;
              end;
              Inc (Position, 3);
            end else begin
              NewChar := Byte (FCode[Position]);
              Inc (Position);
            end;
          end else begin
            NewChar := Byte (FCode[Position]);
            Inc (Position);
          end;
        end;
        'X', 'x' : begin
          try
            NewChar := StrToInt ('$' + Copy (FCode, Position + 2, 2));
            Inc (Position, 4);
          except
            NewChar := 0;
            Inc (Position, 4);
          end;
        end;
        '\' : begin
          NewChar := Byte (FCode[Position]);
          Inc (Position, 2);
        end;
        else begin
          NewChar := Byte (FCode[Position]);
          Inc (Position);
        end;
      end;
    end else begin
      NewChar := Byte (FCode[Position]);
      Inc (Position);
    end;
  end;
end;

procedure TStMaxiCodeBarcode.GetSizes;
var
  ResX : Integer;
  ResY : Integer;

begin
  ResX := GetDeviceCaps (FBitmap.Canvas.Handle, LOGPIXELSX);
  ResY := GetDeviceCaps (FBitmap.Canvas.Handle, LOGPIXELSY);
  GetSizesEx (ResX, ResY);
end;

procedure TStMaxiCodeBarcode.GetSizesEx (ResX : Integer; ResY : Integer);
begin
  if FAutoScale then begin
    FMaxiHexWidth := (ResX * 1.003937) / 29;  { Width is 1.00" }
    FMaxiHexHeight := (ResY * 0.959449) /29;  { Height is 0.96" }
    FMaxiHexVOffset := -1 * (FMaxiHexHeight / 6);
    FMaxiHexHOffset := FMaxiHexWidth / 2;
  end else begin
    if BarWidth <> 0 then
      FMaxiHexWidth := BarWidth
    else
      FMaxiHexWidth :=   (FHorPixelsPerMM * 27) / 29;
    if BarHeight <> 0 then
      FMaxiHexHeight := BarHeight
    else
      FMaxiHexHeight := Round (FVerPixelsPerMM * 25) / 29;
    FMaxiHexVOffset := -1 * (FMaxiHexHeight / 6);
    FMaxiHexHOffset := FMaxiHexWidth / 2;
  end;
end;

procedure TStMaxiCodeBarcode.PlotCell (Row : Integer; Col : Integer);
var
  XPos : Integer;
  YPos : Integer;

begin
  YPos := Round (Row * FMaxiHexHeight + Row * FMaxiHexVOffset);
  if (Row mod 2) <> 0 then
    XPos := Round (FMaxiHexHOffset + FMaxiHexWidth * Col)
  else
    XPos := Round (FMaxiHexWidth * Col);
  DrawHex (XPos, YPos);
end;

procedure TStMaxiCodeBarcode.RenderToResolution (var OutBitmap : TBitmap;
                                                 ResX          : Integer;
                                                 ResY          : Integer;
                                                 var SizeX     : Integer;
                                                 var SizeY     : Integer);
var
  OldBarWidth       : Integer;
  OldBarHeight      : Integer;
  OldHorPixelsPerMM : Extended;
  OldVerPixelsPerMM : Extended;
  OldWidth          : Integer;
  OldHeight         : Integer;
  CurResX           : Integer;
  CurResY           : Integer;
  MultX             : Extended;
  MultY             : Extended;
  OldPPI            : Integer;

begin
  OldBarWidth       := BarWidth;
  OldBarHeight      := BarHeight;
  OldHorPixelsPerMM := FHorPixelsPerMM;
  OldVerPixelsPerMM := FVerPixelsPerMM;
  OldWidth          := Width;
  OldHeight         := Height;
  SizeX             := Width;
  SizeY             := Height;
  try
    if (ResX <> 0) and (ResY <> 0) then begin
      GetCurrentResolution (CurResX, CurResY);
      MultX           := ResX / CurResX;
      MultY           := ResY / CurResY;

      FBarWidth       := Trunc (FBarWidth * MultX);
      FBarHeight      := Trunc (FBarHeight * MultX);
      FHorPixelsPerMM := FHorPixelsPerMM * MultX;
      FVerPixelsPerMM := FVerPixelsPerMM * MultX;
      GetSizesEx (ResX, ResY);
      FBitmap.Width   := Trunc (FBitmap.Width * MultX);
      FBitmap.Height  := Trunc (FBitmap.Height * MultY);

      SizeX           := FBitmap.Width;
      SizeY           := FBitmap.Height;
    end;
    OldPPI            := FBitmap.Canvas.Font.PixelsPerInch;
    try
      FBitmap.Canvas.Font.PixelsPerInch := OutBitmap.Canvas.Font.PixelsPerInch;
      GenerateBarcodeBitmap (FBitmap.Width, FBitmap.Height);
    finally
      FBitmap.Canvas.Font.PixelsPerInch := OldPPI;
    end;
    OutBitmap.Width  := SizeX;
    OutBitmap.Height := SizeY;
    OutBitmap.Canvas.CopyRect (Rect (0, 0, SizeX, SizeY), FBitmap.Canvas,
                               Rect (0, 0, SizeX, SizeY));
  finally
    FBarWidth       := OldBarWidth;
    FBarHeight      := OldBarHeight;
    FHorPixelsPerMM := OldHorPixelsPerMM;
    FVerPixelsPerMM := OldVerPixelsPerMM;
    FBitmap.Width   := OldWidth;
    FBitmap.Height  := OldHeight;
    GetSizes;
    GenerateBarcodeBitmap (Width, Height);
  end;
end;

procedure TStMaxiCodeBarcode.SetAutoScale (const v : Boolean);
var
  OldAutoScale : Boolean;

begin
  if v <> FAutoScale then begin
    OldAutoScale := FAutoScale;
    try
      if (BarHeight = 0) and (HorPixelsPerMM = 0) and (not v) then
        raise E2DBarcodeError.Create (StENeedHorz);
      if (BarWidth = 0) and (VerPixelsPerMM = 0) and (not v) then
        raise E2DBarcodeError.Create (StENeedVert);
      FAutoScale := v;
      GetSizes;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FAutoScale := OldAutoScale;
        try
          GetSizes;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStMaxiCodeBarcode.SetBarHeight (const v : Integer);
begin
  if (v = 0) and (VerPixelsPerMM = 0) and (not AutoScale) then
    raise E2DBarcodeError.Create (StENeedVert);
  inherited SetBarHeight (v);
  GetSizes;
  GenerateBarcodeBitmap (Width, Height);
  Invalidate;
end;

procedure TStMaxiCodeBarcode.SetBarWidth (const v : Integer);
begin
  if (v = 0) and (HorPixelsPerMM = 0) and (not AutoScale) then
    raise E2DBarcodeError.Create (StENeedHorz);
  inherited SetBarWidth (v);
  GetSizes;
  GenerateBarcodeBitmap (Width, Height);
  Invalidate;
end;

procedure TStMaxiCodeBarcode.SetCarrierCountryCode (const v : Integer);
var
  OldCarrierCountryCode : Integer;

begin
  if v <> FCarrierCountryCode then begin
    OldCarrierCountryCode := FCarrierCountryCode;
    try
      FCarrierCountryCode := v;
      if (FMode = cmMode2) or (FMode = cmMode3) then begin
        GenerateCodewords;
        GenerateBarcodeBitmap (Width, Height);
        Invalidate;
      end;
    except
      on E2DBarcodeError do begin
        FCarrierCountryCode := OldCarrierCountryCode;
        try
          GenerateCodewords;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStMaxiCodeBarcode.SetCarrierPostalCode (const v : string);
var
  OldCarrierPostalCode : string;

begin
  if v <> FCarrierPostalCode then begin
    OldCarrierPostalCode := FCarrierPostalCode;
    try
      FCarrierPostalCode := v;
      if (FMode = cmMode2) or (FMode = cmMode3) then begin
        GenerateCodewords;
        GenerateBarcodeBitmap (Width, Height);
        Invalidate;
      end;
    except
      on E2DBarcodeError do begin
        FCarrierPostalCode := OldCarrierPostalCode;
        try
          GenerateCodewords;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStMaxiCodeBarcode.SetCarrierServiceClass (const v : Integer);
var
  OldCarrierServiceClass : Integer;

begin
  if v <> FCarrierServiceClass then begin
    OldCarrierServiceClass := FCarrierServiceClass;
    try
      FCarrierServiceClass := v;
      if (FMode = cmMode2) or (FMode = cmMode3) then begin
        GenerateCodewords;
        GenerateBarcodeBitmap (Width, Height);
        Invalidate;
      end;
    except
      on E2DBarcodeError do begin
        FCarrierServiceClass := OldCarrierServiceClass;
        try
          GenerateCodewords;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStMaxiCodeBarcode.SetHorPixelsPerMM (const v : Extended);
var
  OldHorPixelsPerMM : Extended;

begin
  if v <> FHorPixelsPerMM then begin
    if (v = 0) and (BarWidth = 0) and (not AutoScale) then
      raise E2DBarcodeError.Create (StENeedHorz);
    OldHorPixelsPerMM := FHorPixelsPerMM;
    try
      FHorPixelsPerMM := v;
      GetSizes;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FHorPixelsPerMM := OldHorPixelsPerMM;
        try
          GetSizes;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStMaxiCodeBarcode.SetMode (const v : TStMaxiCodeMode);
var
  OldMode : TStMaxiCodeMode;

begin
  if v <> FMode then begin
    OldMode := Mode;
    try
      FMode := v;
      GenerateCodewords;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FMode := OldMode;
        try
          GenerateCodewords;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStMaxiCodeBarcode.SetVerPixelsPerMM (const v : Extended);
var
  OldVerPixelsPerMM : Extended;

begin
  if v <> FVerPixelsPerMM then begin
    if (v = 0) and (BarHeight = 0) and (not AutoScale) then
      raise E2DBarcodeError.Create (StENeedVert);
    OldVerPixelsPerMM := FVerPixelsPerMM;
    try
      FVerPixelsPerMM := v;
      GetSizes;
      GenerateBarcodeBitmap (Width, Height);
      Invalidate;
    except
      on E2DBarcodeError do begin
        FVerPixelsPerMM := OldVerPixelsPerMM;
        try
          GetSizes;
          GenerateBarcodeBitmap (Width, Height);
          Invalidate;
        except
          on E2DBarcodeError do begin
          end;
        end;
        raise
      end;
    end;
  end;
end;

procedure TStMaxiCodeBarcode.TextToCodewords;


  function FindCodeSet (Value : Char) : TStMaxiCodeCodeSet;
  begin
    Result := csCodeSetA;
    while Result < csNone do begin
      if StMaxiCodeCodeSets[Result][Integer (Value)] <> -1 then
        Exit; 
      Inc (Result);
    end;
    Result := csNone;
  end;

  function ChangeCodeSet (CurrentMode   : TStMaxiCodeCodeSet;
                          NewMode       : TStMaxiCodeCodeSet;
                          Value         : Char;
                          UseShift      : Boolean;
                          UseTwoShift   : Boolean;
                          UseThreeShift : Boolean) : TStMaxiCodeCodeSet;
  const
    ShiftAB  = 59;
    ShiftAC  = 60;
    ShiftAD  = 61;
    ShiftAE  = 62;
    LatchAB  = 63;

    Shift2BA = 56;
    Shift3BA = 57;
    ShiftBA  = 59;
    ShiftBC  = 60;
    ShiftBD  = 61;
    ShiftBE  = 62;
    LatchBA  = 63;

    LatchCA  = 58;
    LockC    = 60;
    ShiftCD  = 61;
    ShiftCE  = 62;
    LatchCB  = 63;

    LatchDA  = 58;
    ShiftDC  = 60;
    LockD    = 61;
    ShiftDE  = 62;
    LatchDB  = 63;

    LatchEA  = 58;
    ShiftEC  = 60;
    ShiftED  = 61;
    LockE    = 62;
    LatchEB  = 63;

  begin
    if UseShift then
      Result := CurrentMode
    else
      Result := NewMode;
      
    case CurrentMode of
      csCodeSetA :
        case NewMode of
          csCodeSetB :
            { A -> B }
            if UseShift then
              AddCodeword (ShiftAB)
            else
              AddCodeword (LatchAB);
          csCodeSetC :
            { A -> C }
            begin
              AddCodeword (ShiftAC);
              if not UseShift then
                AddCodeword (LockC);
            end;
          csCodeSetD :
            { A -> D }
            begin
              AddCodeword (ShiftAD);
              if not UseShift then
                AddCodeword (LockD);
            end;
          csCodeSetE :
            { A -> E }
            begin
              AddCodeword (ShiftAE);
              if not UseShift then
                AddCodeword (LockE);
            end;
        end;

      csCodeSetB :
        case NewMode of
          csCodeSetA :
            { B -> A }
            if UseThreeShift then
              AddCodeword (Shift3BA)
            else if UseTwoShift then
              AddCodeword (Shift2BA)
            else if UseShift then
              AddCodeword (ShiftBA)
            else
              AddCodeword (LatchBA);
          csCodeSetC :
            { B -> C }
            begin
              AddCodeword (ShiftBC);
              if not UseShift then
                AddCodeword (LockC);
            end;
          csCodeSetD :
            { B -> D }
            begin
              AddCodeword (ShiftBD);
              if not UseShift then
                AddCodeword (LockD);
            end;
          csCodeSetE :
            { B -> E }
            begin
              AddCodeword (ShiftBE);
              if not UseShift then
                AddCodeword (LockE);
            end;
        end;

      csCodeSetC :
        case NewMode of
          csCodeSetA :
            { C -> A }
            begin
              AddCodeword (LatchCA);
              Result := NewMode;
            end;
          csCodeSetB :
            { C -> B }
            begin
              AddCodeword (LatchCB);
              Result := NewMode;
            end;
          csCodeSetD :
            { C -> D }
            begin
              AddCodeword (ShiftCD);
              if not UseShift then
                AddCodeword (LockD);
            end;
          csCodeSetE :
            { C -> E }
            begin
              AddCodeword (ShiftCE);
              if not UseShift then
                AddCodeword (LockE);
            end;
        end;

      csCodeSetD :
        case NewMode of
          csCodeSetA :
            { D -> A }
            begin
              AddCodeword (LatchDA);
              Result := NewMode;
            end;
          csCodeSetB :
            { D -> B }
            begin
              AddCodeword (LatchDB);
              Result := NewMode;
            end;
          csCodeSetC :
            { D -> C }
            begin
              AddCodeword (ShiftDC);
              if not UseShift then
                AddCodeword (LockC);
            end;
          csCodeSetE :
            { D -> E }
            begin
              AddCodeword (ShiftDE);
              if not UseShift then
                AddCodeword (LockE);
            end;
        end;

      csCodeSetE :
        case NewMode of
          csCodeSetA :
            { E -> A }
            begin
              AddCodeword (LatchEA);
              Result := NewMode;
            end;
          csCodeSetB :
            { E -> B }
            begin
              AddCodeword (LatchEB);
              Result := NewMode;
            end;
          csCodeSetC :
            { E -> C }
            begin
              AddCodeword (ShiftEC);
              if not UseShift then
                AddCodeword (LockC);
            end;
          csCodeSetD :
            { E -> D }
            begin
              AddCodeword (ShiftED);
              if not UseShift then
                AddCodeword (LockD);
            end;
        end;
    end;
  end;

  procedure GetMessageCodewords;
  var
    CodeLen     : Integer;
    CurrentMode : TStMaxiCodeCodeSet;
    UseShift    : Boolean;
    UseShift2   : Boolean;
    UseShift3   : Boolean;
    WorkMode    : TStMaxiCodeCodeSet;
    i           : Integer;
    Codeword    : Boolean;
    NewChar     : Integer;

  begin
    CodeLen := Length (Code);
    if CodeLen = 0 then begin
      for i := 0 to 144 do 
        AddCodeword (33);
      Exit;
    end;
    CurrentMode := csCodeSetA;
    i := 1;
    while i <= CodeLen do begin
      GetNextCharacter (NewChar, CodeWord, i, CodeLen);
      if CodeWord then
        AddCodeword (NewChar)
      else if StMaxiCodeCodeSets[CurrentMode][NewChar] = -1 then begin
        WorkMode := FindCodeSet (Char (NewChar));
        UseShift := False;
        UseShift2 := False;
        UseShift3 := False;
        if i < CodeLen then begin
          if StMaxiCodeCodeSets[CurrentMode][Integer (Code[i + 1])] <> -1 then
            UseShift := True;
        end;
        CurrentMode := ChangeCodeSet (CurrentMode, WorkMode, Char (NewChar),
                                      UseShift, UseShift2, UseShift3);
        AddCodeword (StMaxiCodeCodeSets[WorkMode][NewChar]);
      end else
        AddCodeword (StMaxiCodeCodeSets[CurrentMode][NewChar]);
    end;

    if (FNumCodewords > 68) and (FMode = cmMode5) then
      raise E2DBarcodeError.Create (StECodeTooLarge)
    else if FNumCodewords > 84 then
      raise E2DBarcodeError.Create (StECodeTooLarge);

    if CodeLen < 144 then begin
      if CurrentMode = csCodeSetC then begin
        AddCodeword (58);
        CurrentMode := csCodeSetA;
      end else if CurrentMode = csCodeSetD then begin
        AddCodeword (58);
        CurrentMode := csCodeSetA;
      end;
      for i := FNumCodewords to 144 do begin
        case CurrentMode of
          csCodeSetA :
            AddCodeword (33);
          csCodeSetB :                                    
            AddCodeword (33);
          csCodeSetE :
            AddCodeword (28);
        end;
      end;
    end;
  end;

  procedure MergeCodewords;
  begin
    case FMode of
      cmMode2 :
        System.Move (FMessage, FCodewords[20], 84);
      cmMode3 :
        System.Move (FMessage, FCodewords[20], 84);
      cmMode4 :
        begin
          System.Move (FMessage, FCodeWords[1], 9);
          System.Move (FMessage[9], FCodewords[20], 84);
        end;
      cmMode5 :
        begin
          System.Move (FMessage, FCodeWords[1], 9);
          System.Move (FMessage[9], FCodewords[20], 68);
        end;
      cmMode6 :
        System.Move (FMessage, FCodewords[20], 84);
    end;
  end;

  function IsNumericPostalCode : Boolean;
  var
    PostalLen : Integer;
    i         : Integer;
    
  begin
    Result := True;
    i := 1;
    PostalLen := Length (FCarrierPostalCode);
    while (i <= PostalLen) do
      if (FCarrierPostalCode[i] < '0') or
         (FCarrierPostalCode[i] > '9') then begin
        Result := False;
        i := PostalLen + 1;
      end else
        Inc (i);
  end;
  
  procedure EncodeCarrierInfo;

  { Encodation of the carrier information requires some fairly bizarre
    bit manipulation

     Codewords:-------->                                              111111
                111111222222333333444444555555666666777777888888999999000000 C
          012345012345012345012345012345012345012345012345012345012345012345 W
     num: ppMMMMppppppppppppppppppppppppllppppccllllccccccssssccssssssEEE...
      an: ppMMMMppppppppppppppppppppppppppppppccppppccccccssssccssssssEEE...
          123456789012345678901234567890123456789012345678901234567890123456 B
     Bits: ----->  111111111122222222223333333333444444444455555555556666666 i
                                                                             t
   MMMM = Mode
   pppp = Postal Code
   ll   = Postal Code Length
   cccc = Country Code
   ssss = Service Class
   EEEE = ECC Codes

   For pppp, ll, cccc and ssss, the MSB is on the right.
  }

  var
    WorkNum : Integer;
    WorkStr : string;
    i       : Integer;

  begin
    for WorkNum := 2 to 10 do 
      FCodewords[WorkNum] := 0;
    FCodewords[0] := FCodewords[0] and $0f;      
      
    if FCodewords[0] = $02 then begin
      { Format numeric postal code }
      { Format the postal code length }
      WorkNum := Length (FCarrierPostalCode);
      FCodewords[6] := (WorkNum and $3c) shr 2;
      FCodewords[5] := (WorkNum and $03) shl 4;
      { Format the postal code }
      try
        WorkNum := StrToInt (FCarrierPostalCode);
      except
        on EConvertError do
          raise E2DBarcodeError.Create (StEBadPostalCode);
      end;
      FCodewords[5] := FCodewords[5] or ((WorkNum shr 26) and $0f);
      FCodewords[4] := (WorkNum shr 20) and $3f;
      FCodewords[3] := (WorkNum shr 14) and $3f;
      FCodewords[2] := (WorkNum shr 8) and $3f;
      FCodewords[1] := (WorkNum shr 2) and $3f;
      FCodewords[0] := FCodewords[0] or ((WorkNum and $03) shl 4);
    end else begin
      { Format alphanumeric postal code }
      WorkStr := UpperCase (FCarrierPostalCode) + '      ';
      for i := 0 to 5 do begin
        WorkNum := StMaxiCodeCodeSets[csCodeSetA][Integer (WorkStr[6 - i])];
        if WorkNum < 0 then
          WorkNum := StMaxiCodeCodeSets[csCodeSetA][32]; { Use a space }
        FCodewords[i] := FCodewords[i] or ((WorkNum and $03) shl 4);
        FCodewords[i + 1] := FCodewords[i + 1] or ((WorkNum and $3c) shr 2);
      end;
    end;

    { Format country code }
    WorkNum := FCarrierCountryCode;
    FCodewords[8] := (WorkNum shr 8) and $03;
    FCodewords[7] := (WorkNum shr 2) and $3f;
    FCodewords[6] := FCodewords[6] or ((WorkNum and $03) shl 4);

    { Format service class }
    WorkNum := FCarrierServiceClass;
    FCodewords[9] := (WorkNum and $3f0) shr 4;
    FCodewords[8] := FCodewords[8] or ((WorkNum and $0f) shl 2);
  end;

var
  i : Integer;

begin
  for i := 0 to 144 do begin
    FCodewords[i] := 0;
    FMessage[i] := 0;
  end;

  FNumCodewords := 0;

  { Encode the primary message and set the FNumCodewords to the begining
    of the secondary message }

  case FMode of
    cmMode2, cmMode3 :
      if IsNumericPostalCode then
        FCodewords[0] := $02
      else
        FCodewords[0] := $03;
    cmMode4 :                
      FCodewords[0] := $04; 
    cmMode5 :
      FCodewords[0] := $05;
    cmMode6 :
      FCodewords[0] := $06;
  end;

  if (FMode = cmMode2) or (FMode = cmMode3) then
    EncodeCarrierInfo;

  GetMessageCodewords;
  MergeCodewords;
  GenerateECC;
  FNumCodewords := 144;

  FTotalCodewords := 144;
  if FMode = cmMode5 then
    FUsedECCCodewords := 66
  else
    FUsedECCCodewords := 50;
  case FMode of           
    cmMode2 :
      FUsedCodewords := FNumCodewords + 10;
    cmMode3 :
      FUsedCodewords := FNumCodewords + 10;
    cmMode4 :
      FUsedCodewords := FNumCodewords + 1;
    cmMode5 :
      FUsedCodewords := FNumCodewords + 1;
    cmMode6 :
      FUsedCodewords := FNumCodewords + 1;
  end;

  FFreeCodewords := FTotalCodewords - FUsedCodewords - FUsedECCCodewords;

end;

end.