From 699f4f7d9c073ae1e1fc7f55ec99519980a75a7e Mon Sep 17 00:00:00 2001 From: rowanhanna Date: Wed, 18 Dec 2013 14:27:32 +1100 Subject: [PATCH] Revert "Revert "First commit of source files"" This reverts commit a12e407da32ed3cd3eccbc458369446c313c1b3f. --- .gitattributes | 22 + .gitignore | 215 ++ Source/DelphiZXIngQRCode.pas | 3574 ++++++++++++++++++ TestApp/DelphiZXingQRCodeTestApp.dpr | 14 + TestApp/DelphiZXingQRCodeTestApp.dproj | 156 + TestApp/DelphiZXingQRCodeTestApp.res | Bin 0 -> 298600 bytes TestApp/DelphiZXingQRCodeTestAppMainForm.dfm | 97 + TestApp/DelphiZXingQRCodeTestAppMainForm.pas | 117 + 8 files changed, 4195 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Source/DelphiZXIngQRCode.pas create mode 100644 TestApp/DelphiZXingQRCodeTestApp.dpr create mode 100644 TestApp/DelphiZXingQRCodeTestApp.dproj create mode 100644 TestApp/DelphiZXingQRCodeTestApp.res create mode 100644 TestApp/DelphiZXingQRCodeTestAppMainForm.dfm create mode 100644 TestApp/DelphiZXingQRCodeTestAppMainForm.pas diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b9d6bd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,215 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +############# +## Windows detritus +############# + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist/ +build/ +eggs/ +parts/ +var/ +sdist/ +develop-eggs/ +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg diff --git a/Source/DelphiZXIngQRCode.pas b/Source/DelphiZXIngQRCode.pas new file mode 100644 index 0000000..160bbb4 --- /dev/null +++ b/Source/DelphiZXIngQRCode.pas @@ -0,0 +1,3574 @@ +unit DelphiZXingQRCode; + +// ZXing QRCode port to Delphi, by Debenu Pty Ltd +// www.debenu.com + +// Original copyright notice +(* + * Copyright 2008 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *) + +interface + +type + TQRCodeEncoding = (qrAuto, qrNumeric, qrAlphanumeric, qrISO88591, qrUTF8NoBOM, qrUTF8BOM); + T2DBooleanArray = array of array of Boolean; + + TDelphiZXingQRCode = class + protected + FData: WideString; + FRows: Integer; + FColumns: Integer; + FEncoding: TQRCodeEncoding; + FQuietZone: Integer; + FElements: T2DBooleanArray; + procedure SetEncoding(NewEncoding: TQRCodeEncoding); + procedure SetData(const NewData: WideString); + procedure SetQuietZone(NewQuietZone: Integer); + function GetIsBlack(Row, Column: Integer): Boolean; + procedure Update; + public + constructor Create; + property Data: WideString read FData write SetData; + property Encoding: TQRCodeEncoding read FEncoding write SetEncoding; + property QuietZone: Integer read FQuietZone write SetQuietZone; + property Rows: Integer read FRows; + property Columns: Integer read FColumns; + property IsBlack[Row, Column: Integer]: Boolean read GetIsBlack; + end; + +implementation + +uses + contnrs, Math, Classes; + +type + TByteArray = array of Byte; + T2DByteArray = array of array of Byte; + TIntegerArray = array of Integer; + +const + NUM_MASK_PATTERNS = 8; + + QUIET_ZONE_SIZE = 4; + + ALPHANUMERIC_TABLE: array[0..95] of Integer = ( + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1 // 0x50-0x5f + ); + + DEFAULT_BYTE_MODE_ENCODING = 'ISO-8859-1'; + + POSITION_DETECTION_PATTERN: array[0..6, 0..6] of Integer = ( + (1, 1, 1, 1, 1, 1, 1), + (1, 0, 0, 0, 0, 0, 1), + (1, 0, 1, 1, 1, 0, 1), + (1, 0, 1, 1, 1, 0, 1), + (1, 0, 1, 1, 1, 0, 1), + (1, 0, 0, 0, 0, 0, 1), + (1, 1, 1, 1, 1, 1, 1)); + + HORIZONTAL_SEPARATION_PATTERN: array[0..0, 0..7] of Integer = ( + (0, 0, 0, 0, 0, 0, 0, 0)); + + VERTICAL_SEPARATION_PATTERN: array[0..6, 0..0] of Integer = ( + (0), (0), (0), (0), (0), (0), (0)); + + POSITION_ADJUSTMENT_PATTERN: array[0..4, 0..4] of Integer = ( + (1, 1, 1, 1, 1), + (1, 0, 0, 0, 1), + (1, 0, 1, 0, 1), + (1, 0, 0, 0, 1), + (1, 1, 1, 1, 1)); + + // From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu. + POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE: array[0..39, 0..6] of Integer = ( + (-1, -1, -1, -1, -1, -1, -1), // Version 1 + ( 6, 18, -1, -1, -1, -1, -1), // Version 2 + ( 6, 22, -1, -1, -1, -1, -1), // Version 3 + ( 6, 26, -1, -1, -1, -1, -1), // Version 4 + ( 6, 30, -1, -1, -1, -1, -1), // Version 5 + ( 6, 34, -1, -1, -1, -1, -1), // Version 6 + ( 6, 22, 38, -1, -1, -1, -1), // Version 7 + ( 6, 24, 42, -1, -1, -1, -1), // Version 8 + ( 6, 26, 46, -1, -1, -1, -1), // Version 9 + ( 6, 28, 50, -1, -1, -1, -1), // Version 10 + ( 6, 30, 54, -1, -1, -1, -1), // Version 11 + ( 6, 32, 58, -1, -1, -1, -1), // Version 12 + ( 6, 34, 62, -1, -1, -1, -1), // Version 13 + ( 6, 26, 46, 66, -1, -1, -1), // Version 14 + ( 6, 26, 48, 70, -1, -1, -1), // Version 15 + ( 6, 26, 50, 74, -1, -1, -1), // Version 16 + ( 6, 30, 54, 78, -1, -1, -1), // Version 17 + ( 6, 30, 56, 82, -1, -1, -1), // Version 18 + ( 6, 30, 58, 86, -1, -1, -1), // Version 19 + ( 6, 34, 62, 90, -1, -1, -1), // Version 20 + ( 6, 28, 50, 72, 94, -1, -1), // Version 21 + ( 6, 26, 50, 74, 98, -1, -1), // Version 22 + ( 6, 30, 54, 78, 102, -1, -1), // Version 23 + ( 6, 28, 54, 80, 106, -1, -1), // Version 24 + ( 6, 32, 58, 84, 110, -1, -1), // Version 25 + ( 6, 30, 58, 86, 114, -1, -1), // Version 26 + ( 6, 34, 62, 90, 118, -1, -1), // Version 27 + ( 6, 26, 50, 74, 98, 122, -1), // Version 28 + ( 6, 30, 54, 78, 102, 126, -1), // Version 29 + ( 6, 26, 52, 78, 104, 130, -1), // Version 30 + ( 6, 30, 56, 82, 108, 134, -1), // Version 31 + ( 6, 34, 60, 86, 112, 138, -1), // Version 32 + ( 6, 30, 58, 86, 114, 142, -1), // Version 33 + ( 6, 34, 62, 90, 118, 146, -1), // Version 34 + ( 6, 30, 54, 78, 102, 126, 150), // Version 35 + ( 6, 24, 50, 76, 102, 128, 154), // Version 36 + ( 6, 28, 54, 80, 106, 132, 158), // Version 37 + ( 6, 32, 58, 84, 110, 136, 162), // Version 38 + ( 6, 26, 54, 82, 110, 138, 166), // Version 39 + ( 6, 30, 58, 86, 114, 142, 170) // Version 40 + ); + + // Type info cells at the left top corner. + TYPE_INFO_COORDINATES: array[0..14, 0..1] of Integer = ( + (8, 0), + (8, 1), + (8, 2), + (8, 3), + (8, 4), + (8, 5), + (8, 7), + (8, 8), + (7, 8), + (5, 8), + (4, 8), + (3, 8), + (2, 8), + (1, 8), + (0, 8) + ); + + // From Appendix D in JISX0510:2004 (p. 67) + VERSION_INFO_POLY = $1f25; // 1 1111 0010 0101 + + // From Appendix C in JISX0510:2004 (p.65). + TYPE_INFO_POLY = $537; + TYPE_INFO_MASK_PATTERN = $5412; + + + VERSION_DECODE_INFO: array[0..33] of Integer = ( + + $07C94, $085BC, $09A99, $0A4D3, $0BBF6, + $0C762, $0D847, $0E60D, $0F928, $10B78, + $1145D, $12A17, $13532, $149A6, $15683, + $168C9, $177EC, $18EC4, $191E1, $1AFAB, + $1B08E, $1CC1A, $1D33F, $1ED75, $1F250, + $209D5, $216F0, $228BA, $2379F, $24B0B, + $2542E, $26A64, $27541, $28C69); + +type + TMode = (qmTerminator, qmNumeric, qmAlphanumeric, qmStructuredAppend, + qmByte, qmECI, qmKanji, qmFNC1FirstPosition, qmFNC1SecondPosition, + qmHanzi); + +const + ModeCharacterCountBits: array[TMode] of array[0..2] of Integer = ( + (0, 0, 0), (10, 12, 14), (9, 11, 13), (0, 0, 0), (8, 16, 16), + (0, 0, 0), (8, 10, 12), (0, 0, 0), (0, 0, 0), (8, 10, 12)); + + ModeBits: array[TMode] of Integer = (0, 1, 2, 3, 4, 7, 8, 5, 9, 13); + +type + TErrorCorrectionLevel = class + private + FBits: Integer; + public + procedure Assign(Source: TErrorCorrectionLevel); + function Ordinal: Integer; + property Bits: Integer read FBits; + end; + + TECB = class + private + Count: Integer; + DataCodewords: Integer; + public + constructor Create(Count, DataCodewords: Integer); + function GetCount: Integer; + function GetDataCodewords: Integer; + end; + + TECBArray = array of TECB; + + TECBlocks = class + private + ECCodewordsPerBlock: Integer; + ECBlocks: TECBArray; + public + constructor Create(ECCodewordsPerBlock: Integer; ECBlocks: TECB); overload; + constructor Create(ECCodewordsPerBlock: Integer; ECBlocks1, ECBlocks2: TECB); overload; + destructor Destroy; override; + function GetTotalECCodewords: Integer; + function GetNumBlocks: Integer; + function GetECCodewordsPerBlock: Integer; + function GetECBlocks: TECBArray; + end; + + TByteMatrix = class + protected + Bytes: T2DByteArray; + FWidth: Integer; + FHeight: Integer; + public + constructor Create(Width, Height: Integer); + function Get(X, Y: Integer): Integer; + procedure SetBoolean(X, Y: Integer; Value: Boolean); + procedure SetInteger(X, Y: Integer; Value: Integer); + function GetArray: T2DByteArray; + procedure Assign(Source: TByteMatrix); + procedure Clear(Value: Byte); + function Hash: AnsiString; + property Width: Integer read FWidth; + property Height: Integer read FHeight; + end; + + TBitArray = class + private + Bits: array of Integer; + Size: Integer; + procedure EnsureCapacity(Size: Integer); + public + constructor Create; overload; + constructor Create(Size: Integer); overload; + function GetSizeInBytes: Integer; + function GetSize: Integer; + function Get(I: Integer): Boolean; + procedure SetBit(Index: Integer); + procedure AppendBit(Bit: Boolean); + procedure AppendBits(Value, NumBits: Integer); + procedure AppendBitArray(NewBitArray: TBitArray); + procedure ToBytes(BitOffset: Integer; Source: TByteArray; Offset, + NumBytes: Integer); + procedure XorOperation(Other: TBitArray); + end; + + TCharacterSetECI = class + + end; + + TVersion = class + private + VersionNumber: Integer; + AlignmentPatternCenters: array of Integer; + ECBlocks: array of TECBlocks; + TotalCodewords: Integer; + ECCodewords: Integer; + public + constructor Create(VersionNumber: Integer; AlignmentPatternCenters: array of Integer; ECBlocks1, ECBlocks2, ECBlocks3, ECBlocks4: TECBlocks); + destructor Destroy; override; + class function GetVersionForNumber(VersionNum: Integer): TVersion; + class function ChooseVersion(NumInputBits: Integer; ecLevel: TErrorCorrectionLevel): TVersion; + function GetTotalCodewords: Integer; + function GetECBlocksForLevel(ECLevel: TErrorCorrectionLevel): TECBlocks; + function GetDimensionForVersion: Integer; + end; + + TMaskUtil = class + public + function GetDataMaskBit(MaskPattern, X, Y: Integer): Boolean; + end; + + TQRCode = class + private + FMode: TMode; + FECLevel: TErrorCorrectionLevel; + FVersion: Integer; + FMatrixWidth: Integer; + FMaskPattern: Integer; + FNumTotalBytes: Integer; + FNumDataBytes: Integer; + FNumECBytes: Integer; + FNumRSBlocks: Integer; + FMatrix: TByteMatrix; + FQRCodeError: Boolean; + public + constructor Create; + destructor Destroy; override; + function At(X, Y: Integer): Integer; + function IsValid: Boolean; + function IsValidMaskPattern(MaskPattern: Integer): Boolean; + procedure SetMatrix(NewMatrix: TByteMatrix); + procedure SetECLevel(NewECLevel: TErrorCorrectionLevel); + procedure SetAll(VersionNum, NumBytes, NumDataBytes, NumRSBlocks, NumECBytes, MatrixWidth: Integer); + property QRCodeError: Boolean read FQRCodeError; + property Mode: TMode read FMode write FMode; + property Version: Integer read FVersion write FVersion; + property NumDataBytes: Integer read FNumDataBytes; + property NumTotalBytes: Integer read FNumTotalBytes; + property NumRSBlocks: Integer read FNumRSBlocks; + property MatrixWidth: Integer read FMatrixWidth; + property MaskPattern: Integer read FMaskPattern write FMaskPattern; + property ECLevel: TErrorCorrectionLevel read FECLevel; + end; + + TMatrixUtil = class + + private + FMatrixUtilError: Boolean; + procedure ClearMatrix(Matrix: TByteMatrix); + + procedure EmbedBasicPatterns(Version: Integer; Matrix: TByteMatrix); + procedure EmbedTypeInfo(ECLevel: TErrorCorrectionLevel; MaskPattern: Integer; Matrix: TByteMatrix); + procedure MaybeEmbedVersionInfo(Version: Integer; Matrix: TByteMatrix); + procedure EmbedDataBits(DataBits: TBitArray; MaskPattern: Integer; Matrix: TByteMatrix); + function FindMSBSet(Value: Integer): Integer; + function CalculateBCHCode(Value, Poly: Integer): Integer; + procedure MakeTypeInfoBits(ECLevel: TErrorCorrectionLevel; MaskPattern: Integer; Bits: TBitArray); + procedure MakeVersionInfoBits(Version: Integer; Bits: TBitArray); + function IsEmpty(Value: Integer): Boolean; + procedure EmbedTimingPatterns(Matrix: TByteMatrix); + procedure EmbedDarkDotAtLeftBottomCorner(Matrix: TByteMatrix); + procedure EmbedHorizontalSeparationPattern(XStart, YStart: Integer; Matrix: TByteMatrix); + procedure EmbedVerticalSeparationPattern(XStart, YStart: Integer; Matrix: TByteMatrix); + procedure EmbedPositionAdjustmentPattern(XStart, YStart: Integer; Matrix: TByteMatrix); + procedure EmbedPositionDetectionPattern(XStart, YStart: Integer; Matrix: TByteMatrix); + procedure EmbedPositionDetectionPatternsAndSeparators(Matrix: TByteMatrix); + procedure MaybeEmbedPositionAdjustmentPatterns(Version: Integer; Matrix: TByteMatrix); + public + constructor Create; + property MatrixUtilError: Boolean read FMatrixUtilError; + procedure BuildMatrix(DataBits: TBitArray; ECLevel: TErrorCorrectionLevel; Version, MaskPattern: Integer; Matrix: TByteMatrix); + end; + +function GetModeBits(Mode: TMode): Integer; +begin + Result := ModeBits[Mode]; +end; + +function GetModeCharacterCountBits(Mode: TMode; Version: TVersion): Integer; +var + Number: Integer; + Offset: Integer; +begin + Number := Version.VersionNumber; + + if (Number <= 9) then + begin + Offset := 0; + end else + if (number <= 26) then + begin + Offset := 1; + end else + begin + Offset := 2; + end; + Result := ModeCharacterCountBits[Mode][Offset]; +end; + +type + TBlockPair = class + private + FDataBytes: TByteArray; + FErrorCorrectionBytes: TByteArray; + public + constructor Create(BA1, BA2: TByteArray); + function GetDataBytes: TByteArray; + function GetErrorCorrectionBytes: TByteArray; + end; + + TGenericGFPoly = class; + + TGenericGF = class + private + FExpTable: TIntegerArray; + FLogTable: TIntegerArray; + FZero: TGenericGFPoly; + FOne: TGenericGFPoly; + FSize: Integer; + FPrimitive: Integer; + FGeneratorBase: Integer; + FInitialized: Boolean; + FPolyList: array of TGenericGFPoly; + + procedure CheckInit; + procedure Initialize; + public + class function CreateQRCodeField256: TGenericGF; + class function AddOrSubtract(A, B: Integer): Integer; + constructor Create(Primitive, Size, B: Integer); + destructor Destroy; override; + function GetZero: TGenericGFPoly; + function Exp(A: Integer): Integer; + function GetGeneratorBase: Integer; + function Inverse(A: Integer): Integer; + function Multiply(A, B: Integer): Integer; + function BuildMonomial(Degree, Coefficient: Integer): TGenericGFPoly; + end; + + TGenericGFPolyArray = array of TGenericGFPoly; + TGenericGFPoly = class + private + FField: TGenericGF; + FCoefficients: TIntegerArray; + public + constructor Create(AField: TGenericGF; ACoefficients: TIntegerArray); + destructor Destroy; override; + function Coefficients: TIntegerArray; + function Multiply(Other: TGenericGFPoly): TGenericGFPoly; + function MultiplyByMonomial(Degree, Coefficient: Integer): TGenericGFPoly; + function Divide(Other: TGenericGFPoly): TGenericGFPolyArray; + function GetCoefficients: TIntegerArray; + function IsZero: Boolean; + function GetCoefficient(Degree: Integer): Integer; + function GetDegree: Integer; + function AddOrSubtract(Other: TGenericGFPoly): TGenericGFPoly; + end; + + TReedSolomonEncoder = class + private + FField: TGenericGF; + FCachedGenerators: TObjectList; + public + constructor Create(AField: TGenericGF); + destructor Destroy; override; + procedure Encode(ToEncode: TIntegerArray; ECBytes: Integer); + function BuildGenerator(Degree: Integer): TGenericGFPoly; + end; + + TEncoder = class + private + FEncoderError: Boolean; + + function ApplyMaskPenaltyRule1Internal(Matrix: TByteMatrix; + IsHorizontal: Boolean): Integer; + function ChooseMode(const Content: WideString; var EncodeOptions: Integer): TMode; overload; + function FilterContent(const Content: WideString; Mode: TMode; EncodeOptions: Integer): WideString; + procedure Append8BitBytes(const Content: WideString; Bits: TBitArray; EncodeOptions: Integer); + + procedure AppendAlphanumericBytes(const Content: WideString; + Bits: TBitArray); + procedure AppendBytes(const Content: WideString; Mode: TMode; + Bits: TBitArray; EncodeOptions: Integer); + procedure AppendKanjiBytes(const Content: WideString; Bits: TBitArray); + procedure AppendLengthInfo(NumLetters, VersionNum: Integer; Mode: TMode; + Bits: TBitArray); + procedure AppendModeInfo(Mode: TMode; Bits: TBitArray); + procedure AppendNumericBytes(const Content: WideString; Bits: TBitArray); + function ChooseMaskPattern(Bits: TBitArray; ECLevel: TErrorCorrectionLevel; + Version: Integer; Matrix: TByteMatrix): Integer; + function GenerateECBytes(DataBytes: TByteArray; + + NumECBytesInBlock: Integer): TByteArray; + function GetAlphanumericCode(Code: Integer): Integer; + procedure GetNumDataBytesAndNumECBytesForBlockID(NumTotalBytes, + NumDataBytes, NumRSBlocks, BlockID: Integer; var NumDataBytesInBlock: TIntegerArray; + var NumECBytesInBlock: TIntegerArray); + procedure InterleaveWithECBytes(Bits: TBitArray; NumTotalBytes, + NumDataBytes, NumRSBlocks: Integer; var Result: TBitArray); + //function IsOnlyDoubleByteKanji(const Content: WideString): Boolean; + procedure TerminateBits(NumDataBytes: Integer; var Bits: TBitArray); + function CalculateMaskPenalty(Matrix: TByteMatrix): Integer; + function ApplyMaskPenaltyRule1(Matrix: TByteMatrix): Integer; + function ApplyMaskPenaltyRule2(Matrix: TByteMatrix): Integer; + function ApplyMaskPenaltyRule3(Matrix: TByteMatrix): Integer; + function ApplyMaskPenaltyRule4(Matrix: TByteMatrix): Integer; + //procedure Encode(const Content: WideString; ECLevel: TErrorCorrectionLevel; QRCode: TQRCode); overload; + procedure Encode(const Content: WideString; EncodeOptions: Integer; ECLevel: TErrorCorrectionLevel; QRCode: TQRCode); + public + constructor Create; + property EncoderError: Boolean read FEncoderError; + end; + +function TEncoder.ApplyMaskPenaltyRule1(Matrix: TByteMatrix): Integer; +begin + Result := ApplyMaskPenaltyRule1Internal(Matrix, True) + + ApplyMaskPenaltyRule1Internal(Matrix, False); +end; + +// Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give +// penalty to them. +function TEncoder.ApplyMaskPenaltyRule2(Matrix: TByteMatrix): Integer; +var + Penalty: Integer; + TheArray: T2DByteArray; + Width: Integer; + Height: Integer; + X: Integer; + Y: Integer; + Value: Integer; +begin + Penalty := 0; + TheArray := Matrix.GetArray; + Width := Matrix.Width; + Height := Matrix.Height; + for Y := 0 to Height - 2 do + begin + for X := 0 to Width - 2 do + begin + Value := TheArray[Y][X]; + if ((Value = TheArray[Y][X + 1]) and (Value = TheArray[Y + 1][X]) and + (Value = TheArray[Y + 1][X + 1])) then + begin + Inc(Penalty, 3); + end; + end; + end; + Result := Penalty; +end; + +// Apply mask penalty rule 3 and return the penalty. Find consecutive cells of 00001011101 or +// 10111010000, and give penalty to them. If we find patterns like 000010111010000, we give +// penalties twice (i.e. 40 * 2). +function TEncoder.ApplyMaskPenaltyRule3(Matrix: TByteMatrix): Integer; +var + Penalty: Integer; + TheArray: T2DByteArray; + Width: Integer; + Height: Integer; + X: Integer; + Y: Integer; +begin + Penalty := 0; + TheArray := Matrix.GetArray; + Width := Matrix.Width; + Height := Matrix.Height; + for Y := 0 to Height - 1 do + begin + for X := 0 to Width - 1 do + begin + if ((X + 6 < Width) and + (TheArray[Y][X] = 1) and + (TheArray[Y][X + 1] = 0) and + (TheArray[Y][X + 2] = 1) and + (TheArray[Y][X + 3] = 1) and + (TheArray[Y][X + 4] = 1) and + (TheArray[Y][X + 5] = 0) and + (TheArray[Y][X + 6] = 1) and + (((X + 10 < Width) and + (TheArray[Y][X + 7] = 0) and + (TheArray[Y][X + 8] = 0) and + (TheArray[Y][X + 9] = 0) and + (TheArray[Y][X + 10] = 0)) or + ((x - 4 >= 0) and + (TheArray[Y][X - 1] = 0) and + (TheArray[Y][X - 2] = 0) and + (TheArray[Y][X - 3] = 0) and + (TheArray[Y][X - 4] = 0)))) then + begin + Inc(Penalty, 40); + end; + if ((Y + 6 < Height) and + (TheArray[Y][X] = 1) and + (TheArray[Y + 1][X] = 0) and + (TheArray[Y + 2][X] = 1) and + (TheArray[Y + 3][X] = 1) and + (TheArray[Y + 4][X] = 1) and + (TheArray[Y + 5][X] = 0) and + (TheArray[Y + 6][X] = 1) and + (((Y + 10 < Height) and + (TheArray[Y + 7][X] = 0) and + (TheArray[Y + 8][X] = 0) and + (TheArray[Y + 9][X] = 0) and + (TheArray[Y + 10][X] = 0)) or + ((Y - 4 >= 0) and + (TheArray[Y - 1][X] = 0) and + (TheArray[Y - 2][X] = 0) and + (TheArray[Y - 3][X] = 0) and + (TheArray[Y - 4][X] = 0)))) then + begin + Inc(Penalty, 40); + end; + end; + end; + Result := Penalty; +end; + +// Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give +// penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. Examples: +// - 0% => 100 +// - 40% => 20 +// - 45% => 10 +// - 50% => 0 +// - 55% => 10 +// - 55% => 20 +// - 100% => 100 +function TEncoder.ApplyMaskPenaltyRule4(Matrix: TByteMatrix): Integer; +var + NumDarkCells: Integer; + TheArray: T2DByteArray; + Width: Integer; + Height: Integer; + NumTotalCells: Integer; + DarkRatio: Double; + X: Integer; + Y: Integer; +begin + NumDarkCells := 0; + TheArray := Matrix.GetArray; + Width := Matrix.Width; + Height := matrix.Height; + for Y := 0 to Height - 1 do + begin + for X := 0 to Width - 1 do + begin + if (TheArray[Y][X] = 1) then + begin + Inc(NumDarkCells); + end; + end; + end; + numTotalCells := matrix.Height * Matrix.Width; + DarkRatio := NumDarkCells / NumTotalCells; + Result := Round(Abs((DarkRatio * 100 - 50)) / 50); +end; + +// Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both +// vertical and horizontal orders respectively. +function TEncoder.ApplyMaskPenaltyRule1Internal(Matrix: TByteMatrix; IsHorizontal: Boolean): Integer; +var + Penalty: Integer; + NumSameBitCells: Integer; + PrevBit: Integer; + TheArray: T2DByteArray; + I: Integer; + J: Integer; + Bit: Integer; + ILimit: Integer; + JLimit: Integer; +begin + Penalty := 0; + NumSameBitCells := 0; + PrevBit := -1; + // Horizontal mode: + // for (int i = 0; i < matrix.height(); ++i) { + // for (int j = 0; j < matrix.width(); ++j) { + // int bit = matrix.get(i, j); + // Vertical mode: + // for (int i = 0; i < matrix.width(); ++i) { + // for (int j = 0; j < matrix.height(); ++j) { + // int bit = matrix.get(j, i); + if (IsHorizontal) then + begin + ILimit := Matrix.Height; + JLimit := Matrix.Width; + end else + begin + ILimit := Matrix.Width; + JLimit := Matrix.Height; + end; + TheArray := Matrix.GetArray; + + for I := 0 to ILimit - 1 do + begin + for J := 0 to JLimit - 1 do + begin + if (IsHorizontal) then + begin + Bit := TheArray[I][J]; + end else + begin + Bit := TheArray[J][I]; + end; + if (Bit = PrevBit) then + begin + Inc(NumSameBitCells); + // Found five repetitive cells with the same color (bit). + // We'll give penalty of 3. + if (NumSameBitCells = 5) then + begin + Inc(Penalty, 3); + end else if (NumSameBitCells > 5) then + begin + // After five repetitive cells, we'll add the penalty one + // by one. + Inc(Penalty, 1);; + end; + end else + begin + NumSameBitCells := 1; // Include the cell itself. + PrevBit := bit; + end; + end; + NumSameBitCells := 0; // Clear at each row/column. + end; + Result := Penalty; +end; + +{ TQRCode } + +constructor TQRCode.Create; +begin + FMode := qmTerminator; + FQRCodeError := False; + FECLevel := nil; + FVersion := -1; + FMatrixWidth := -1; + FMaskPattern := -1; + FNumTotalBytes := -1; + FNumDataBytes := -1; + FNumECBytes := -1; + FNumRSBlocks := -1; + FMatrix := nil; +end; + +destructor TQRCode.Destroy; +begin + if (Assigned(FECLevel)) then + begin + FECLevel.Free; + end; + if (Assigned(FMatrix)) then + begin + FMatrix.Free; + end; + inherited; +end; + +function TQRCode.At(X, Y: Integer): Integer; +var + Value: Integer; +begin + // The value must be zero or one. + Value := FMatrix.Get(X, Y); + if (not ((Value = 0) or (Value = 1))) then + begin + FQRCodeError := True; + end; + Result := Value; +end; + +function TQRCode.IsValid: Boolean; +begin + Result := + // First check if all version are not uninitialized. + ((FECLevel <> nil) and + (FVersion <> -1) and + (FMatrixWidth <> -1) and + (FMaskPattern <> -1) and + (FNumTotalBytes <> -1) and + (FNumDataBytes <> -1) and + (FNumECBytes <> -1) and + (FNumRSBlocks <> -1) and + // Then check them in other ways.. + IsValidMaskPattern(FMaskPattern) and + (FNumTotalBytes = FNumDataBytes + FNumECBytes) and + // ByteMatrix stuff. + (Assigned(FMatrix)) and + (FMatrixWidth = FMatrix.Width) and + // See 7.3.1 of JISX0510:2004 (Fp.5). + (FMatrix.Width = FMatrix.Height)); // Must be square. +end; + +function TQRCode.IsValidMaskPattern(MaskPattern: Integer): Boolean; +begin + Result := (MaskPattern >= 0) and (MaskPattern < NUM_MASK_PATTERNS); +end; + +procedure TQRCode.SetMatrix(NewMatrix: TByteMatrix); +begin + if (Assigned(FMatrix)) then + begin + FMatrix.Free; + FMatrix := nil; + end; + FMatrix := NewMatrix; +end; + +procedure TQRCode.SetAll(VersionNum, NumBytes, NumDataBytes, NumRSBlocks, + NumECBytes, MatrixWidth: Integer); +begin + FVersion := VersionNum; + FNumTotalBytes := NumBytes; + FNumDataBytes := NumDataBytes; + FNumRSBlocks := NumRSBlocks; + FNumECBytes := NumECBytes; + FMatrixWidth := MatrixWidth; +end; + +procedure TQRCode.SetECLevel(NewECLevel: TErrorCorrectionLevel); +begin + if (Assigned(FECLevel)) then + begin + FECLevel.Free; + end; + FECLevel := TErrorCorrectionLevel.Create; + FECLevel.Assign(NewECLevel); +end; + +{ TByteMatrix } + +procedure TByteMatrix.Clear(Value: Byte); +var + X, Y: Integer; +begin + for Y := 0 to FHeight - 1 do + begin + for X := 0 to FWidth - 1 do + begin + Bytes[Y][X] := Value; + end; + end; +end; + +constructor TByteMatrix.Create(Width, Height: Integer); +var + Y: Integer; + X: Integer; +begin + FWidth := Width; + FHeight := Height; + SetLength(Bytes, Height); + for Y := 0 to Height - 1 do + begin + SetLength(Bytes[Y], Width); + for X := 0 to Width - 1 do + begin + Bytes[Y][X] := 0; + end; + end; +end; + +function TByteMatrix.Get(X, Y: Integer): Integer; +begin + if (Bytes[Y][X] = 255) then Result := -1 else Result := Bytes[Y][X]; +end; + +function TByteMatrix.GetArray: T2DByteArray; +begin + Result := Bytes; +end; + +function TByteMatrix.Hash: AnsiString; +var + X, Y: Integer; + Counter: Integer; + CC: Integer; +begin + Result := ''; + for Y := 0 to FHeight - 1 do + begin + Counter := 0; + for X := 0 to FWidth - 1 do + begin + CC := Get(X, Y); + if (CC = -1) then CC := 255; + Counter := Counter + CC; + end; + Result := Result + AnsiChar((Counter mod 26) + 65); + end; +end; + +procedure TByteMatrix.SetBoolean(X, Y: Integer; Value: Boolean); +begin + Bytes[Y][X] := Byte(Value) and $FF; +end; + +procedure TByteMatrix.SetInteger(X, Y, Value: Integer); +begin + Bytes[Y][X] := Value and $FF; +end; + +procedure TByteMatrix.Assign(Source: TByteMatrix); +var + SourceLength: Integer; +begin + SourceLength := Length(Source.Bytes); + SetLength(Bytes, SourceLength); + if (SourceLength > 0) then + begin + Move(Source.Bytes[0], Bytes[0], SourceLength); + end; + FWidth := Source.Width; + FHeight := Source.Height; +end; + +{ TEncoder } + +function TEncoder.CalculateMaskPenalty(Matrix: TByteMatrix): Integer; +var + Penalty: Integer; +begin + Penalty := 0; + Inc(Penalty, ApplyMaskPenaltyRule1(Matrix)); + Inc(Penalty, ApplyMaskPenaltyRule2(Matrix)); + Inc(Penalty, ApplyMaskPenaltyRule3(Matrix)); + Inc(Penalty, ApplyMaskPenaltyRule4(Matrix)); + Result := Penalty; +end; + +{procedure TEncoder.Encode(const Content: WideString; ECLevel: TErrorCorrectionLevel; QRCode: TQRCode); +begin + Encode(Content, ECLevel, nil, QRCode); +end;} + +procedure TEncoder.Encode(const Content: WideString; EncodeOptions: Integer; ECLevel: TErrorCorrectionLevel; QRCode: TQRCode); +var + Mode: TMode; + DataBits: TBitArray; + FinalBits: TBitArray; + HeaderBits: TBitArray; + HeaderAndDataBits: TBitArray; + Matrix: TByteMatrix; + NumLetters: Integer; + MatrixUtil: TMatrixUtil; + BitsNeeded: Integer; + ProvisionalBitsNeeded: Integer; + ProvisionalVersion: TVersion; + Version: TVersion; + ECBlocks: TECBlocks; + NumDataBytes: Integer; + Dimension: Integer; + FilteredContent: WideString; +begin + DataBits := TBitArray.Create; + HeaderBits := TBitArray.Create; + + // Pick an encoding mode appropriate for the content. Note that this will not attempt to use + // multiple modes / segments even if that were more efficient. Twould be nice. + // Collect data within the main segment, separately, to count its size if needed. Don't add it to + // main payload yet. + + Mode := ChooseMode(Content, EncodeOptions); + FilteredContent := FilterContent(Content, Mode, EncodeOptions); + AppendBytes(FilteredContent, Mode, DataBits, EncodeOptions); + + // (With ECI in place,) Write the mode marker + AppendModeInfo(Mode, HeaderBits); + + // Hard part: need to know version to know how many bits length takes. But need to know how many + // bits it takes to know version. First we take a guess at version by assuming version will be + // the minimum, 1: + ProvisionalVersion := TVersion.GetVersionForNumber(1); + try + ProvisionalBitsNeeded := HeaderBits.GetSize + + GetModeCharacterCountBits(Mode, ProvisionalVersion) + + DataBits.GetSize; + finally + ProvisionalVersion.Free; + end; + + ProvisionalVersion := TVersion.ChooseVersion(ProvisionalBitsNeeded, ECLevel); + try + // Use that guess to calculate the right version. I am still not sure this works in 100% of cases. + BitsNeeded := HeaderBits.GetSize + + GetModeCharacterCountBits(Mode, ProvisionalVersion) + + DataBits.GetSize; + Version := TVersion.ChooseVersion(BitsNeeded, ECLevel); + finally + ProvisionalVersion.Free; + end; + + HeaderAndDataBits := TBitArray.Create; + FinalBits := TBitArray.Create; + try + HeaderAndDataBits.AppendBitArray(HeaderBits); + + // Find "length" of main segment and write it + if (Mode = qmByte) then + begin + NumLetters := DataBits.GetSizeInBytes; + end else + begin + NumLetters := Length(FilteredContent); + end; + AppendLengthInfo(NumLetters, Version.VersionNumber, Mode, HeaderAndDataBits); + // Put data together into the overall payload + HeaderAndDataBits.AppendBitArray(DataBits); + + ECBlocks := Version.GetECBlocksForLevel(ECLevel); + NumDataBytes := Version.GetTotalCodewords - ECBlocks.GetTotalECCodewords; + + // Terminate the bits properly. + TerminateBits(NumDataBytes, HeaderAndDataBits); + + // Interleave data bits with error correction code. + InterleaveWithECBytes(HeaderAndDataBits, Version.GetTotalCodewords, + NumDataBytes, ECBlocks.GetNumBlocks, FinalBits); + + // QRCode qrCode = new QRCode(); // This is passed in + + + QRCode.SetECLevel(ECLevel); + QRCode.Mode := Mode; + QRCode.Version := Version.VersionNumber; + + // Choose the mask pattern and set to "qrCode". + Dimension := Version.GetDimensionForVersion; + Matrix := TByteMatrix.Create(Dimension, Dimension); + + QRCode.MaskPattern := ChooseMaskPattern(FinalBits, ECLevel, Version.VersionNumber, Matrix); + + Matrix.Free; + Matrix := TByteMatrix.Create(Dimension, Dimension); + + // Build the matrix and set it to "qrCode". + MatrixUtil := TMatrixUtil.Create; + try + MatrixUtil.BuildMatrix(FinalBits, QRCode.ECLevel, QRCode.Version, + QRCode.MaskPattern, Matrix); + finally + MatrixUtil.Free; + end; + + QRCode.SetMatrix(Matrix); // QRCode will free the matrix + finally + DataBits.Free; + HeaderAndDataBits.Free; + FinalBits.Free; + HeaderBits.Free; + Version.Free; + end; +end; + +function TEncoder.FilterContent(const Content: WideString; Mode: TMode; + EncodeOptions: Integer): WideString; +var + X: Integer; + CanAdd: Boolean; +begin + Result := ''; + for X := 1 to Length(Content) do + begin + CanAdd := False; + if (Mode = qmNumeric) then + begin + CanAdd := (Content[X] >= '0') and (Content[X] <= '9'); + end else + if (Mode = qmAlphanumeric) then + begin + CanAdd := GetAlphanumericCode(Ord(Content[X])) > 0; + end else + if (Mode = qmByte) then + begin + if (EncodeOptions = 3) then + begin + CanAdd := Ord(Content[X]) <= $FF; + end else + if ((EncodeOptions = 4) or (EncodeOptions = 5)) then + begin + CanAdd := True; + end; + end; + if (CanAdd) then + begin + Result := Result + Content[X]; + end; + end; +end; + +// Return the code point of the table used in alphanumeric mode or +// -1 if there is no corresponding code in the table. +function TEncoder.GetAlphanumericCode(Code: Integer): Integer; +begin + if (Code < Length(ALPHANUMERIC_TABLE)) then + begin + Result := ALPHANUMERIC_TABLE[Code]; + end else + begin + Result := -1; + end; +end; + +// Choose the mode based on the content +function TEncoder.ChooseMode(const Content: WideString; var EncodeOptions: Integer): TMode; +var + AllNumeric: Boolean; + AllAlphanumeric: Boolean; + AllISO: Boolean; + I: Integer; + C: WideChar; +begin + if (EncodeOptions = 0) then + begin + AllNumeric := Length(Content) > 0; + I := 1; + while (I <= Length(Content)) and (AllNumeric) do + begin + C := Content[I]; + if ((C < '0') or (C > '9')) then + begin + AllNumeric := False; + end else + begin + Inc(I); + end; + end; + + if (not AllNumeric) then + begin + AllAlphanumeric := Length(Content) > 0; + I := 1; + while (I <= Length(Content)) and (AllAlphanumeric) do + begin + C := Content[I]; + if (GetAlphanumericCode(Ord(C)) < 0) then + begin + AllAlphanumeric := False; + end else + begin + Inc(I); + end; + end; + end else + begin + AllAlphanumeric := False; + end; + + if (not AllAlphanumeric) then + begin + AllISO := Length(Content) > 0; + I := 1; + while (I <= Length(Content)) and (AllISO) do + begin + C := Content[I]; + if (Ord(C) > $FF) then + begin + AllISO := False; + end else + begin + Inc(I); + end; + end; + end else + begin + AllISO := False; + end; + + if (AllNumeric) then + begin + Result := qmNumeric; + end else + if (AllAlphanumeric) then + begin + Result := qmAlphanumeric; + end else + if (AllISO) then + begin + Result := qmByte; + EncodeOptions := 3; + end else + begin + Result := qmByte; + EncodeOptions := 4; + end; + end else + if (EncodeOptions = 1) then + begin + Result := qmNumeric; + end else + if (EncodeOptions = 2) then + begin + Result := qmAlphanumeric; + end else + begin + Result := qmByte; + end; +end; + +constructor TEncoder.Create; +begin + FEncoderError := False; +end; + +{function TEncoder.IsOnlyDoubleByteKanji(const Content: WideString): Boolean; +var + I: Integer; + Char1: Integer; +begin + Result := True; + I := 0; + while ((I < Length(Content)) and Result) do + begin + Char1 := Ord(Content[I + 1]); + if (((Char1 < $81) or (Char1 > $9F)) and ((Char1 < $E0) or (Char1 > $EB))) then + begin + Result := False; + end; + end; +end;} + +function TEncoder.ChooseMaskPattern(Bits: TBitArray; ECLevel: TErrorCorrectionLevel; Version: Integer; Matrix: TByteMatrix): Integer; +var + MinPenalty: Integer; + BestMaskPattern: Integer; + MaskPattern: Integer; + MatrixUtil: TMatrixUtil; + Penalty: Integer; +begin + MinPenalty := MaxInt; + BestMaskPattern := -1; + + // We try all mask patterns to choose the best one. + for MaskPattern := 0 to NUM_MASK_PATTERNS - 1 do + begin + MatrixUtil := TMatrixUtil.Create; + try + MatrixUtil.BuildMatrix(Bits, ECLevel, Version, MaskPattern, Matrix); + finally + MatrixUtil.Free; + end; + Penalty := CalculateMaskPenalty(Matrix); + if (Penalty < MinPenalty) then + begin + MinPenalty := Penalty; + BestMaskPattern := MaskPattern; + end; + end; + + Result := BestMaskPattern; +end; + +// Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). +procedure TEncoder.TerminateBits(NumDataBytes: Integer; var Bits: TBitArray); +var + Capacity: Integer; + I: Integer; + NumBitsInLastByte: Integer; + NumPaddingBytes: Integer; +begin + Capacity := NumDataBytes shl 3; + if (Bits.GetSize > Capacity) then + begin + FEncoderError := True; + Exit; + end; + I := 0; + while ((I < 4) and (Bits.GetSize < capacity)) do + begin + Bits.AppendBit(False); + Inc(I); + end; + + // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details. + // If the last byte isn't 8-bit aligned, we'll add padding bits. + NumBitsInLastByte := Bits.GetSize and $07; + if (NumBitsInLastByte > 0) then + begin + for I := numBitsInLastByte to 7 do + begin + Bits.AppendBit(False); + end; + end; + + // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24). + NumPaddingBytes := NumDataBytes - Bits.GetSizeInBytes; + for I := 0 to NumPaddingBytes - 1 do + begin + if ((I and $01) = 0) then + begin + Bits.AppendBits($EC, 8); + end else + begin + Bits.AppendBits($11, 8); + end; + end; + if (Bits.GetSize <> Capacity) then + begin + FEncoderError := True; + end; +end; + +// Get number of data bytes and number of error correction bytes for block id "blockID". Store +// the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of +// JISX0510:2004 (p.30) +procedure TEncoder.GetNumDataBytesAndNumECBytesForBlockID(NumTotalBytes, NumDataBytes, + NumRSBlocks, BlockID: Integer; var NumDataBytesInBlock: TIntegerArray; + var NumECBytesInBlock: TIntegerArray); +var + NumRSBlocksInGroup1: Integer; + NumRSBlocksInGroup2: Integer; + NumTotalBytesInGroup1: Integer; + NumTotalBytesInGroup2: Integer; + NumDataBytesInGroup1: Integer; + NumDataBytesInGroup2: Integer; + NumECBytesInGroup1: Integer; + NumECBytesInGroup2: Integer; +begin + if (BlockID >= NumRSBlocks) then + begin + FEncoderError := True; + Exit; + end; + // numRsBlocksInGroup2 = 196 % 5 = 1 + NumRSBlocksInGroup2 := NumTotalBytes mod NumRSBlocks; + // numRsBlocksInGroup1 = 5 - 1 = 4 + NumRSBlocksInGroup1 := NumRSBlocks - NumRSBlocksInGroup2; + // numTotalBytesInGroup1 = 196 / 5 = 39 + NumTotalBytesInGroup1 := NumTotalBytes div NumRSBlocks; + // numTotalBytesInGroup2 = 39 + 1 = 40 + NumTotalBytesInGroup2 := NumTotalBytesInGroup1 + 1; + // numDataBytesInGroup1 = 66 / 5 = 13 + NumDataBytesInGroup1 := NumDataBytes div NumRSBlocks; + // numDataBytesInGroup2 = 13 + 1 = 14 + NumDataBytesInGroup2 := NumDataBytesInGroup1 + 1; + // numEcBytesInGroup1 = 39 - 13 = 26 + NumECBytesInGroup1 := NumTotalBytesInGroup1 - NumDataBytesInGroup1; + // numEcBytesInGroup2 = 40 - 14 = 26 + NumECBytesInGroup2 := NumTotalBytesInGroup2 - NumDataBytesInGroup2; + // Sanity checks. + // 26 = 26 + if (NumECBytesInGroup1 <> NumECBytesInGroup2) then + begin + FEncoderError := True; + Exit; + end; + // 5 = 4 + 1. + if (NumRSBlocks <> (NumRSBlocksInGroup1 + NumRSBlocksInGroup2)) then + begin + FEncoderError := True; + Exit; + end; + // 196 = (13 + 26) * 4 + (14 + 26) * 1 + if (NumTotalBytes <> + ((NumDataBytesInGroup1 + NumECBytesInGroup1) * NumRsBlocksInGroup1) + + ((NumDataBytesInGroup2 + NumECBytesInGroup2) * NumRsBlocksInGroup2)) then + begin + FEncoderError := True; + Exit; + end; + + if (BlockID < NumRSBlocksInGroup1) then + begin + NumDataBytesInBlock[0] := NumDataBytesInGroup1; + NumECBytesInBlock[0] := numECBytesInGroup1; + end else + begin + NumDataBytesInBlock[0] := NumDataBytesInGroup2; + NumECBytesInBlock[0] := numEcBytesInGroup2; + end; +end; + +// Interleave "bits" with corresponding error correction bytes. On success, store the result in +// "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details. +procedure TEncoder.InterleaveWithECBytes(Bits: TBitArray; NumTotalBytes, + NumDataBytes, NumRSBlocks: Integer; var Result: TBitArray); +var + DataBytesOffset: Integer; + MaxNumDataBytes: Integer; + MaxNumECBytes: Integer; + Blocks: TObjectList; + NumDataBytesInBlock: TIntegerArray; + NumECBytesInBlock: TIntegerArray; + Size: Integer; + DataBytes: TByteArray; + ECBytes: TByteArray; + I, J: Integer; + BlockPair: TBlockPair; +begin + SetLength(ECBytes, 0); + + // "bits" must have "getNumDataBytes" bytes of data. + if (Bits.GetSizeInBytes <> NumDataBytes) then + begin + FEncoderError := True; + Exit; + end; + + // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll + // store the divided data bytes blocks and error correction bytes blocks into "blocks". + DataBytesOffset := 0; + MaxNumDataBytes := 0; + MaxNumEcBytes := 0; + + // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number. + Blocks := TObjectList.Create(True); + try + Blocks.Capacity := NumRSBlocks; + + for I := 0 to NumRSBlocks - 1 do + begin + SetLength(NumDataBytesInBlock, 1); + SetLength(NumECBytesInBlock, 1); + GetNumDataBytesAndNumECBytesForBlockID( + NumTotalBytes, NumDataBytes, NumRSBlocks, I, + NumDataBytesInBlock, NumEcBytesInBlock); + + Size := NumDataBytesInBlock[0]; + SetLength(DataBytes, Size); + Bits.ToBytes(8 * DataBytesOffset, DataBytes, 0, Size); + ECBytes := GenerateECBytes(DataBytes, NumEcBytesInBlock[0]); + BlockPair := TBlockPair.Create(DataBytes, ECBytes); + Blocks.Add(BlockPair); + + MaxNumDataBytes := Max(MaxNumDataBytes, Size); + MaxNumECBytes := Max(MaxNumECBytes, Length(ECBytes)); + Inc(DataBytesOffset, NumDataBytesInBlock[0]); + end; + if (NumDataBytes <> DataBytesOffset) then + begin + FEncoderError := True; + Exit; + end; + + // First, place data blocks. + for I := 0 to MaxNumDataBytes - 1 do + begin + for J := 0 to Blocks.Count - 1 do + begin + DataBytes := TBlockPair(Blocks.Items[J]).GetDataBytes; + if (I < Length(DataBytes)) then + begin + Result.AppendBits(DataBytes[I], 8); + end; + end; + end; + // Then, place error correction blocks. + for I := 0 to MaxNumECBytes - 1 do + begin + for J := 0 to Blocks.Count - 1 do + begin + ECBytes := TBlockPair(Blocks.Items[J]).GetErrorCorrectionBytes; + if (I < Length(ECBytes)) then + begin + Result.AppendBits(ECBytes[I], 8); + end; + end; + end; + finally + Blocks.Free; + end; + if (numTotalBytes <> Result.GetSizeInBytes) then // Should be same. + begin + FEncoderError := True; + Exit; + end; +end; + +function TEncoder.GenerateECBytes(DataBytes: TByteArray; NumECBytesInBlock: Integer): TByteArray; +var + NumDataBytes: Integer; + ToEncode: TIntegerArray; + ReedSolomonEncoder: TReedSolomonEncoder; + I: Integer; + ECBytes: TByteArray; + GenericGF: TGenericGF; +begin + NumDataBytes := Length(DataBytes); + SetLength(ToEncode, NumDataBytes + NumECBytesInBlock); + + for I := 0 to NumDataBytes - 1 do + begin + ToEncode[I] := DataBytes[I] and $FF; + end; + + GenericGF := TGenericGF.CreateQRCodeField256; + try + ReedSolomonEncoder := TReedSolomonEncoder.Create(GenericGF); + try + ReedSolomonEncoder.Encode(ToEncode, NumECBytesInBlock); + finally + ReedSolomonEncoder.Free; + end; + finally + GenericGF.Free; + end; + + SetLength(ECBytes, NumECBytesInBlock); + for I := 0 to NumECBytesInBlock - 1 do + begin + ECBytes[I] := ToEncode[NumDataBytes + I]; + end; + + Result := ECBytes; +end; + +// Append mode info. On success, store the result in "bits". +procedure TEncoder.AppendModeInfo(Mode: TMode; Bits: TBitArray); +begin + Bits.AppendBits(GetModeBits(Mode), 4); +end; + +// Append length info. On success, store the result in "bits". +procedure TEncoder.AppendLengthInfo(NumLetters, VersionNum: Integer; Mode: TMode; Bits: TBitArray); +var + NumBits: Integer; + Version: TVersion; +begin + Version := TVersion.GetVersionForNumber(VersionNum); + try + NumBits := GetModeCharacterCountBits(Mode, Version); + finally + Version.Free; + end; + + if (NumLetters > ((1 shl NumBits) - 1)) then + begin + FEncoderError := True; + Exit; + end; + + Bits.AppendBits(NumLetters, NumBits); +end; + +// Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". +procedure TEncoder.AppendBytes(const Content: WideString; Mode: TMode; Bits: TBitArray; EncodeOptions: Integer); +begin + if (Mode = qmNumeric) then + begin + AppendNumericBytes(Content, Bits); + end else + if (Mode = qmAlphanumeric) then + begin + AppendAlphanumericBytes(Content, Bits); + end else + if (Mode = qmByte) then + begin + Append8BitBytes(Content, Bits, EncodeOptions); + end else + if (Mode = qmKanji) then + begin + AppendKanjiBytes(Content, Bits); + end else + begin + FEncoderError := True; + Exit; + end; +end; + +procedure TEncoder.AppendNumericBytes(const Content: WideString; Bits: TBitArray); +var + ContentLength: Integer; + I: Integer; + Num1: Integer; + Num2: Integer; + Num3: Integer; +begin + ContentLength := Length(Content); + I := 0; + while (I < ContentLength) do + begin + Num1 := Ord(Content[I + 0 + 1]) - Ord('0'); + if (I + 2 < ContentLength) then + begin + // Encode three numeric letters in ten bits. + Num2 := Ord(Content[I + 1 + 1]) - Ord('0'); + Num3 := Ord(Content[I + 2 + 1]) - Ord('0'); + Bits.AppendBits(Num1 * 100 + Num2 * 10 + Num3, 10); + Inc(I, 3); + end else + if (I + 1 < ContentLength) then + begin + // Encode two numeric letters in seven bits. + Num2 := Ord(Content[I + 1 + 1]) - Ord('0'); + Bits.AppendBits(Num1 * 10 + Num2, 7); + Inc(I, 2); + end else + begin + // Encode one numeric letter in four bits. + Bits.AppendBits(Num1, 4); + Inc(I); + end; + end; +end; + +procedure TEncoder.AppendAlphanumericBytes(const Content: WideString; Bits: TBitArray); +var + ContentLength: Integer; + I: Integer; + Code1: Integer; + Code2: Integer; +begin + ContentLength := Length(Content); + I := 0; + while (I < ContentLength) do + begin + Code1 := GetAlphanumericCode(Ord(Content[I + 0 + 1])); + if (Code1 = -1) then + begin + FEncoderError := True; + Exit; + end; + if (I + 1 < ContentLength) then + begin + Code2 := GetAlphanumericCode(Ord(Content[I + 1 + 1])); + if (Code2 = -1) then + begin + FEncoderError := True; + Exit; + end; + // Encode two alphanumeric letters in 11 bits. + Bits.AppendBits(Code1 * 45 + Code2, 11); + Inc(I, 2); + end else + begin + // Encode one alphanumeric letter in six bits. + Bits.AppendBits(Code1, 6); + Inc(I); + end; + end; +end; + +procedure TEncoder.Append8BitBytes(const Content: WideString; Bits: TBitArray; EncodeOptions: Integer); +var + Bytes: TByteArray; + I: Integer; + UTF8Version: AnsiString; +begin + SetLength(Bytes, 0); + if (EncodeOptions = 3) then + begin + SetLength(Bytes, Length(Content)); + for I := 1 to Length(Content) do + begin + Bytes[I - 1] := Ord(Content[I]) and $FF; + end; + end else + if (EncodeOptions = 4) then + begin + // Add the UTF-8 BOM + UTF8Version := #$EF#$BB#$BF + UTF8Encode(Content); + SetLength(Bytes, Length(UTF8Version)); + if (Length(UTF8Version) > 0) then + begin + Move(UTF8Version[1], Bytes[0], Length(UTF8Version)); + end; + end else + if (EncodeOptions = 5) then + begin + // No BOM + UTF8Version := UTF8Encode(Content); + SetLength(Bytes, Length(UTF8Version)); + if (Length(UTF8Version) > 0) then + begin + Move(UTF8Version[1], Bytes[0], Length(UTF8Version)); + end; + end; + for I := 0 to Length(Bytes) - 1 do + begin + Bits.AppendBits(Bytes[I], 8); + end; +end; + +procedure TEncoder.AppendKanjiBytes(const Content: WideString; Bits: TBitArray); +var + Bytes: TByteArray; + ByteLength: Integer; + I: Integer; + Byte1: Integer; + Byte2: Integer; + Code: Integer; + Subtracted: Integer; + Encoded: Integer; +begin + SetLength(Bytes, 0); + try + + except + FEncoderError := True; + Exit; + end; + + ByteLength := Length(Bytes); + I := 0; + while (I < ByteLength) do + begin + Byte1 := Bytes[I] and $FF; + Byte2 := Bytes[I + 1] and $FF; + Code := (Byte1 shl 8) or Byte2; + Subtracted := -1; + if ((Code >= $8140) and (Code <= $9ffc)) then + begin + Subtracted := Code - $8140; + end else + if ((Code >= $e040) and (Code <= $ebbf)) then + begin + Subtracted := Code - $c140; + end; + if (Subtracted = -1) then + begin + FEncoderError := True; + Exit; + end; + Encoded := ((Subtracted shr 8) * $c0) + (Subtracted and $ff); + Bits.AppendBits(Encoded, 13); + Inc(I, 2); + end; +end; + +procedure TMatrixUtil.ClearMatrix(Matrix: TByteMatrix); +begin + Matrix.Clear(Byte(-1)); +end; + +constructor TMatrixUtil.Create; +begin + FMatrixUtilError := False; +end; + +// Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On +// success, store the result in "matrix" and return true. +procedure TMatrixUtil.BuildMatrix(DataBits: TBitArray; ECLevel: TErrorCorrectionLevel; + Version, MaskPattern: Integer; Matrix: TByteMatrix); +begin + ClearMatrix(Matrix); + EmbedBasicPatterns(Version, Matrix); + + // Type information appear with any version. + EmbedTypeInfo(ECLevel, MaskPattern, Matrix); + + // Version info appear if version >= 7. + MaybeEmbedVersionInfo(Version, Matrix); + + // Data should be embedded at end. + EmbedDataBits(DataBits, MaskPattern, Matrix); +end; + +// Embed basic patterns. On success, modify the matrix and return true. +// The basic patterns are: +// - Position detection patterns +// - Timing patterns +// - Dark dot at the left bottom corner +// - Position adjustment patterns, if need be +procedure TMatrixUtil.EmbedBasicPatterns(Version: Integer; Matrix: TByteMatrix); +begin + // Let's get started with embedding big squares at corners. + EmbedPositionDetectionPatternsAndSeparators(Matrix); + + // Then, embed the dark dot at the left bottom corner. + EmbedDarkDotAtLeftBottomCorner(Matrix); + + // Position adjustment patterns appear if version >= 2. + MaybeEmbedPositionAdjustmentPatterns(Version, Matrix); + + // Timing patterns should be embedded after position adj. patterns. + EmbedTimingPatterns(Matrix); +end; + +// Embed type information. On success, modify the matrix. +procedure TMatrixUtil.EmbedTypeInfo(ECLevel: TErrorCorrectionLevel; MaskPattern: Integer; Matrix: TByteMatrix); +var + TypeInfoBits: TBitArray; + I: Integer; + Bit: Boolean; + X1, Y1: Integer; + X2, Y2: Integer; +begin + TypeInfoBits := TBitArray.Create; + try + MakeTypeInfoBits(ECLevel, MaskPattern, TypeInfoBits); + + for I := 0 to TypeInfoBits.GetSize - 1 do + begin + // Place bits in LSB to MSB order. LSB (least significant bit) is the last value in + // "typeInfoBits". + Bit := TypeInfoBits.Get(TypeInfoBits.GetSize - 1 - I); + + // Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46). + X1 := TYPE_INFO_COORDINATES[I][0]; + Y1 := TYPE_INFO_COORDINATES[I][1]; + Matrix.SetBoolean(X1, Y1, Bit); + + if (I < 8) then + begin + // Right top corner. + X2 := Matrix.Width - I - 1; + Y2 := 8; + Matrix.SetBoolean(X2, Y2, Bit); + end else + begin + // Left bottom corner. + X2 := 8; + Y2 := Matrix.Height - 7 + (I - 8); + Matrix.SetBoolean(X2, Y2, Bit); + end; + end; + finally + TypeInfoBits.Free; + end; +end; + +// Embed version information if need be. On success, modify the matrix and return true. +// See 8.10 of JISX0510:2004 (p.47) for how to embed version information. +procedure TMatrixUtil.MaybeEmbedVersionInfo(Version: Integer; Matrix: TByteMatrix); +var + VersionInfoBits: TBitArray; + I, J: Integer; + BitIndex: Integer; + Bit: Boolean; +begin + if (Version < 7) then + begin + Exit; // Don't need version info. + end; + + VersionInfoBits := TBitArray.Create; + try + MakeVersionInfoBits(Version, VersionInfoBits); + + BitIndex := 6 * 3 - 1; // It will decrease from 17 to 0. + for I := 0 to 5 do + begin + for J := 0 to 2 do + begin + // Place bits in LSB (least significant bit) to MSB order. + Bit := VersionInfoBits.Get(BitIndex); + Dec(BitIndex); + // Left bottom corner. + Matrix.SetBoolean(I, Matrix.Height - 11 + J, Bit); + // Right bottom corner. + Matrix.SetBoolean(Matrix.Height - 11 + J, I, bit); + end; + end; + finally + VersionInfoBits.Free; + end; +end; + +// Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true. +// For debugging purposes, it skips masking process if "getMaskPattern" is -1. +// See 8.7 of JISX0510:2004 (p.38) for how to embed data bits. +procedure TMatrixUtil.EmbedDataBits(DataBits: TBitArray; MaskPattern: Integer; Matrix: TByteMatrix); +var + BitIndex: Integer; + Direction: Integer; + X, Y, I, XX: Integer; + Bit: Boolean; + MaskUtil: TMaskUtil; +begin + MaskUtil := TMaskUtil.Create; + try + bitIndex := 0; + direction := -1; + // Start from the right bottom cell. + X := Matrix.Width - 1; + Y := Matrix.Height - 1; + while (X > 0) do + begin + // Skip the vertical timing pattern. + if (X = 6) then + begin + Dec(X, 1); + end; + while ((Y >= 0) and (y < Matrix.Height)) do + begin + for I := 0 to 1 do + begin + XX := X - I; + // Skip the cell if it's not empty. + if (not IsEmpty(Matrix.Get(XX, Y))) then + begin + Continue; + end; + + if (BitIndex < DataBits.GetSize) then + begin + Bit := DataBits.Get(BitIndex); + Inc(BitIndex); + end else + begin + // Padding bit. If there is no bit left, we'll fill the left cells with 0, as described + // in 8.4.9 of JISX0510:2004 (p. 24). + Bit := False; + end; + + // Skip masking if mask_pattern is -1. + if (MaskPattern <> -1) then + begin + if (MaskUtil.GetDataMaskBit(MaskPattern, XX, Y)) then + begin + Bit := not Bit; + end; + end; + Matrix.SetBoolean(XX, Y, Bit); + end; + Inc(Y, Direction); + end; + Direction := -Direction; // Reverse the direction. + Inc(Y, Direction); + Dec(X, 2); // Move to the left. + end; + finally + MaskUtil.Free; + end; + + // All bits should be consumed. + if (BitIndex <> DataBits.GetSize()) then + begin + FMatrixUtilError := True; + Exit; + end; +end; + +// Return the position of the most significant bit set (to one) in the "value". The most +// significant bit is position 32. If there is no bit set, return 0. Examples: +// - findMSBSet(0) => 0 +// - findMSBSet(1) => 1 +// - findMSBSet(255) => 8 +function TMatrixUtil.FindMSBSet(Value: Integer): Integer; +var + NumDigits: Integer; +begin + NumDigits := 0; + while (Value <> 0) do + begin + Value := Value shr 1; + Inc(NumDigits); + end; + Result := NumDigits; +end; + +// Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH +// code is used for encoding type information and version information. +// Example: Calculation of version information of 7. +// f(x) is created from 7. +// - 7 = 000111 in 6 bits +// - f(x) = x^2 + x^1 + x^0 +// g(x) is given by the standard (p. 67) +// - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1 +// Multiply f(x) by x^(18 - 6) +// - f'(x) = f(x) * x^(18 - 6) +// - f'(x) = x^14 + x^13 + x^12 +// Calculate the remainder of f'(x) / g(x) +// x^2 +// __________________________________________________ +// g(x) )x^14 + x^13 + x^12 +// x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2 +// -------------------------------------------------- +// x^11 + x^10 + x^7 + x^4 + x^2 +// +// The remainder is x^11 + x^10 + x^7 + x^4 + x^2 +// Encode it in binary: 110010010100 +// The return value is 0xc94 (1100 1001 0100) +// +// Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit +// operations. We don't care if cofficients are positive or negative. +function TMatrixUtil.CalculateBCHCode(Value, Poly: Integer): Integer; +var + MSBSetInPoly: Integer; +begin + // If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1 + // from 13 to make it 12. + MSBSetInPoly := FindMSBSet(Poly); + Value := Value shl (MSBSetInPoly - 1); + // Do the division business using exclusive-or operations. + while (FindMSBSet(Value) >= MSBSetInPoly) do + begin + Value := Value xor (Poly shl (FindMSBSet(Value) - MSBSetInPoly)); + end; + // Now the "value" is the remainder (i.e. the BCH code) + Result := Value; +end; + +// Make bit vector of type information. On success, store the result in "bits" and return true. +// Encode error correction level and mask pattern. See 8.9 of +// JISX0510:2004 (p.45) for details. +procedure TMatrixUtil.MakeTypeInfoBits(ECLevel: TErrorCorrectionLevel; MaskPattern: Integer; Bits: TBitArray); +var + TypeInfo: Integer; + BCHCode: Integer; + MaskBits: TBitArray; +begin + if ((MaskPattern >= 0) and (MaskPattern < NUM_MASK_PATTERNS)) then + begin + TypeInfo := (ECLevel.Bits shl 3) or MaskPattern; + Bits.AppendBits(TypeInfo, 5); + + BCHCode := CalculateBCHCode(TypeInfo, TYPE_INFO_POLY); + Bits.AppendBits(BCHCode, 10); + + MaskBits := TBitArray.Create; + try + MaskBits.AppendBits(TYPE_INFO_MASK_PATTERN, 15); + Bits.XorOperation(MaskBits); + finally + MaskBits.Free; + end; + + if (Bits.GetSize <> 15) then // Just in case. + begin + FMatrixUtilError := True; + Exit; + end; + end; +end; + +// Make bit vector of version information. On success, store the result in "bits" and return true. +// See 8.10 of JISX0510:2004 (p.45) for details. +procedure TMatrixUtil.MakeVersionInfoBits(Version: Integer; Bits: TBitArray); +var + BCHCode: Integer; +begin + Bits.AppendBits(Version, 6); + BCHCode := CalculateBCHCode(Version, VERSION_INFO_POLY); + Bits.AppendBits(BCHCode, 12); + + if (Bits.GetSize() <> 18) then + begin + FMatrixUtilError := True; + Exit; + end; +end; + +// Check if "value" is empty. +function TMatrixUtil.IsEmpty(Value: Integer): Boolean; +begin + Result := (Value = -1); +end; + +procedure TMatrixUtil.EmbedTimingPatterns(Matrix: TByteMatrix); +var + I: Integer; + Bit: Integer; +begin + // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical + // separation patterns (size 1). Thus, 8 = 7 + 1. + for I := 8 to Matrix.Width - 9 do + begin + Bit := (I + 1) mod 2; + // Horizontal line. + if (IsEmpty(Matrix.Get(I, 6))) then + begin + Matrix.SetInteger(I, 6, Bit); + end; + // Vertical line. + if (IsEmpty(Matrix.Get(6, I))) then + begin + Matrix.SetInteger(6, I, Bit); + end; + end; +end; + +// Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46) +procedure TMatrixUtil.EmbedDarkDotAtLeftBottomCorner(Matrix: TByteMatrix); +begin + if (Matrix.Get(8, Matrix.Height - 8) = 0) then + begin + FMatrixUtilError := True; + Exit; + end; + Matrix.SetInteger(8, Matrix.Height - 8, 1); +end; + +procedure TMatrixUtil.EmbedHorizontalSeparationPattern(XStart, YStart: Integer; Matrix: TByteMatrix); +var + X: Integer; +begin + // We know the width and height. + for X := 0 to 7 do + begin + if (not IsEmpty(Matrix.Get(XStart + X, YStart))) then + begin + FMatrixUtilError := True; + Exit; + end; + Matrix.SetInteger(XStart + X, YStart, HORIZONTAL_SEPARATION_PATTERN[0][X]); + end; +end; + +procedure TMatrixUtil.EmbedVerticalSeparationPattern(XStart, YStart: Integer; Matrix: TByteMatrix); +var + Y: Integer; +begin + // We know the width and height. + for Y := 0 to 6 do + begin + if (not IsEmpty(Matrix.Get(XStart, YStart + Y))) then + begin + FMatrixUtilError := True; + Exit; + end; + Matrix.SetInteger(XStart, YStart + Y, VERTICAL_SEPARATION_PATTERN[Y][0]); + end; +end; + +// Note that we cannot unify the function with embedPositionDetectionPattern() despite they are +// almost identical, since we cannot write a function that takes 2D arrays in different sizes in +// C/C++. We should live with the fact. +procedure TMatrixUtil.EmbedPositionAdjustmentPattern(XStart, YStart: Integer; Matrix: TByteMatrix); +var + X, Y: Integer; +begin + // We know the width and height. + for Y := 0 to 4 do + begin + for X := 0 to 4 do + begin + if (not IsEmpty(Matrix.Get(XStart + X, YStart + Y))) then + begin + FMatrixUtilError := True; + Exit; + end; + Matrix.SetInteger(XStart + X, YStart + Y, POSITION_ADJUSTMENT_PATTERN[Y][X]); + end; + end; +end; + +procedure TMatrixUtil.EmbedPositionDetectionPattern(XStart, YStart: Integer; Matrix: TByteMatrix); +var + X, Y: Integer; +begin + // We know the width and height. + for Y := 0 to 6 do + begin + for X := 0 to 6 do + begin + if (not IsEmpty(Matrix.Get(XStart + X, YStart + Y))) then + begin + FMatrixUtilError := True; + Exit; + end; + Matrix.SetInteger(XStart + X, YStart + Y, POSITION_DETECTION_PATTERN[Y][X]); + end; + end; +end; + +// Embed position detection patterns and surrounding vertical/horizontal separators. +procedure TMatrixUtil.EmbedPositionDetectionPatternsAndSeparators(Matrix: TByteMatrix); +var + PDPWidth: Integer; + HSPWidth: Integer; + VSPSize: Integer; +begin + // Embed three big squares at corners. + PDPWidth := Length(POSITION_DETECTION_PATTERN[0]); + // Left top corner. + EmbedPositionDetectionPattern(0, 0, Matrix); + // Right top corner. + EmbedPositionDetectionPattern(Matrix.Width - PDPWidth, 0, Matrix); + // Left bottom corner. + EmbedPositionDetectionPattern(0, Matrix.Width- PDPWidth, Matrix); + + // Embed horizontal separation patterns around the squares. + HSPWidth := Length(HORIZONTAL_SEPARATION_PATTERN[0]); + // Left top corner. + EmbedHorizontalSeparationPattern(0, HSPWidth - 1, Matrix); + // Right top corner. + EmbedHorizontalSeparationPattern(Matrix.Width - HSPWidth, + HSPWidth - 1, Matrix); + // Left bottom corner. + EmbedHorizontalSeparationPattern(0, Matrix.Width - HSPWidth, Matrix); + + // Embed vertical separation patterns around the squares. + VSPSize := Length(VERTICAL_SEPARATION_PATTERN); + // Left top corner. + EmbedVerticalSeparationPattern(VSPSize, 0, Matrix); + // Right top corner. + EmbedVerticalSeparationPattern(Matrix.Height - VSPSize - 1, 0, Matrix); + // Left bottom corner. + EmbedVerticalSeparationPattern(VSPSize, Matrix.Height - VSPSize, Matrix); +end; + +// Embed position adjustment patterns if need be. +procedure TMatrixUtil.MaybeEmbedPositionAdjustmentPatterns(Version: Integer; Matrix: TByteMatrix); +var + Index: Integer; + Coordinates: array of Integer; + NumCoordinates: Integer; + X, Y, I, J: Integer; +begin + if (Version >= 2) then + begin + Index := Version - 1; + NumCoordinates := Length(POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[Index]); + SetLength(Coordinates, NumCoordinates); + Move(POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[Index][0], Coordinates[0], NumCoordinates * SizeOf(Integer)); + for I := 0 to NumCoordinates - 1 do + begin + for J := 0 to NumCoordinates - 1 do + begin + Y := Coordinates[I]; + X := Coordinates[J]; + if ((X = -1) or (Y = -1)) then + begin + Continue; + end; + // If the cell is unset, we embed the position adjustment pattern here. + if (IsEmpty(Matrix.Get(X, Y))) then + begin + // -2 is necessary since the x/y coordinates point to the center of the pattern, not the + // left top corner. + EmbedPositionAdjustmentPattern(X - 2, Y - 2, Matrix); + end; + end; + end; + end; +end; + + +{ TBitArray } + + +procedure TBitArray.AppendBits(Value, NumBits: Integer); +var + NumBitsLeft: Integer; +begin + if ((NumBits < 0) or (NumBits > 32)) then + begin + + end; + EnsureCapacity(Size + NumBits); + for NumBitsLeft := NumBits downto 1 do + begin + AppendBit(((Value shr (NumBitsLeft - 1)) and $01) = 1); + end; +end; + +constructor TBitArray.Create(Size: Integer); + +begin + Size := Size; + SetLength(Bits, (Size + 31) shr 5); +end; + +constructor TBitArray.Create; +begin + Size := 0; + SetLength(Bits, 1); +end; + +function TBitArray.Get(I: Integer): Boolean; +begin + Result := (Bits[I shr 5] and (1 shl (I and $1F))) <> 0; +end; + +function TBitArray.GetSize: Integer; +begin + Result := Size; +end; + +function TBitArray.GetSizeInBytes: Integer; +begin + Result := (Size + 7) shr 3; +end; + +procedure TBitArray.SetBit(Index: Integer); +begin + Bits[Index shr 5] := Bits[Index shr 5] or (1 shl (Index and $1F)); +end; + +procedure TBitArray.AppendBit(Bit: Boolean); +begin + EnsureCapacity(Size + 1); + if (Bit) then + begin + Bits[Size shr 5] := Bits[Size shr 5] or (1 shl (Size and $1F)); + end; + Inc(Size); +end; + +procedure TBitArray.ToBytes(BitOffset: Integer; Source: TByteArray; Offset, + NumBytes: Integer); +var + I: Integer; + J: Integer; + TheByte: Integer; +begin + for I := 0 to NumBytes - 1 do + begin + TheByte := 0; + for J := 0 to 7 do + begin + if (Get(BitOffset)) then + begin + TheByte := TheByte or (1 shl (7 - J)); + end; + Inc(BitOffset); + end; + Source[Offset + I] := TheByte; + end; +end; + +procedure TBitArray.XorOperation(Other: TBitArray); +var + I: Integer; +begin + if (Length(Bits) = Length(Other.Bits)) then + begin + for I := 0 to Length(Bits) - 1 do + begin + // The last byte could be incomplete (i.e. not have 8 bits in + // it) but there is no problem since 0 XOR 0 == 0. + Bits[I] := Bits[I] xor Other.Bits[I]; + end; + end; +end; + +procedure TBitArray.AppendBitArray(NewBitArray: TBitArray); +var + OtherSize: Integer; + I: Integer; +begin + OtherSize := NewBitArray.GetSize; + EnsureCapacity(Size + OtherSize); + for I := 0 to OtherSize - 1 do + begin + AppendBit(NewBitArray.Get(I)); + end; +end; + +procedure TBitArray.EnsureCapacity(Size: Integer); +begin + if (Size > (Length(Bits) shl 5)) then + begin + SetLength(Bits, Size); + end; +end; + +{ TErrorCorrectionLevel } + +procedure TErrorCorrectionLevel.Assign(Source: TErrorCorrectionLevel); +begin + Self.FBits := Source.FBits; +end; + +function TErrorCorrectionLevel.Ordinal: Integer; +begin + Result := 0; +end; + +{ TVersion } + +class function TVersion.ChooseVersion(NumInputBits: Integer; + ECLevel: TErrorCorrectionLevel): TVersion; +var + VersionNum: Integer; + Version: TVersion; + NumBytes: Integer; + ECBlocks: TECBlocks; + NumECBytes: Integer; + NumDataBytes: Integer; + TotalInputBytes: Integer; +begin + Result := nil; + // In the following comments, we use numbers of Version 7-H. + for VersionNum := 1 to 40 do + begin + Version := TVersion.GetVersionForNumber(VersionNum); + + // numBytes = 196 + NumBytes := Version.GetTotalCodewords; + // getNumECBytes = 130 + ECBlocks := Version.GetECBlocksForLevel(ECLevel); + NumECBytes := ECBlocks.GetTotalECCodewords; + // getNumDataBytes = 196 - 130 = 66 + NumDataBytes := NumBytes - NumECBytes; + TotalInputBytes := (NumInputBits + 7) div 8; + + if (numDataBytes >= totalInputBytes) then + begin + Result := Version; + Exit; + end else + begin + Version.Free; + end; + end; +end; + +constructor TVersion.Create(VersionNumber: Integer; + AlignmentPatternCenters: array of Integer; ECBlocks1, ECBlocks2, ECBlocks3, + ECBlocks4: TECBlocks); +var + Total: Integer; + ECBlock: TECB; + ECBArray: TECBArray; + I: Integer; +begin + Self.VersionNumber := VersionNumber; + SetLength(Self.AlignmentPatternCenters, Length(AlignmentPatternCenters)); + if (Length(AlignmentPatternCenters) > 0) then + begin + Move(AlignmentPatternCenters[0], Self.AlignmentPatternCenters[0], + Length(AlignmentPatternCenters) * SizeOf(Integer)); + end; + SetLength(ECBlocks, 4); + ECBlocks[0] := ECBlocks1; + ECBlocks[1] := ECBlocks2; + ECBlocks[2] := ECBlocks3; + ECBlocks[3] := ECBlocks4; + Total := 0; + ECCodewords := ECBlocks1.GetECCodewordsPerBlock; + ECBArray := ECBlocks1.GetECBlocks; + for I := 0 to Length(ECBArray) - 1 do + begin + ECBlock := ECBArray[I]; + Inc(Total, ECBlock.GetCount * (ECBlock.GetDataCodewords + ECCodewords)); + end; + TotalCodewords := Total; +end; + +destructor TVersion.Destroy; +var + X: Integer; +begin + for X := 0 to Length(ECBlocks) - 1 do + begin + ECBlocks[X].Free; + end; + inherited; +end; + +function TVersion.GetDimensionForVersion: Integer; +begin + Result := 17 + 4 * VersionNumber; +end; + +function TVersion.GetECBlocksForLevel(ECLevel: TErrorCorrectionLevel): TECBlocks; +begin + Result := ECBlocks[ECLevel.Ordinal]; +end; + +function TVersion.GetTotalCodewords: Integer; +begin + Result := TotalCodewords; +end; + +class function TVersion.GetVersionForNumber(VersionNum: Integer): TVersion; +begin + if (VersionNum = 1) then + begin + Result := TVersion.Create(1, [], + TECBlocks.Create(7, TECB.Create(1, 19)), + TECBlocks.Create(10, TECB.Create(1, 16)), + TECBlocks.Create(13, TECB.Create(1, 13)), + TECBlocks.Create(17, TECB.Create(1, 9))); + end else + if (VersionNum = 2) then + begin + Result := TVersion.Create(2, [6, 18], + TECBlocks.Create(10, TECB.Create(1, 34)), + TECBlocks.Create(16, TECB.Create(1, 28)), + TECBlocks.Create(22, TECB.Create(1, 22)), + TECBlocks.Create(28, TECB.Create(1, 16))); + end else + if (VersionNum = 3) then + begin + Result := TVersion.Create(3, [6, 22], + TECBlocks.Create(15, TECB.Create(1, 55)), + TECBlocks.Create(26, TECB.Create(1, 44)), + TECBlocks.Create(18, TECB.Create(2, 17)), + TECBlocks.Create(22, TECB.Create(2, 13))); + end else + if (VersionNum = 4) then + begin + Result := TVersion.Create(4, [6, 26], + TECBlocks.Create(20, TECB.Create(1, 80)), + TECBlocks.Create(18, TECB.Create(2, 32)), + TECBlocks.Create(26, TECB.Create(2, 24)), + TECBlocks.Create(16, TECB.Create(4, 9))); + end else + if (VersionNum = 5) then + begin + Result := TVersion.Create(5, [6, 30], + TECBlocks.Create(26, TECB.Create(1, 108)), + TECBlocks.Create(24, TECB.Create(2, 43)), + TECBlocks.Create(18, TECB.Create(2, 15), + TECB.Create(2, 16)), + TECBlocks.Create(22, TECB.Create(2, 11), + TECB.Create(2, 12))); + end else + if (VersionNum = 6) then + begin + Result := TVersion.Create(6, [6, 34], + TECBlocks.Create(18, TECB.Create(2, 68)), + TECBlocks.Create(16, TECB.Create(4, 27)), + TECBlocks.Create(24, TECB.Create(4, 19)), + TECBlocks.Create(28, TECB.Create(4, 15))); + end else + if (VersionNum = 7) then + begin + Result := TVersion.Create(7, [6, 22, 38], + TECBlocks.Create(20, TECB.Create(2, 78)), + TECBlocks.Create(18, TECB.Create(4, 31)), + TECBlocks.Create(18, TECB.Create(2, 14), + TECB.Create(4, 15)), + TECBlocks.Create(26, TECB.Create(4, 13), + TECB.Create(1, 14))); + end else + if (VersionNum = 8) then + begin + Result := TVersion.Create(8, [6, 24, 42], + TECBlocks.Create(24, TECB.Create(2, 97)), + TECBlocks.Create(22, TECB.Create(2, 38), + TECB.Create(2, 39)), + TECBlocks.Create(22, TECB.Create(4, 18), + TECB.Create(2, 19)), + TECBlocks.Create(26, TECB.Create(4, 14), + TECB.Create(2, 15))); + end else + if (VersionNum = 9) then + begin + Result := TVersion.Create(9, [6, 26, 46], + TECBlocks.Create(30, TECB.Create(2, 116)), + TECBlocks.Create(22, TECB.Create(3, 36), + TECB.Create(2, 37)), + TECBlocks.Create(20, TECB.Create(4, 16), + TECB.Create(4, 17)), + TECBlocks.Create(24, TECB.Create(4, 12), + TECB.Create(4, 13))); + end else + if (VersionNum = 10) then + begin + Result := TVersion.Create(10, [6, 28, 50], + TECBlocks.Create(18, TECB.Create(2, 68), + TECB.Create(2, 69)), + TECBlocks.Create(26, TECB.Create(4, 43), + TECB.Create(1, 44)), + TECBlocks.Create(24, TECB.Create(6, 19), + TECB.Create(2, 20)), + TECBlocks.Create(28, TECB.Create(6, 15), + TECB.Create(2, 16))); + end else + if (VersionNum = 11) then + begin + Result := TVersion.Create(11, [6, 30, 54], + TECBlocks.Create(20, TECB.Create(4, 81)), + TECBlocks.Create(30, TECB.Create(1, 50), + TECB.Create(4, 51)), + TECBlocks.Create(28, TECB.Create(4, 22), + TECB.Create(4, 23)), + TECBlocks.Create(24, TECB.Create(3, 12), + TECB.Create(8, 13))); + end else + if (VersionNum = 12) then + begin + Result := TVersion.Create(12, [6, 32, 58], + TECBlocks.Create(24, TECB.Create(2, 92), + TECB.Create(2, 93)), + TECBlocks.Create(22, TECB.Create(6, 36), + TECB.Create(2, 37)), + TECBlocks.Create(26, TECB.Create(4, 20), + TECB.Create(6, 21)), + TECBlocks.Create(28, TECB.Create(7, 14), + TECB.Create(4, 15))); + end else + if (VersionNum = 13) then + begin + Result := TVersion.Create(13, [6, 34, 62], + TECBlocks.Create(26, TECB.Create(4, 107)), + TECBlocks.Create(22, TECB.Create(8, 37), + TECB.Create(1, 38)), + TECBlocks.Create(24, TECB.Create(8, 20), + TECB.Create(4, 21)), + TECBlocks.Create(22, TECB.Create(12, 11), + TECB.Create(4, 12))); + end else + if (VersionNum = 14) then + begin + Result := TVersion.Create(14, [6, 26, 46, 66], + TECBlocks.Create(30, TECB.Create(3, 115), + TECB.Create(1, 116)), + TECBlocks.Create(24, TECB.Create(4, 40), + TECB.Create(5, 41)), + TECBlocks.Create(20, TECB.Create(11, 16), + TECB.Create(5, 17)), + TECBlocks.Create(24, TECB.Create(11, 12), + TECB.Create(5, 13))); + end else + if (VersionNum = 15) then + begin + Result := TVersion.Create(15, [6, 26, 48, 70], + TECBlocks.Create(22, TECB.Create(5, 87), + TECB.Create(1, 88)), + TECBlocks.Create(24, TECB.Create(5, 41), + TECB.Create(5, 42)), + TECBlocks.Create(30, TECB.Create(5, 24), + TECB.Create(7, 25)), + TECBlocks.Create(24, TECB.Create(11, 12), + TECB.Create(7, 13))); + end else + if (VersionNum = 16) then + begin + Result := TVersion.Create(16, [6, 26, 50, 74], + TECBlocks.Create(24, TECB.Create(5, 98), + TECB.Create(1, 99)), + TECBlocks.Create(28, TECB.Create(7, 45), + TECB.Create(3, 46)), + TECBlocks.Create(24, TECB.Create(15, 19), + TECB.Create(2, 20)), + TECBlocks.Create(30, TECB.Create(3, 15), + TECB.Create(13, 16))); + end else + if (VersionNum = 17) then + begin + Result := TVersion.Create(17, [6, 30, 54, 78], + TECBlocks.Create(28, TECB.Create(1, 107), + TECB.Create(5, 108)), + TECBlocks.Create(28, TECB.Create(10, 46), + TECB.Create(1, 47)), + TECBlocks.Create(28, TECB.Create(1, 22), + TECB.Create(15, 23)), + TECBlocks.Create(28, TECB.Create(2, 14), + TECB.Create(17, 15))); + end else + if (VersionNum = 18) then + begin + Result := TVersion.Create(18, [6, 30, 56, 82], + TECBlocks.Create(30, TECB.Create(5, 120), + TECB.Create(1, 121)), + TECBlocks.Create(26, TECB.Create(9, 43), + TECB.Create(4, 44)), + TECBlocks.Create(28, TECB.Create(17, 22), + TECB.Create(1, 23)), + TECBlocks.Create(28, TECB.Create(2, 14), + TECB.Create(19, 15))); + end else + if (VersionNum = 19) then + begin + Result := TVersion.Create(19, [6, 30, 58, 86], + TECBlocks.Create(28, TECB.Create(3, 113), + TECB.Create(4, 114)), + TECBlocks.Create(26, TECB.Create(3, 44), + TECB.Create(11, 45)), + TECBlocks.Create(26, TECB.Create(17, 21), + TECB.Create(4, 22)), + TECBlocks.Create(26, TECB.Create(9, 13), + TECB.Create(16, 14))); + end else + if (VersionNum = 20) then + begin + Result := TVersion.Create(20, [6, 34, 62, 90], + TECBlocks.Create(28, TECB.Create(3, 107), + TECB.Create(5, 108)), + TECBlocks.Create(26, TECB.Create(3, 41), + TECB.Create(13, 42)), + TECBlocks.Create(30, TECB.Create(15, 24), + TECB.Create(5, 25)), + TECBlocks.Create(28, TECB.Create(15, 15), + TECB.Create(10, 16))); + end else + if (VersionNum = 21) then + begin + Result := TVersion.Create(21, [6, 28, 50, 72, 94], + TECBlocks.Create(28, TECB.Create(4, 116), + TECB.Create(4, 117)), + TECBlocks.Create(26, TECB.Create(17, 42)), + TECBlocks.Create(28, TECB.Create(17, 22), + TECB.Create(6, 23)), + TECBlocks.Create(30, TECB.Create(19, 16), + TECB.Create(6, 17))); + end else + if (VersionNum = 22) then + begin + Result := TVersion.Create(22, [6, 26, 50, 74, 98], + TECBlocks.Create(28, TECB.Create(2, 111), + TECB.Create(7, 112)), + TECBlocks.Create(28, TECB.Create(17, 46)), + TECBlocks.Create(30, TECB.Create(7, 24), + TECB.Create(16, 25)), + TECBlocks.Create(24, TECB.Create(34, 13))); + end else + if (VersionNum = 23) then + begin + Result := TVersion.Create(23, [6, 30, 54, 78, 102], + TECBlocks.Create(30, TECB.Create(4, 121), + TECB.Create(5, 122)), + TECBlocks.Create(28, TECB.Create(4, 47), + TECB.Create(14, 48)), + TECBlocks.Create(30, TECB.Create(11, 24), + TECB.Create(14, 25)), + TECBlocks.Create(30, TECB.Create(16, 15), + TECB.Create(14, 16))); + end else + if (VersionNum = 24) then + begin + Result := TVersion.Create(24, [6, 28, 54, 80, 106], + TECBlocks.Create(30, TECB.Create(6, 117), + TECB.Create(4, 118)), + TECBlocks.Create(28, TECB.Create(6, 45), + TECB.Create(14, 46)), + TECBlocks.Create(30, TECB.Create(11, 24), + TECB.Create(16, 25)), + TECBlocks.Create(30, TECB.Create(30, 16), + TECB.Create(2, 17))); + end else + if (VersionNum = 25) then + begin + Result := TVersion.Create(25, [6, 32, 58, 84, 110], + TECBlocks.Create(26, TECB.Create(8, 106), + TECB.Create(4, 107)), + TECBlocks.Create(28, TECB.Create(8, 47), + TECB.Create(13, 48)), + TECBlocks.Create(30, TECB.Create(7, 24), + TECB.Create(22, 25)), + TECBlocks.Create(30, TECB.Create(22, 15), + TECB.Create(13, 16))); + end else + if (VersionNum = 26) then + begin + Result := TVersion.Create(26, [6, 30, 58, 86, 114], + TECBlocks.Create(28, TECB.Create(10, 114), + TECB.Create(2, 115)), + TECBlocks.Create(28, TECB.Create(19, 46), + TECB.Create(4, 47)), + TECBlocks.Create(28, TECB.Create(28, 22), + TECB.Create(6, 23)), + TECBlocks.Create(30, TECB.Create(33, 16), + TECB.Create(4, 17))); + end else + if (VersionNum = 27) then + begin + Result := TVersion.Create(27, [6, 34, 62, 90, 118], + TECBlocks.Create(30, TECB.Create(8, 122), + TECB.Create(4, 123)), + TECBlocks.Create(28, TECB.Create(22, 45), + TECB.Create(3, 46)), + TECBlocks.Create(30, TECB.Create(8, 23), + TECB.Create(26, 24)), + TECBlocks.Create(30, TECB.Create(12, 15), + TECB.Create(28, 16))); + end else + if (VersionNum = 28) then + begin + Result := TVersion.Create(28, [6, 26, 50, 74, 98, 122], + TECBlocks.Create(30, TECB.Create(3, 117), + TECB.Create(10, 118)), + TECBlocks.Create(28, TECB.Create(3, 45), + TECB.Create(23, 46)), + TECBlocks.Create(30, TECB.Create(4, 24), + TECB.Create(31, 25)), + TECBlocks.Create(30, TECB.Create(11, 15), + TECB.Create(31, 16))); + end else + if (VersionNum = 29) then + begin + Result := TVersion.Create(29, [6, 30, 54, 78, 102, 126], + TECBlocks.Create(30, TECB.Create(7, 116), + TECB.Create(7, 117)), + TECBlocks.Create(28, TECB.Create(21, 45), + TECB.Create(7, 46)), + TECBlocks.Create(30, TECB.Create(1, 23), + TECB.Create(37, 24)), + TECBlocks.Create(30, TECB.Create(19, 15), + TECB.Create(26, 16))); + end else + if (VersionNum = 30) then + begin + Result := TVersion.Create(30, [6, 26, 52, 78, 104, 130], + TECBlocks.Create(30, TECB.Create(5, 115), + TECB.Create(10, 116)), + TECBlocks.Create(28, TECB.Create(19, 47), + TECB.Create(10, 48)), + TECBlocks.Create(30, TECB.Create(15, 24), + TECB.Create(25, 25)), + TECBlocks.Create(30, TECB.Create(23, 15), + TECB.Create(25, 16))); + end else + if (VersionNum = 31) then + begin + Result := TVersion.Create(31, [6, 30, 56, 82, 108, 134], + TECBlocks.Create(30, TECB.Create(13, 115), + TECB.Create(3, 116)), + TECBlocks.Create(28, TECB.Create(2, 46), + TECB.Create(29, 47)), + TECBlocks.Create(30, TECB.Create(42, 24), + TECB.Create(1, 25)), + TECBlocks.Create(30, TECB.Create(23, 15), + TECB.Create(28, 16))); + end else + if (VersionNum = 32) then + begin + Result := TVersion.Create(32, [6, 34, 60, 86, 112, 138], + TECBlocks.Create(30, TECB.Create(17, 115)), + TECBlocks.Create(28, TECB.Create(10, 46), + TECB.Create(23, 47)), + TECBlocks.Create(30, TECB.Create(10, 24), + TECB.Create(35, 25)), + TECBlocks.Create(30, TECB.Create(19, 15), + TECB.Create(35, 16))); + end else + if (VersionNum = 33) then + begin + Result := TVersion.Create(33, [6, 30, 58, 86, 114, 142], + TECBlocks.Create(30, TECB.Create(17, 115), + TECB.Create(1, 116)), + TECBlocks.Create(28, TECB.Create(14, 46), + TECB.Create(21, 47)), + TECBlocks.Create(30, TECB.Create(29, 24), + TECB.Create(19, 25)), + TECBlocks.Create(30, TECB.Create(11, 15), + TECB.Create(46, 16))); + end else + if (VersionNum = 34) then + begin + Result := TVersion.Create(34, [6, 34, 62, 90, 118, 146], + TECBlocks.Create(30, TECB.Create(13, 115), + TECB.Create(6, 116)), + TECBlocks.Create(28, TECB.Create(14, 46), + TECB.Create(23, 47)), + TECBlocks.Create(30, TECB.Create(44, 24), + TECB.Create(7, 25)), + TECBlocks.Create(30, TECB.Create(59, 16), + TECB.Create(1, 17))); + end else + if (VersionNum = 35) then + begin + Result := TVersion.Create(35, [6, 30, 54, 78, 102, 126, 150], + TECBlocks.Create(30, TECB.Create(12, 121), + TECB.Create(7, 122)), + TECBlocks.Create(28, TECB.Create(12, 47), + TECB.Create(26, 48)), + TECBlocks.Create(30, TECB.Create(39, 24), + TECB.Create(14, 25)), + TECBlocks.Create(30, TECB.Create(22, 15), + TECB.Create(41, 16))); + end else + if (VersionNum = 36) then + begin + Result := TVersion.Create(36, [6, 24, 50, 76, 102, 128, 154], + TECBlocks.Create(30, TECB.Create(6, 121), + TECB.Create(14, 122)), + TECBlocks.Create(28, TECB.Create(6, 47), + TECB.Create(34, 48)), + TECBlocks.Create(30, TECB.Create(46, 24), + TECB.Create(10, 25)), + TECBlocks.Create(30, TECB.Create(2, 15), + TECB.Create(64, 16))); + end else + if (VersionNum = 37) then + begin + Result := TVersion.Create(37, [6, 28, 54, 80, 106, 132, 158], + TECBlocks.Create(30, TECB.Create(17, 122), + TECB.Create(4, 123)), + TECBlocks.Create(28, TECB.Create(29, 46), + TECB.Create(14, 47)), + TECBlocks.Create(30, TECB.Create(49, 24), + TECB.Create(10, 25)), + TECBlocks.Create(30, TECB.Create(24, 15), + TECB.Create(46, 16))); + end else + if (VersionNum = 38) then + begin + Result := TVersion.Create(38, [6, 32, 58, 84, 110, 136, 162], + TECBlocks.Create(30, TECB.Create(4, 122), + TECB.Create(18, 123)), + TECBlocks.Create(28, TECB.Create(13, 46), + TECB.Create(32, 47)), + TECBlocks.Create(30, TECB.Create(48, 24), + TECB.Create(14, 25)), + TECBlocks.Create(30, TECB.Create(42, 15), + TECB.Create(32, 16))); + end else + if (VersionNum = 39) then + begin + Result := TVersion.Create(39, [6, 26, 54, 82, 110, 138, 166], + TECBlocks.Create(30, TECB.Create(20, 117), + TECB.Create(4, 118)), + TECBlocks.Create(28, TECB.Create(40, 47), + TECB.Create(7, 48)), + TECBlocks.Create(30, TECB.Create(43, 24), + TECB.Create(22, 25)), + TECBlocks.Create(30, TECB.Create(10, 15), + TECB.Create(67, 16))); + end else + if (VersionNum = 40) then + begin + Result := TVersion.Create(40, [6, 30, 58, 86, 114, 142, 170], + TECBlocks.Create(30, TECB.Create(19, 118), + TECB.Create(6, 119)), + TECBlocks.Create(28, TECB.Create(18, 47), + TECB.Create(31, 48)), + TECBlocks.Create(30, TECB.Create(34, 24), + TECB.Create(34, 25)), + TECBlocks.Create(30, TECB.Create(20, 15), + TECB.Create(61, 16))); + end else + begin + Result := nil; + end; +end; + +{ TMaskUtil } + +// Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask +// pattern conditions. +function TMaskUtil.GetDataMaskBit(MaskPattern, X, Y: Integer): Boolean; +var + Intermediate: Integer; + Temp: Integer; +begin + Intermediate := 0; + if ((MaskPattern >= 0) and (MaskPattern < NUM_MASK_PATTERNS)) then + begin + case (maskPattern) of + 0: Intermediate := (Y + X) and 1; + 1: Intermediate := Y and 1; + 2: Intermediate := X mod 3; + 3: Intermediate := (Y + X) mod 3; + 4: Intermediate := ((y shr 1) + (X div 3)) and 1; + 5: + begin + Temp := Y * X; + Intermediate := (Temp and 1) + (Temp mod 3); + end; + 6: + begin + Temp := Y * X; + Intermediate := ((Temp and 1) + (Temp mod 3)) and 1; + end; + 7: + begin + Temp := Y * X; + Intermediate := ((temp mod 3) + ((Y + X) and 1)) and 1; + end; + end; + end; + Result := Intermediate = 0; +end; + +{ TECBlocks } + +constructor TECBlocks.Create(ECCodewordsPerBlock: Integer; ECBlocks: TECB); +begin + Self.ECCodewordsPerBlock := ECCodewordsPerBlock; + SetLength(Self.ECBlocks, 1); + Self.ECBlocks[0] := ECBlocks; +end; + +constructor TECBlocks.Create(ECCodewordsPerBlock: Integer; ECBlocks1, + ECBlocks2: TECB); +begin + Self.ECCodewordsPerBlock := ECCodewordsPerBlock; + SetLength(Self.ECBlocks, 2); + ECBlocks[0] := ECBlocks1; + ECBlocks[1] := ECBlocks2; +end; + +destructor TECBlocks.Destroy; +var + X: Integer; +begin + for X := 0 to Length(ECBlocks) - 1 do + begin + ECBlocks[X].Free; + end; + inherited; +end; + +function TECBlocks.GetECBlocks: TECBArray; +begin + Result := ECBlocks; +end; + +function TECBlocks.GetECCodewordsPerBlock: Integer; +begin + Result := ECCodewordsPerBlock; +end; + +function TECBlocks.GetNumBlocks: Integer; +var + Total: Integer; + I: Integer; +begin + Total := 0; + for I := 0 to Length(ECBlocks) - 1 do + begin + Inc(Total, ECBlocks[I].GetCount); + end; + Result := Total; +end; + +function TECBlocks.GetTotalECCodewords: Integer; +begin + Result := ECCodewordsPerBlock * GetNumBlocks; +end; + +{ TBlockPair } + +constructor TBlockPair.Create(BA1, BA2: TByteArray); +begin + FDataBytes := BA1; + FErrorCorrectionBytes := BA2; +end; + +function TBlockPair.GetDataBytes: TByteArray; +begin + Result := FDataBytes; +end; + +function TBlockPair.GetErrorCorrectionBytes: TByteArray; +begin + Result := FErrorCorrectionBytes; +end; + +{ TReedSolomonEncoder } + +function TReedSolomonEncoder.BuildGenerator(Degree: Integer): TGenericGFPoly; +var + LastGenerator: TGenericGFPoly; + NextGenerator: TGenericGFPoly; + Poly: TGenericGFPoly; + D: Integer; + CA: TIntegerArray; +begin + if (Degree >= FCachedGenerators.Count) then + begin + LastGenerator := TGenericGFPoly(FCachedGenerators[FCachedGenerators.Count - 1]); + + for D := FCachedGenerators.Count to Degree do + begin + SetLength(CA, 2); + CA[0] := 1; + CA[1] := FField.Exp(D - 1 + FField.GetGeneratorBase); + Poly := TGenericGFPoly.Create(FField, CA); + NextGenerator := LastGenerator.Multiply(Poly); + FCachedGenerators.Add(NextGenerator); + LastGenerator := NextGenerator; + end; + end; + Result := TGenericGFPoly(FCachedGenerators[Degree]); +end; + +constructor TReedSolomonEncoder.Create(AField: TGenericGF); +var + GenericGFPoly: TGenericGFPoly; + IntArray: TIntegerArray; +begin + FField := AField; + + // Contents of FCachedGenerators will be freed by FGenericGF.Destroy + FCachedGenerators := TObjectList.Create(False); + + SetLength(IntArray, 1); + IntArray[0] := 1; + GenericGFPoly := TGenericGFPoly.Create(AField, IntArray); + FCachedGenerators.Add(GenericGFPoly); +end; + +destructor TReedSolomonEncoder.Destroy; +begin + FCachedGenerators.Free; + inherited; +end; + +procedure TReedSolomonEncoder.Encode(ToEncode: TIntegerArray; ECBytes: Integer); +var + DataBytes: Integer; + Generator: TGenericGFPoly; + InfoCoefficients: TIntegerArray; + Info: TGenericGFPoly; + Remainder: TGenericGFPoly; + Coefficients: TIntegerArray; + NumZeroCoefficients: Integer; + I: Integer; +begin + SetLength(Coefficients, 0); + if (ECBytes > 0) then + begin + DataBytes := Length(ToEncode) - ECBytes; + if (DataBytes > 0) then + begin + Generator := BuildGenerator(ECBytes); + SetLength(InfoCoefficients, DataBytes); + InfoCoefficients := Copy(ToEncode, 0, DataBytes); + Info := TGenericGFPoly.Create(FField, InfoCoefficients); + Info := Info.MultiplyByMonomial(ECBytes, 1); + Remainder := Info.Divide(Generator)[1]; + Coefficients := Remainder.GetCoefficients; + NumZeroCoefficients := ECBytes - Length(Coefficients); + for I := 0 to NumZeroCoefficients - 1 do + begin + ToEncode[DataBytes + I] := 0; + end; + Move(Coefficients[0], ToEncode[DataBytes + NumZeroCoefficients], Length(Coefficients) * SizeOf(Integer)); + end; + end; +end; + +{ TECB } + +constructor TECB.Create(Count, DataCodewords: Integer); +begin + Self.Count := Count; + Self.DataCodewords := DataCodewords; +end; + +function TECB.GetCount: Integer; +begin + Result := Count; +end; + +function TECB.GetDataCodewords: Integer; +begin + Result := DataCodewords; +end; + +{ TGenericGFPoly } + +function TGenericGFPoly.AddOrSubtract(Other: TGenericGFPoly): TGenericGFPoly; +var + SmallerCoefficients: TIntegerArray; + LargerCoefficients: TIntegerArray; + Temp: TIntegerArray; + SumDiff: TIntegerArray; + LengthDiff: Integer; + I: Integer; +begin + SetLength(SmallerCoefficients, 0); + SetLength(LargerCoefficients, 0); + SetLength(Temp, 0); + SetLength(SumDiff, 0); + + Result := nil; + if (Assigned(Other)) then + begin + if (FField = Other.FField) then + begin + if (IsZero) then + begin + Result := Other; + Exit; + end; + + if (Other.IsZero) then + begin + Result := Self; + Exit; + end; + + SmallerCoefficients := FCoefficients; + LargerCoefficients := Other.Coefficients; + if (Length(SmallerCoefficients) > Length(LargerCoefficients)) then + begin + Temp := smallerCoefficients; + SmallerCoefficients := LargerCoefficients; + LargerCoefficients := temp; + end; + SetLength(SumDiff, Length(LargerCoefficients)); + LengthDiff := Length(LargerCoefficients) - Length(SmallerCoefficients); + + // Copy high-order terms only found in higher-degree polynomial's coefficients + if (LengthDiff > 0) then + begin + //SumDiff := Copy(LargerCoefficients, 0, LengthDiff); + Move(LargerCoefficients[0], SumDiff[0], LengthDiff * SizeOf(Integer)); + end; + + for I := LengthDiff to Length(LargerCoefficients) - 1 do + begin + SumDiff[I] := TGenericGF.AddOrSubtract(SmallerCoefficients[I - LengthDiff], LargerCoefficients[I]); + end; + + Result := TGenericGFPoly.Create(FField, SumDiff); + end; + end; +end; + +function TGenericGFPoly.Coefficients: TIntegerArray; +begin + Result := FCoefficients; +end; + +constructor TGenericGFPoly.Create(AField: TGenericGF; + ACoefficients: TIntegerArray); +var + CoefficientsLength: Integer; + FirstNonZero: Integer; +begin + FField := AField; + SetLength(FField.FPolyList, Length(FField.FPolyList) + 1); + FField.FPolyList[Length(FField.FPolyList) - 1] := Self; + CoefficientsLength := Length(ACoefficients); + if ((CoefficientsLength > 1) and (ACoefficients[0] = 0)) then + begin + // Leading term must be non-zero for anything except the constant polynomial "0" + FirstNonZero := 1; + while ((FirstNonZero < CoefficientsLength) and (ACoefficients[FirstNonZero] = 0)) do + begin + Inc(FirstNonZero); + end; + + if (FirstNonZero = CoefficientsLength) then + begin + FCoefficients := AField.GetZero.Coefficients; + end else + begin + SetLength(FCoefficients, CoefficientsLength - FirstNonZero); + FCoefficients := Copy(ACoefficients, FirstNonZero, Length(FCoefficients)); + end; + end else + begin + FCoefficients := ACoefficients; + end; +end; + +destructor TGenericGFPoly.Destroy; +begin + Self.FField := FField; + inherited; +end; + +function TGenericGFPoly.Divide(Other: TGenericGFPoly): TGenericGFPolyArray; +var + Quotient: TGenericGFPoly; + Remainder: TGenericGFPoly; + DenominatorLeadingTerm: Integer; + InverseDenominatorLeadingTerm: integer; + DegreeDifference: Integer; + Scale: Integer; + Term: TGenericGFPoly; + IterationQuotient: TGenericGFPoly; +begin + SetLength(Result, 0); + if ((FField = Other.FField) and (not Other.IsZero)) then + begin + + Quotient := FField.GetZero; + Remainder := Self; + + DenominatorLeadingTerm := Other.GetCoefficient(Other.GetDegree); + InverseDenominatorLeadingTerm := FField.Inverse(DenominatorLeadingTerm); + + while ((Remainder.GetDegree >= Other.GetDegree) and (not Remainder.IsZero)) do + begin + DegreeDifference := Remainder.GetDegree - Other.GetDegree; + Scale := FField.Multiply(Remainder.GetCoefficient(Remainder.GetDegree), InverseDenominatorLeadingTerm); + Term := Other.MultiplyByMonomial(DegreeDifference, Scale); + IterationQuotient := FField.BuildMonomial(degreeDifference, scale); + Quotient := Quotient.AddOrSubtract(IterationQuotient); + Remainder := Remainder.AddOrSubtract(Term); + end; + + SetLength(Result, 2); + Result[0] := Quotient; + Result[1] := Remainder; + end; +end; + +function TGenericGFPoly.GetCoefficient(Degree: Integer): Integer; +begin + Result := FCoefficients[Length(FCoefficients) - 1 - Degree]; +end; + +function TGenericGFPoly.GetCoefficients: TIntegerArray; +begin + Result := FCoefficients; +end; + +function TGenericGFPoly.GetDegree: Integer; +begin + Result := Length(FCoefficients) - 1; +end; + +function TGenericGFPoly.IsZero: Boolean; +begin + Result := FCoefficients[0] = 0; +end; + +function TGenericGFPoly.Multiply(Other: TGenericGFPoly): TGenericGFPoly; +var + ACoefficients: TIntegerArray; + BCoefficients: TIntegerArray; + Product: TIntegerArray; + ALength: Integer; + BLength: Integer; + I: Integer; + J: Integer; + ACoeff: Integer; +begin + SetLength(ACoefficients, 0); + SetLength(BCoefficients, 0); + Result := nil; + + if (FField = Other.FField) then + begin + if (IsZero or Other.IsZero) then + begin + Result := FField.GetZero; + Exit; + end; + + ACoefficients := FCoefficients; + ALength := Length(ACoefficients); + BCoefficients := Other.Coefficients; + BLength := Length(BCoefficients); + SetLength(Product, aLength + bLength - 1); + for I := 0 to ALength - 1 do + begin + ACoeff := ACoefficients[I]; + for J := 0 to BLength - 1 do + begin + Product[I + J] := TGenericGF.AddOrSubtract(Product[I + J], + FField.Multiply(ACoeff, BCoefficients[J])); + end; + end; + Result := TGenericGFPoly.Create(FField, Product); + end; +end; + +function TGenericGFPoly.MultiplyByMonomial(Degree, + Coefficient: Integer): TGenericGFPoly; +var + I: Integer; + Size: Integer; + Product: TIntegerArray; +begin + Result := nil; + if (Degree >= 0) then + begin + if (Coefficient = 0) then + begin + Result := FField.GetZero; + Exit; + end; + Size := Length(Coefficients); + SetLength(Product, Size + Degree); + for I := 0 to Size - 1 do + begin + Product[I] := FField.Multiply(FCoefficients[I], Coefficient); + end; + Result := TGenericGFPoly.Create(FField, Product); + end; +end; + +{ TGenericGF } + +class function TGenericGF.AddOrSubtract(A, B: Integer): Integer; +begin + Result := A xor B; +end; + +function TGenericGF.BuildMonomial(Degree, Coefficient: Integer): TGenericGFPoly; +var + Coefficients: TIntegerArray; +begin + CheckInit(); + + if (Degree >= 0) then + begin + if (Coefficient = 0) then + begin + Result := FZero; + Exit; + end; + SetLength(Coefficients, Degree + 1); + Coefficients[0] := Coefficient; + Result := TGenericGFPoly.Create(Self, Coefficients); + end else + begin + Result := nil; + end; +end; + +procedure TGenericGF.CheckInit; +begin + if (not FInitialized) then + begin + Initialize; + end; +end; + +constructor TGenericGF.Create(Primitive, Size, B: Integer); +begin + FInitialized := False; + FPrimitive := Primitive; + FSize := Size; + FGeneratorBase := B; + if (FSize < 0) then + begin + Initialize; + end; +end; + +class function TGenericGF.CreateQRCodeField256: TGenericGF; +begin + Result := TGenericGF.Create($011D, 256, 0); +end; + +destructor TGenericGF.Destroy; +var + X: Integer; + Y: Integer; +begin + for X := 0 to Length(FPolyList) - 1 do + begin + if (Assigned(FPolyList[X])) then + begin + for Y := X + 1 to Length(FPolyList) - 1 do + begin + if (FPolyList[Y] = FPolyList[X]) then + begin + FPolyList[Y] := nil; + end; + end; + FPolyList[X].Free; + end; + end; + inherited; +end; + +function TGenericGF.Exp(A: Integer): Integer; +begin + CheckInit; + Result := FExpTable[A]; +end; + +function TGenericGF.GetGeneratorBase: Integer; +begin + Result := FGeneratorBase; +end; + +function TGenericGF.GetZero: TGenericGFPoly; +begin + CheckInit; + Result := FZero; +end; + +procedure TGenericGF.Initialize; +var + X: Integer; + I: Integer; + CA: TIntegerArray; +begin + SetLength(FExpTable, FSize); + SetLength(FLogTable, FSize); + X := 1; + for I := 0 to FSize - 1 do + begin + FExpTable[I] := x; + X := X shl 1; // x = x * 2; we're assuming the generator alpha is 2 + if (X >= FSize) then + begin + X := X xor FPrimitive; + X := X and (FSize - 1); + end; + end; + + for I := 0 to FSize - 2 do + begin + FLogTable[FExpTable[I]] := I; + end; + + // logTable[0] == 0 but this should never be used + + SetLength(CA, 1); + CA[0] := 0; + FZero := TGenericGFPoly.Create(Self, CA); + + SetLength(CA, 1); + CA[0] := 1; + FOne := TGenericGFPoly.Create(Self, CA); + + FInitialized := True; +end; + +function TGenericGF.Inverse(A: Integer): Integer; +begin + CheckInit; + + if (a <> 0) then + begin + Result := FExpTable[FSize - FLogTable[A] - 1]; + end else + begin + Result := 0; + end; +end; + +function TGenericGF.Multiply(A, B: Integer): Integer; +begin + CheckInit; + if ((A <> 0) and (B <> 0)) then + begin + Result := FExpTable[(FLogTable[A] + FLogTable[B]) mod (FSize - 1)]; + end else + begin + Result := 0; + end; +end; + +function GenerateQRCode(const Input: WideString; EncodeOptions: Integer): T2DBooleanArray; +var + Encoder: TEncoder; + Level: TErrorCorrectionLevel; + QRCode: TQRCode; + X: Integer; + Y: Integer; +begin + Level := TErrorCorrectionLevel.Create; + Level.FBits := 1; + Encoder := TEncoder.Create; + QRCode := TQRCode.Create; + try + Encoder.Encode(Input, EncodeOptions, Level, QRCode); + if (Assigned(QRCode.FMatrix)) then + begin + SetLength(Result, QRCode.FMatrix.FHeight); + for Y := 0 to QRCode.FMatrix.FHeight - 1 do + begin + SetLength(Result[Y], QRCode.FMatrix.FWidth); + for X := 0 to QRCode.FMatrix.FWidth - 1 do + begin + Result[Y][X] := QRCode.FMatrix.Get(Y, X) = 1; + end; + end; + end; + finally + QRCode.Free; + Encoder.Free; + Level.Free; + end; +end; + +{ TDelphiZXingQRCode } + +constructor TDelphiZXingQRCode.Create; +begin + FData := ''; + FEncoding := qrAuto; + FQuietZone := 4; + FRows := 0; + FColumns := 0; +end; + +function TDelphiZXingQRCode.GetIsBlack(Row, Column: Integer): Boolean; +begin + Dec(Row, FQuietZone); + Dec(Column, FQuietZone); + if ((Row >= 0) and (Column >= 0) and (Row < (FRows - FQuietZone * 2)) and (Column < (FColumns - FQuietZone * 2))) then + begin + Result := FElements[Column, Row]; + end else + begin + Result := False; + end; +end; + +procedure TDelphiZXingQRCode.SetData(const NewData: WideString); +begin + if (FData <> NewData) then + begin + FData := NewData; + Update; + end; +end; + +procedure TDelphiZXingQRCode.SetEncoding(NewEncoding: TQRCodeEncoding); +begin + if (FEncoding <> NewEncoding) then + begin + FEncoding := NewEncoding; + Update; + end; +end; + +procedure TDelphiZXingQRCode.SetQuietZone(NewQuietZone: Integer); +begin + if ((FQuietZone <> NewQuietZone) and (NewQuietZone >= 0) and (NewQuietZone <= 100)) then + begin + FQuietZone := NewQuietZone; + Update; + end; +end; + +procedure TDelphiZXingQRCode.Update; +begin + FElements := GenerateQRCode(FData, Ord(FEncoding)); + FRows := Length(FElements) + FQuietZone * 2; + FColumns := FRows; +end; + +end. \ No newline at end of file diff --git a/TestApp/DelphiZXingQRCodeTestApp.dpr b/TestApp/DelphiZXingQRCodeTestApp.dpr new file mode 100644 index 0000000..8c121a5 --- /dev/null +++ b/TestApp/DelphiZXingQRCodeTestApp.dpr @@ -0,0 +1,14 @@ +program DelphiZXingQRCodeTestApp; + +uses + Vcl.Forms, + DelphiZXingQRCodeTestAppMainForm in 'DelphiZXingQRCodeTestAppMainForm.pas' {Form1}; + +{$R *.res} + +begin + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git a/TestApp/DelphiZXingQRCodeTestApp.dproj b/TestApp/DelphiZXingQRCodeTestApp.dproj new file mode 100644 index 0000000..f72f248 --- /dev/null +++ b/TestApp/DelphiZXingQRCodeTestApp.dproj @@ -0,0 +1,156 @@ + + + {9B95C818-479B-45EB-917E-C5AC561D7C60} + 13.4 + VCL + DelphiZXingQRCodeTestApp.dpr + True + Debug + Win32 + 1 + Application + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + ..\Source\;$(DCC_UnitSearchPath) + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + None + 7177 + bindcompfmx;dsnap;fmx;rtl;dbrtl;fmxase;bindcomp;fmxobj;xmlrtl;fmxdae;bindengine;$(DCC_UsePackage) + $(BDS)\bin\delphi_PROJECTICON.ico + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + + + bindcompvcl;vcltouch;VclSmp;vcl;dsnapcon;vclx;vclimg;vclactnband;vcldb;vcldsnap;$(DCC_UsePackage) + + + bindcompvcl;vcltouch;vcldbx;VclSmp;vcl;IndyCore;IndySystem;dsnapcon;DelphiAdobeReaderActiveX;vclx;svnui;svn;vclimg;fmi;IndyProtocols;bdertl;vclactnband;vcldb;vcldsnap;$(DCC_UsePackage) + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + 1033 + $(BDS)\bin\default_app.manifest + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + + + DEBUG;$(DCC_Define) + false + true + true + true + + + true + 1033 + false + + + false + RELEASE;$(DCC_Define) + 0 + false + + + + MainSource + + +
Form1
+ dfm +
+ + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 7177 + 1252 + + + + + 1.0.0.0 + + + + + + 1.0.0.0 + + + + DelphiZXingQRCodeTestApp.dpr + + + CodeSite Express 5.1 + + + + + False + True + + + 12 + + + +
diff --git a/TestApp/DelphiZXingQRCodeTestApp.res b/TestApp/DelphiZXingQRCodeTestApp.res new file mode 100644 index 0000000000000000000000000000000000000000..c287ee9863dccfc9aa3b7e6b7563332879c2b5b1 GIT binary patch literal 298600 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 literal 0 HcmV?d00001 diff --git a/TestApp/DelphiZXingQRCodeTestAppMainForm.dfm b/TestApp/DelphiZXingQRCodeTestAppMainForm.dfm new file mode 100644 index 0000000..51c5eb0 --- /dev/null +++ b/TestApp/DelphiZXingQRCodeTestAppMainForm.dfm @@ -0,0 +1,97 @@ +object Form1: TForm1 + Left = 0 + Top = 0 + Caption = 'Delphi port of ZXing QRCode' + ClientHeight = 282 + ClientWidth = 534 + Color = clBtnFace + Constraints.MinHeight = 320 + Constraints.MinWidth = 550 + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = False + OnCreate = FormCreate + OnDestroy = FormDestroy + DesignSize = ( + 534 + 282) + PixelsPerInch = 96 + TextHeight = 13 + object Label1: TLabel + Left = 8 + Top = 13 + Width = 22 + Height = 13 + Caption = 'Text' + end + object Label2: TLabel + Left = 8 + Top = 69 + Width = 43 + Height = 13 + Caption = 'Encoding' + end + object Label3: TLabel + Left = 184 + Top = 69 + Width = 52 + Height = 13 + Caption = 'Quiet zone' + end + object Label4: TLabel + Left = 296 + Top = 13 + Width = 38 + Height = 13 + Caption = 'Preview' + end + object PaintBox1: TPaintBox + Left = 296 + Top = 32 + Width = 230 + Height = 242 + Anchors = [akLeft, akTop, akRight, akBottom] + OnPaint = PaintBox1Paint + ExplicitWidth = 331 + ExplicitHeight = 260 + end + object edtText: TEdit + Left = 8 + Top = 32 + Width = 265 + Height = 21 + TabOrder = 0 + Text = 'Hello world' + OnChange = edtTextChange + end + object cmbEncoding: TComboBox + Left = 8 + Top = 88 + Width = 145 + Height = 21 + Style = csDropDownList + ItemIndex = 0 + TabOrder = 1 + Text = 'Auto' + OnChange = cmbEncodingChange + Items.Strings = ( + 'Auto' + 'Numeric' + 'Alphanumeric' + 'ISO-8859-1' + 'UTF-8 without BOM' + 'UTF-8 with BOM') + end + object edtQuietZone: TEdit + Left = 184 + Top = 88 + Width = 89 + Height = 21 + TabOrder = 2 + Text = '4' + OnChange = edtQuietZoneChange + end +end diff --git a/TestApp/DelphiZXingQRCodeTestAppMainForm.pas b/TestApp/DelphiZXingQRCodeTestAppMainForm.pas new file mode 100644 index 0000000..898aaa9 --- /dev/null +++ b/TestApp/DelphiZXingQRCodeTestAppMainForm.pas @@ -0,0 +1,117 @@ +unit DelphiZXingQRCodeTestAppMainForm; + +// Demo app for ZXing QRCode port to Delphi, by Debenu Pty Ltd +// www.debenu.com + +interface + +uses + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, DelphiZXingQRCode, Vcl.ExtCtrls, + Vcl.StdCtrls; + +type + TForm1 = class(TForm) + edtText: TEdit; + Label1: TLabel; + cmbEncoding: TComboBox; + Label2: TLabel; + Label3: TLabel; + edtQuietZone: TEdit; + Label4: TLabel; + PaintBox1: TPaintBox; + procedure FormDestroy(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure PaintBox1Paint(Sender: TObject); + procedure edtTextChange(Sender: TObject); + procedure cmbEncodingChange(Sender: TObject); + procedure edtQuietZoneChange(Sender: TObject); + private + QRCodeBitmap: TBitmap; + public + procedure Update; + end; + +var + Form1: TForm1; + +implementation + +{$R *.dfm} + +procedure TForm1.cmbEncodingChange(Sender: TObject); +begin + Update; +end; + +procedure TForm1.edtQuietZoneChange(Sender: TObject); +begin + Update; +end; + +procedure TForm1.edtTextChange(Sender: TObject); +begin + Update; +end; + +procedure TForm1.FormCreate(Sender: TObject); +begin + QRCodeBitmap := TBitmap.Create; + Update; +end; + +procedure TForm1.FormDestroy(Sender: TObject); +begin + QRCodeBitmap.Free; +end; + +procedure TForm1.PaintBox1Paint(Sender: TObject); +var + Scale: Double; +begin + PaintBox1.Canvas.Brush.Color := clWhite; + PaintBox1.Canvas.FillRect(Rect(0, 0, PaintBox1.Width, PaintBox1.Height)); + if ((QRCodeBitmap.Width > 0) and (QRCodeBitmap.Height > 0)) then + begin + if (PaintBox1.Width < PaintBox1.Height) then + begin + Scale := PaintBox1.Width / QRCodeBitmap.Width; + end else + begin + Scale := PaintBox1.Height / QRCodeBitmap.Height; + end; + PaintBox1.Canvas.StretchDraw(Rect(0, 0, Trunc(Scale * QRCodeBitmap.Width), Trunc(Scale * QRCodeBitmap.Height)), QRCodeBitmap); + end; +end; + +procedure TForm1.Update; +var + QRCode: TDelphiZXingQRCode; + Row, Column: Integer; +begin + QRCode := TDelphiZXingQRCode.Create; + try + QRCode.Data := edtText.Text; + QRCode.Encoding := TQRCodeEncoding(cmbEncoding.ItemIndex); + QRCode.QuietZone := StrToIntDef(edtQuietZone.Text, 4); + QRCodeBitmap.SetSize(QRCode.Rows, QRCode.Columns); + for Row := 0 to QRCode.Rows - 1 do + begin + for Column := 0 to QRCode.Columns - 1 do + begin + if (QRCode.IsBlack[Row, Column]) then + begin + QRCodeBitmap.Canvas.Pixels[Column, Row] := clBlack; + end else + begin + QRCodeBitmap.Canvas.Pixels[Column, Row] := clWhite; + end; + end; + end; + finally + QRCode.Free; + end; + PaintBox1.Repaint; +end; + +end.