feat(apk): scanner le QR du poste depuis l'app (ZXing.Delphi, Apache 2.0)

Le scanner du systeme demandait avec quoi ouvrir le lien. L'app ouvre la
camera arriere, decode une image sur quatre et remplit URL + jeton des
qu'un lien pmserver://sync est lu. Rien n'est enregistre avant Save.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-17 18:38:41 +01:00
parent 780eecc11b
commit dd9fd62dcb
81 changed files with 22141 additions and 19 deletions
@@ -0,0 +1,564 @@
{
* 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.
* Original Author: bbrown@google.com (Brian Brown)
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.BitMatrixParser;
interface
uses
System.SysUtils,
System.Generics.Collections,
ZXing.DecodeHintType,
ZXing.Common.BitMatrix,
ZXing.QrCode.Internal.FormatInformation,
ZXing.Common.Detector.MathUtils,
ZXing.Datamatrix.Internal.Version;
type
TBitMatrixParser = class sealed
private
mappingBitMatrix: TBitMatrix;
readMappingMatrix: TBitMatrix;
Fversion: TVersion;
/// <summary>
/// <p>Reads a bit of the mapping matrix accounting for boundary wrapping.</p>
///
/// <param name="row">Row to read in the mapping matrix</param>
/// <param name="column">Column to read in the mapping matrix</param>
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>value of the given bit in the mapping matrix</returns>
/// </summary>
function readModule(row, column, numRows, numColumns: Integer): Boolean;
/// <summary>
/// <p>Reads the 8 bits of the standard Utah-shaped pattern.</p>
///
/// <p>See ISO 16022:2006, 5.8.1 Figure 6</p>
///
/// <param name="row">Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern</param>
/// <param name="column">Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern</param>
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the utah shape</returns>
/// </summary>
function readUtah(row, column, numRows, numColumns: Integer): Integer;
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 1.</p>
///
/// <p>See ISO 16022:2006, Figure F.3</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 1</returns>
/// </summary>
function readCorner1(numRows, numColumns: Integer): Integer;
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 2.</p>
///
/// <p>See ISO 16022:2006, Figure F.4</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 2</returns>
/// </summary>
function readCorner2(numRows, numColumns: Integer): Integer;
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 3.</p>
///
/// <p>See ISO 16022:2006, Figure F.5</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 3</returns>
/// </summary>
function readCorner3(numRows, numColumns: Integer): Integer;
/// <summary>
/// <p>Reads the 8 bits of the special corner condition 4.</p>
///
/// <p>See ISO 16022:2006, Figure F.6</p>
///
/// <param name="numRows">Number of rows in the mapping matrix</param>
/// <param name="numColumns">Number of columns in the mapping matrix</param>
/// <returns>byte from the Corner condition 4</returns>
/// </summary>
function readCorner4(numRows, numColumns: Integer): Integer;
/// <summary>
/// <p>Creates the version object based on the dimension of the original bit matrix from
/// the datamatrix code.</p>
///
/// <p>See ISO 16022:2006 Table 7 - ECC 200 symbol attributes</p>
///
/// <param name="bitMatrix">Original <see cref="BitMatrix" />including alignment patterns</param>
/// <returns><see cref="Version" />encapsulating the Data Matrix Code's "version"</returns>
/// <exception cref="FormatException">if the dimensions of the mapping matrix are not valid</exception>
/// Data Matrix dimensions.
/// </summary>
function readVersion(BitMatrix: TBitMatrix): TVersion;
public
/// <param name="bitMatrix">{@link TBitMatrix} to parse</param>
/// <throws>ReaderException if dimension is not >= 21 and 1 mod 4</throws>
constructor Create(const BitMatrix: TBitMatrix);
/// <summary>
/// <p>Extracts the data region from a <see cref="BitMatrix" />that contains
/// alignment patterns.</p>
///
/// <param name="bitMatrix">Original <see cref="BitMatrix" />with alignment patterns</param>
/// <returns>BitMatrix that has the alignment patterns removed</returns>
/// </summary>
function extractDataRegion(BitMatrix: TBitMatrix): TBitMatrix;
/// <summary>
/// <p>Reads the bits in the <see cref="BitMatrix" />representing the mapping matrix (No alignment patterns)
/// in the correct order in order to reconstitute the codewords bytes contained within the
/// Data Matrix Code.</p>
///
/// <returns>bytes encoded within the Data Matrix Code</returns>
/// <exception cref="FormatException">if the exact number of bytes expected is not read</exception>
/// </summary>
function readCodewords: TArray<Byte>;
destructor Destroy; override;
property Version: TVersion read Fversion;
end;
implementation
{ TBitMatrixParser }
constructor TBitMatrixParser.Create(const BitMatrix: TBitMatrix);
var
dimension: Integer;
begin
dimension := BitMatrix.Height;
if (((dimension >= 8) and (dimension <= 144)) and ((dimension and $01) = 0))
then
begin
self.Fversion := readVersion(BitMatrix);
if (self.Version <> nil) then
begin
self.mappingBitMatrix := extractDataRegion(BitMatrix);
self.readMappingMatrix := TBitMatrix.Create(self.mappingBitMatrix.Width,
self.mappingBitMatrix.Height)
end;
end;
end;
destructor TBitMatrixParser.Destroy;
begin
self.mappingBitMatrix.Free;
self.readMappingMatrix.Free;
inherited;
end;
function TBitMatrixParser.readVersion(BitMatrix: TBitMatrix): TVersion;
begin
Result := TVersion.getVersionForDimensions(BitMatrix.Height, BitMatrix.Width);
end;
function TBitMatrixParser.readCodewords: TArray<Byte>;
var
resultOffset, row, column, numRows, numColumns: Integer;
corner1Read, corner2Read, corner3Read, corner4Read: Boolean;
begin
Result := TArray<Byte>.Create();
SetLength(Result, Version.TotalCodewords);
resultOffset := 0;
row := 4;
column := 0;
numRows := self.mappingBitMatrix.Height;
numColumns := self.mappingBitMatrix.Width;
corner1Read := false;
corner2Read := false;
corner3Read := false;
corner4Read := false;
// Read all of the codewords
while ((row < numRows) or (column < numColumns)) do
begin
// Check the four corner cases
if ((row = numRows) and (column = 0) and (not corner1Read)) then
begin
Result[resultOffset] := Byte(readCorner1(numRows, numColumns));
Inc(resultOffset);
Dec(row, 2);
Inc(column, 2);
corner1Read := true;
end
else
begin
if ((row = (numRows - 2)) and (column = 0) and ((numColumns and $03) <> 0)
and (not corner2Read)) then
begin
Result[resultOffset] := Byte(readCorner2(numRows, numColumns));
Inc(resultOffset);
Dec(row, 2);
Inc(column, 2);
corner2Read := true;
end
else
begin
if ((row = (numRows + 4)) and (column = 2) and
((numColumns and $07) = 0) and (not corner3Read)) then
begin
Result[resultOffset] := Byte(readCorner3(numRows, numColumns));
Inc(resultOffset);
Dec(row, 2);
Inc(column, 2);
corner3Read := true;
end
else
begin
if ((row = (numRows - 2)) and (column = 0) and
((numColumns and $07) = 4) and (not corner4Read)) then
begin
Result[resultOffset] := Byte(readCorner4(numRows, numColumns));
Inc(resultOffset);
Dec(row, 2);
Inc(column, 2);
corner4Read := true;
end
else
begin
// Sweep upward diagonally to the right
while ((row >= 0) and (column < numColumns)) do
begin
if ((row < numRows) and (column >= 0) and
(not readMappingMatrix[column, row])) then
begin
Result[resultOffset] :=
Byte(readUtah(row, column, numRows, numColumns));
Inc(resultOffset);
end;
Dec(row, 2);
Inc(column, 2)
end;
Inc(row);
Inc(column, 3);
// Sweep downward diagonally to the left
while ((row < numRows) and (column >= 0)) do
begin
if ((row >= 0) and (column < numColumns) and
(not readMappingMatrix[column, row])) then
begin
Result[resultOffset] :=
Byte(readUtah(row, column, numRows, numColumns));
Inc(resultOffset);
end;
Inc(row, 2);
Dec(column, 2)
end;
Inc(row, 3);
Inc(column)
end;
end;
end;
end;
end;
if (resultOffset <> Version.TotalCodewords) then
Result := nil;
end;
function TBitMatrixParser.readModule(row, column, numRows,
numColumns: Integer): Boolean;
begin
// Adjust the row and column indices based on boundary wrapping
if (row < 0) then
begin
Inc(row, numRows);
Inc(column, (4 - ((numRows + 4) and $07)))
end;
if (column < 0) then
begin
Inc(column, numColumns);
Inc(row, (4 - ((numColumns + 4) and $07)))
end;
readMappingMatrix[column, row] := true;
Result := mappingBitMatrix[column, row];
end;
function TBitMatrixParser.readUtah(row, column, numRows,
numColumns: Integer): Integer;
var
currentByte: Byte;
begin
currentByte := 0;
if (readModule((row - 2), (column - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((row - 2), (column - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((row - 1), (column - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((row - 1), (column - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((row - 1), column, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(row, (column - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(row, (column - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(row, column, numRows, numColumns)) then
currentByte := (currentByte or 1);
Result := currentByte;
end;
function TBitMatrixParser.readCorner1(numRows, numColumns: Integer): Integer;
var
currentByte: Byte;
begin
currentByte := 0;
if (readModule((numRows - 1), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 1), 1, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 1), 2, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(2, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(3, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
Result := currentByte;
end;
function TBitMatrixParser.readCorner2(numRows, numColumns: Integer): Integer;
var
currentByte: Byte;
begin
currentByte := 0;
if (readModule((numRows - 3), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 2), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 1), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 4), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 3), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
Result := currentByte;
end;
function TBitMatrixParser.readCorner3(numRows, numColumns: Integer): Integer;
var
currentByte: Byte;
begin
currentByte := 0;
if (readModule((numRows - 1), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 1), (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 3), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 3), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
Result := currentByte;
end;
function TBitMatrixParser.readCorner4(numRows, numColumns: Integer): Integer;
var
currentByte: Byte;
begin
currentByte := 0;
if (readModule((numRows - 3), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 2), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule((numRows - 1), 0, numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (self.readModule(0, (numColumns - 2), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(0, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(1, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(2, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
currentByte := (currentByte shl 1);
if (readModule(3, (numColumns - 1), numRows, numColumns)) then
currentByte := (currentByte or 1);
Result := currentByte;
end;
function TBitMatrixParser.extractDataRegion(BitMatrix: TBitMatrix): TBitMatrix;
var
i, j, symbolSizeRows, dataRegionSizeRows,
dataRegionSizeColumns, numDataRegionsRow, numDataRegionsColumn,
sizeDataRegionRow, sizeDataRegionColumn, dataRegionRow, dataRegionRowOffset,
dataRegionColumn, dataRegionColumnOffset, readRowOffset, writeRowOffset,
readColumnOffset, writeColumnOffset: Integer;
bitMatrixWithoutAlignment: TBitMatrix;
begin
symbolSizeRows := Version.symbolSizeRows;
if (BitMatrix.Height <> symbolSizeRows) then
raise EArgumentException.Create
('Dimension of bitMarix must match the version size');
dataRegionSizeRows := Version.dataRegionSizeRows;
dataRegionSizeColumns := Version.dataRegionSizeColumns;
numDataRegionsRow := (symbolSizeRows div dataRegionSizeRows);
numDataRegionsColumn := (Version.symbolSizeColumns div dataRegionSizeColumns);
sizeDataRegionRow := (numDataRegionsRow * dataRegionSizeRows);
sizeDataRegionColumn := (numDataRegionsColumn * dataRegionSizeColumns);
bitMatrixWithoutAlignment := TBitMatrix.Create(sizeDataRegionColumn,
sizeDataRegionRow);
for dataRegionRow := 0 to Pred(numDataRegionsRow) do
begin
dataRegionRowOffset := (dataRegionRow * dataRegionSizeRows);
for dataRegionColumn := 0 to Pred(numDataRegionsColumn) do
begin
dataRegionColumnOffset := (dataRegionColumn * dataRegionSizeColumns);
for i := 0 to Pred(dataRegionSizeRows) do
begin
readRowOffset := (dataRegionRow * (dataRegionSizeRows + 2)) + 1 + i;
writeRowOffset := (dataRegionRowOffset + i);
for j := 0 to Pred(dataRegionSizeColumns) do
begin
readColumnOffset :=
(dataRegionColumn * (dataRegionSizeColumns + 2)) + 1 + j;
if (BitMatrix[readColumnOffset, readRowOffset]) then
begin
writeColumnOffset := (dataRegionColumnOffset + j);
bitMatrixWithoutAlignment[writeColumnOffset,
writeRowOffset] := true;
end;
end;
end;
end;
end;
Result := bitMatrixWithoutAlignment;
end;
end.
@@ -0,0 +1,179 @@
{
* 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.
* Original Author: bbrown@google.com (Brian Brown)
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.DataBlock;
interface
uses
System.SysUtils,
ZXing.Datamatrix.Internal.Version;
type
/// <summary>
/// <p>Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into
/// multiple blocks, each of which is a unit of data and error-correction codewords. Each
/// is represented by an instance of this class.</p>
/// </summary>
TDataBlock = class sealed
private
Fcodewords: TArray<Byte>;
FNumDataCodewords: Integer;
public
constructor Create(const Acodewords: TArray<Byte>;
const ANumDataCodewords: Integer);
destructor Destroy;override;
class function getDataBlocks(rawCodewords: TArray<Byte>;
version: TVersion): TArray<TDataBlock>;
property codewords: TArray<Byte> read Fcodewords;
property NumDataCodewords: Integer read FNumDataCodewords;
end;
implementation
{ TDataBlock }
constructor TDataBlock.Create(const Acodewords: TArray<Byte>;
const ANumDataCodewords: Integer);
begin
FnumDataCodewords := AnumDataCodewords;
Fcodewords := Acodewords;
end;
destructor TDataBlock.Destroy;
begin
Fcodewords := nil;
inherited;
end;
class function TDataBlock.getDataBlocks(rawCodewords: TArray<Byte>;
version: TVersion): TArray<TDataBlock>;
var
i, j: Integer;
ecBlock : TVersion.TECB;
ecBlocks : TVersion.TECBlocks;
totalBlocks : Integer;
ecBlockArray : TArray<TVersion.TECB>;
byteArray : TArray<Byte>;
numResultBlocks,
numDataCodewords,
numBlockCodewords,
longerBlocksTotalCodewords,
//shorterBlocksTotalCodewords
longerBlocksNumDataCodewords,
shorterBlocksNumDataCodewords,
rawCodewordsOffset,
numLongerBlocks,
max,
iOffset,
jOffset : Integer;
specialVersion: Boolean;
begin
// Figure out the number and size of data blocks used by this version
ecBlocks := version.ecBlocks;
// First count the total number of data blocks
totalBlocks := 0;
ecBlockArray := ecBlocks.ECBBlocksValue;
for ecBlock in ecBlockArray do
begin
inc(totalBlocks, ecBlock.Count)
end;
// Now establish DataBlocks of the appropriate size and number of data codewords
result := TArray<TDataBlock>.Create();
SetLength(result, totalBlocks);
numResultBlocks := 0;
for ecBlock in ecBlockArray do
begin
for i := 0 to Pred(ecBlock.Count) do
begin
numDataCodewords := ecBlock.DataCodewords;
numBlockCodewords := (ecBlocks.ECCodewords + numDataCodewords);
byteArray := TArray<Byte>.Create();
SetLength(byteArray, numBlockCodewords);
result[numResultBlocks] := TDataBlock.Create(byteArray, numDataCodewords);
Inc(numResultBlocks);
end
end;
// All blocks have the same amount of data, except that the last n
// (where n may be 0) have 1 less byte. Figure out where these start.
// TODO: There is only one case where there is a difference for Data Matrix for size 144
longerBlocksTotalCodewords := Length(result[0].codewords);
//shorterBlocksTotalCodewords := (longerBlocksTotalCodewords - 1);
longerBlocksNumDataCodewords := (longerBlocksTotalCodewords - ecBlocks.ECCodewords);
shorterBlocksNumDataCodewords := (longerBlocksNumDataCodewords - 1);
// The last elements of result may be 1 element shorter for 144 matrix
// first fill out as many elements as all of them have minus 1
rawCodewordsOffset := 0;
for i := 0 to Pred(shorterBlocksNumDataCodewords) do
begin
for j := 0 to Pred(numResultBlocks) do
begin
result[j].codewords[i] := rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
end;
// Fill out the last data block in the longer ones
specialVersion := (version.versionNumber = 24);
if specialVersion
then
numLongerBlocks := 8
else
numLongerBlocks := numResultBlocks;
for j := 0 to Pred(numLongerBlocks) do
begin
result[j].codewords[(longerBlocksNumDataCodewords - 1)] := rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
// Now add in error correction blocks
max := Length(result[0].codewords);
for i := longerBlocksNumDataCodewords to Pred(max) do
begin
for j := 0 to Pred(numResultBlocks) do
begin
if (specialVersion)
then
jOffset := (j + 8) mod numResultBlocks
else
jOffset := j;
if (specialVersion and (jOffset > 7))
then
iOffset := (i - 1)
else
iOffset := i;
result[jOffset].codewords[iOffset] := rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
end;
if (not (rawCodewordsOffset = Length(rawCodewords)))
then
raise EArgumentException.Create('Arguments error');
end;
end.
@@ -0,0 +1,903 @@
{
* 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.
* Original Authors: bbrown@google.com (Brian Brown) and Sean Owen
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.DecodedBitStreamParser;
interface
uses
System.SysUtils,
System.Generics.Collections,
ZXing.DecoderResult,
ZXing.ByteSegments,
ZXing.BitSource;
type
/// <summary>
/// <p>Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes
/// in one Data Matrix Code. This class decodes the bits back into text.</p>
///
/// <p>See ISO 16022:2006, 5.2.1 - 5.2.9.2</p>
/// </summary>
TDecodedBitStreamParser = class abstract
type
TMode = (PAD_ENCODE, // Not really a mode
ASCII_ENCODE, C40_ENCODE, TEXT_ENCODE, ANSIX12_ENCODE, EDIFACT_ENCODE,
BASE256_ENCODE);
private
class var C40_BASIC_SET_CHARS, C40_SHIFT2_SET_CHARS, TEXT_BASIC_SET_CHARS,
TEXT_SHIFT2_SET_CHARS, TEXT_SHIFT3_SET_CHARS: TArray<Char>;
class procedure InitializeClass;
class procedure FinalizeClass;
class function decodeC40Segment(bits: TBitSource;
res: TStringBuilder): boolean;
class function decodeTextSegment(bits: TBitSource;
res: TStringBuilder): boolean;
class function decodeAnsiX12Segment(bits: TBitSource;
res: TStringBuilder): boolean;
class function decodeEdifactSegment(bits: TBitSource;
res: TStringBuilder): boolean;
class function decodeBase256Segment(bits: TBitSource; res: TStringBuilder;
byteSegments:IByteSegments ): boolean;
class function decodeAsciiSegment(bits: TBitSource; res: TStringBuilder;
resultTrailer: TStringBuilder; var mode: TMode): boolean;
class procedure parseTwoBytes(firstByte: Integer; secondByte: Integer;
result: TArray<Integer>);
class function unrandomize255State(randomizedBase256Codeword: Integer;
base256CodewordPosition: Integer): Integer;
public
class function decode(bytes: TArray<Byte>): TDecoderResult;
end;
implementation
{ TDecodedBitStreamParser }
class function TDecodedBitStreamParser.decode(bytes: TArray<Byte>)
: TDecoderResult;
var
bits: TBitSource;
res, resultTrailer: TStringBuilder;
byteSegments: IByteSegments;
mode: TMode;
begin
bits := TBitSource.Create(bytes);
res := TStringBuilder.Create(100);
resultTrailer := TStringBuilder.Create(0);
byteSegments := ByteSegmentsCreate;
try
byteSegments.Add(TArray<Byte>.Create()); // TODO (KG): Validation
mode := TMode.ASCII_ENCODE;
while ((mode <> TMode.PAD_ENCODE) and (bits.available() > 0)) do
begin
if (mode = TMode.ASCII_ENCODE) then
begin
if (not TDecodedBitStreamParser.decodeAsciiSegment(bits, res,
resultTrailer, mode)) then
begin
result := nil; // this line was totally missing. I assume the correct thing is to set result = nil.
exit;
end;
end
else
begin
case mode of
TMode.C40_ENCODE:
begin
if (not TDecodedBitStreamParser.decodeC40Segment(bits, res)) then
begin
result := nil;
exit;
end;
end;
TMode.TEXT_ENCODE:
begin
if (not TDecodedBitStreamParser.decodeTextSegment(bits, res)) then
begin
result := nil;
exit;
end;
end;
TMode.ANSIX12_ENCODE:
begin
if (not TDecodedBitStreamParser.decodeAnsiX12Segment(bits, res))
then
begin
result := nil;
exit;
end;
end;
TMode.EDIFACT_ENCODE:
begin
if (not TDecodedBitStreamParser.decodeEdifactSegment(bits, res))
then
begin
result := nil;
exit;
end;
end;
TMode.BASE256_ENCODE:
begin
if (not TDecodedBitStreamParser.decodeBase256Segment(bits, res,
byteSegments)) then
begin
result := nil;
exit;
end;
end;
else
begin
result := nil;
exit;
end;
end;
mode := TMode.ASCII_ENCODE;
end;
end;
if (resultTrailer.Length > 0) then
res.Append(resultTrailer.ToString);
if (byteSegments.Count = 0) then
result := TDecoderResult.Create(bytes, res.ToString, nil, '')
else
result := TDecoderResult.Create(bytes, res.ToString, byteSegments, '');
finally
bits.Free;
res.Free;
resultTrailer.Free;
//
// for i := 0 to byteSegments.Count - 1 do
// begin
//
// end;
//
// byteSegments.Free;
end;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.7
/// </summary>
class function TDecodedBitStreamParser.decodeAnsiX12Segment(bits: TBitSource;
res: TStringBuilder): boolean;
var
i: Integer;
cValues: TArray<Integer>;
firstByte: Integer;
cValue: Integer;
begin
// Three ANSI X12 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
cValues := TArray<Integer>.Create(0, 0, 0);
while (bits.available() > 0) do
begin
// If there is only one byte left then it will be encoded as ASCII
if (bits.available = 8) then
begin
result := true;
exit;
end;
firstByte := bits.readBits(8);
if (firstByte = 254) then
begin
// Unlatch codeword
result := true;
exit;
end;
TDecodedBitStreamParser.parseTwoBytes(firstByte, bits.readBits(8), cValues);
for i := 0 to 2 do
begin
cValue := cValues[i];
case cValue of
0:
begin
// X12 segment terminator <CR>
res.Append(#13);
end;
1:
begin
// X12 segment separator *
res.Append('*');
end;
2:
begin
// X12 sub-element separator >
res.Append('>');
end;
3:
begin
// space
res.Append(' ');
end;
else
begin
if (cValue < 14) then
// 0 - 9
res.Append(Char(cValue + 44))
else if (cValue < 40) then
// A - Z
res.Append(Char(cValue + 51))
else
begin
result := false;
exit;
end;
end;
end;
end;
end;;
result := true;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.3 and Annex C, Table C.2
/// </summary>
class function TDecodedBitStreamParser.decodeAsciiSegment(bits: TBitSource;
res: TStringBuilder; resultTrailer: TStringBuilder; var mode: TMode): boolean;
var
oneByte: Integer;
upperShift: boolean;
value: Byte;
begin
upperShift := false;
mode := TMode.ASCII_ENCODE;
while (bits.available() > 0) do
begin
oneByte := bits.readBits(8);
if (oneByte = 0) then
begin
result := false;
exit;
end
else
begin
if (oneByte <= 128) then
begin
// ASCII data (ASCII value + 1)
if (upperShift) then
begin
Inc(oneByte, 128);
// upperShift := false;
end;
res.Append(Char(oneByte - 1));
mode := TMode.ASCII_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 129) then
begin
// Pad
mode := TMode.PAD_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte <= 229) then
begin
// 2-digit data 00-99 (Numeric Value + 130)
value := (oneByte - 130);
if (value < 10) then
// pad with '0' for single digit values
res.Append('0');
res.Append(value);
end
else
begin
if (oneByte = 230) then
begin
// Latch to C40 encodation
mode := TMode.C40_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 231) then
begin
// Latch to Base 256 encodation
mode := TMode.BASE256_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 232) then
begin
// FNC1
res.Append(Char(29)); // translate as ASCII 29
end
else
begin
if ((oneByte = 233) or (oneByte = 234)) then
begin
// Structured Append, Reader Programming
// Ignore these symbols for now
// throw EReaderException.Instance;
end
else
begin
if (oneByte = 235) then
begin
// Upper Shift (shift to Extended ASCII)
upperShift := true;
end
else
begin
if (oneByte = 236) then
begin
// 05 Macro
res.Append('[)>' + #$001E05 + #$001D);
res.Insert(0, #$001E + #$0004);
end
else
begin
if (oneByte = 237) then
begin
// 06 Macro
res.Append('[)>' + #$001E06 + #$001D);
res.Insert(0, #$001E + #$0004);
end
else
begin
if (oneByte = 238) then
begin
// Latch to ANSI X12 encodation
mode := TMode.ANSIX12_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 239) then
begin
// Latch to Text encodation
mode := TMode.TEXT_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 240) then
begin
mode := TMode.EDIFACT_ENCODE;
result := true;
exit;
end
else
begin
if (oneByte = 241) then
begin
// ECI Character
// TODO: I think we need to support ECI
// throw TReaderException.Instance;
// Ignore this symbol for now
end
else if (oneByte >= 242) then
begin
// Not to be used in ASCII encodation
// ... but work around encoders that end with 254, latch back to ASCII
if ((oneByte <> 254) or (bits.available() <> 0)) then
begin
result := false;
exit;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
mode := TMode.ASCII_ENCODE;
result := true;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.9 and Annex B, B.2
/// </summary>
class function TDecodedBitStreamParser.decodeBase256Segment(bits: TBitSource;
res: TStringBuilder; byteSegments: IByteSegments ): boolean;
var
i, Count, codewordPosition, d1: Integer;
bytes: TArray<Byte>;
begin
// Figure out how long the Base 256 Segment is.
codewordPosition := (1 + bits.ByteOffset); // position is 1-indexed
d1 := TDecodedBitStreamParser.unrandomize255State(bits.readBits(8),
codewordPosition);
Inc(codewordPosition);
if (d1 = 0) then
// Read the remainder of the symbol
Count := (bits.available div 8)
else if (d1 < 250) then
Count := d1
else
begin
Count := ((250 * (d1 - 249)) + TDecodedBitStreamParser.unrandomize255State
(bits.readBits(8), codewordPosition));
Inc(codewordPosition);
end;
// We're seeing NegativeArraySizeException errors from users.
if (Count < 0) then
begin
result := false;
exit;
end;
bytes := TArray<Byte>.Create();
SetLength(bytes, Count);
for i := 0 to Pred(Count) do
begin
// Have seen this particular error in the wild, such as at
// http://www.bcgen.com/demo/IDAutomationStreamingDataMatrix.aspx?MODE=3&D=Fred&PFMT=3&PT=F&X=0.3&O=0&LM=0.2
if (bits.available() < 8) then
begin
result := false;
exit;
end;
bytes[i] := Byte(TDecodedBitStreamParser.unrandomize255State
(bits.readBits(8), codewordPosition));
Inc(codewordPosition);
end;
byteSegments.Add(bytes);
try
res.Append(TEncoding.GetEncoding('ISO-8859-1').GetString(bytes))
except
on uee: Exception do
raise EInvalidOpException.Create
('Platform does not support required encoding: ' + uee.Message)
end;
result := true;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.5 and Annex C, Table C.1
/// </summary>
class function TDecodedBitStreamParser.decodeC40Segment(bits: TBitSource;
res: TStringBuilder): boolean;
var
i: Integer;
c40char: Char;
cValues: TArray<Integer>;
upperShift: boolean;
shift: Integer;
firstByte: Byte;
cValue: Integer;
begin
// Three C40 values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO: The Upper Shift with C40 doesn't work in the 4 value scenario all the time
upperShift := false;
cValues := TArray<Integer>.Create(0, 0, 0);
shift := 0;
while (bits.available() > 0) do
begin
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() = 8) then
begin
result := true;
exit;
end;
firstByte := bits.readBits(8);
if (firstByte = 254) then
begin
// Unlatch codeword
result := true;
exit;
end;
TDecodedBitStreamParser.parseTwoBytes(firstByte, bits.readBits(8), cValues);
for i := 0 to 2 do
begin
cValue := cValues[i];
case shift of
0:
begin
if (cValue < 3) then
shift := (cValue + 1)
else
begin
if (cValue < Length(C40_BASIC_SET_CHARS)) then
begin
c40char := C40_BASIC_SET_CHARS[cValue];
if (upperShift) then
begin
res.Append(Char(c40char) + Char(128));
upperShift := false;
end
else
res.Append(c40char);
end
else
begin
result := false;
exit;
end;
end;
end;
1:
begin
if (upperShift) then
begin
res.Append(Char(cValue) + Char(128));
upperShift := false;
end
else
res.Append(Char(cValue));
shift := 0;
end;
2:
begin
if (cValue < Length(C40_SHIFT2_SET_CHARS)) then
begin
c40char := C40_SHIFT2_SET_CHARS[cValue];
if (upperShift) then
begin
res.Append(Char(c40char) + Char(128));
upperShift := false;
end
else
res.Append(c40char);
end
else
begin
if (cValue = 27) then
// FNC1
res.Append(Char(29)) // translate as ASCII 29
else if (cValue = 30) then
// Upper Shift
upperShift := true
else
begin
result := false;
exit;
end;
end;
shift := 0;
end;
3:
begin
if (upperShift) then
begin
res.Append(Char(cValue + 254));
upperShift := false;
end
else
res.Append(Char(cValue + 96));
shift := 0;
end;
else
begin
result := false;
exit;
end;
end;
end;
end;
result := true;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.8 and Annex C Table C.3
/// </summary>
class function TDecodedBitStreamParser.decodeEdifactSegment(bits: TBitSource;
res: TStringBuilder): boolean;
var
i: Integer;
edifactValue: Integer;
bitsLeft: Integer;
begin
while (bits.available() > 0) do
begin
// If there is only two or less bytes left then it will be encoded as ASCII
if (bits.available() <= 16) then
begin
result := true;
exit;
end;
for i := 0 to 3 do
begin
edifactValue := bits.readBits(6);
// Check for the unlatch character
if (edifactValue = $1F) then
begin
// 011111
// Read rest of byte, which should be 0, and stop
bitsLeft := (8 - bits.BitOffset);
if (not bitsLeft = 8) then
bits.readBits(bitsLeft);
result := true;
exit;
end;
if ((edifactValue and $20) = 0) then
begin
// no 1 in the leading (6th) bit
edifactValue := (edifactValue or $40);
// Add a leading 01 to the 6 bit binary value
end;
res.Append(Char(edifactValue));
end;
end;
result := true;
end;
/// <summary>
/// See ISO 16022:2006, 5.2.6 and Annex C, Table C.2
/// </summary>
class function TDecodedBitStreamParser.decodeTextSegment(bits: TBitSource;
res: TStringBuilder): boolean;
var
textChar: Char;
cValues: TArray<Integer>;
upperShift: boolean;
i, shift: Integer;
firstByte: Integer;
cValue: Integer;
begin
// Three Text values are encoded in a 16-bit value as
// (1600 * C1) + (40 * C2) + C3 + 1
// TODO: The Upper Shift with Text doesn't work in the 4 value scenario all the time
upperShift := false;
cValues := TArray<Integer>.Create(0, 0, 0);
shift := 0;
while (bits.available() > 0) do
begin
// If there is only one byte left then it will be encoded as ASCII
if (bits.available() = 8) then
begin
result := true;
exit;
end;
firstByte := bits.readBits(8);
if (firstByte = 254) then
begin
// Unlatch codeword
result := true;
exit;
end;
TDecodedBitStreamParser.parseTwoBytes(firstByte, bits.readBits(8), cValues);
for i := 0 to 2 do
begin
cValue := cValues[i];
case (shift) of
0:
begin
if (cValue < 3) then
shift := (cValue + 1)
else
begin
if (cValue < Length(TEXT_BASIC_SET_CHARS)) then
begin
textChar := TEXT_BASIC_SET_CHARS[cValue];
if (upperShift) then
begin
res.Append(Char(textChar) + Char(128));
upperShift := false;
end
else
res.Append(textChar);
end
else
begin
result := false;
exit;
end;
end;
end;
1:
begin
if (upperShift) then
begin
res.Append(Char(cValue + 128));
upperShift := false;
end
else
res.Append(Char(cValue));
shift := 0;
end;
2:
begin
// Shift 2 for Text is the same encoding as C40
if (cValue < Length(TEXT_SHIFT2_SET_CHARS)) then
begin
textChar := TEXT_SHIFT2_SET_CHARS[cValue];
if (upperShift) then
begin
res.Append(Char(textChar) + Char(128));
upperShift := false;
end
else
res.Append(textChar);
end
else
begin
if (cValue = 27) then
// FNC1
res.Append(Char(29)) // translate as ASCII 29
else if (cValue = 30) then
// Upper Shift
upperShift := true
else
begin
result := false;
exit;
end;
end;
shift := 0;
end;
3:
begin
if (cValue < Length(TEXT_SHIFT3_SET_CHARS)) then
begin
textChar := TEXT_SHIFT3_SET_CHARS[cValue];
if (upperShift) then
begin
res.Append(Char(textChar) + Char(128));
upperShift := false;
end
else
res.Append(textChar);
shift := 0;
end
else
begin
result := false;
exit;
end;
end;
else
begin
result := false;
exit;
end;
end;
end;
end;
result := true;
end;
class procedure TDecodedBitStreamParser.parseTwoBytes(firstByte: Integer;
secondByte: Integer; result: TArray<Integer>);
var
fullBitValue, temp: Integer;
begin
fullBitValue := (((firstByte shl 8) + secondByte) - 1);
temp := (fullBitValue div $640);
result[0] := temp;
dec(fullBitValue, (temp * $640));
temp := (fullBitValue div 40);
result[1] := temp;
result[2] := (fullBitValue - (temp * 40))
end;
/// <summary>
/// See ISO 16022:2006, Annex B, B.2
/// </summary>
class function TDecodedBitStreamParser.unrandomize255State
(randomizedBase256Codeword: Integer;
base256CodewordPosition: Integer): Integer;
var
pseudoRandomNumber: Integer;
tempVariable: Integer;
begin
pseudoRandomNumber := (((149 * base256CodewordPosition) mod 255) + 1);
tempVariable := (randomizedBase256Codeword - pseudoRandomNumber);
if (tempVariable >= 0) then
result := tempVariable
else
result := (tempVariable + 256);
end;
class procedure TDecodedBitStreamParser.InitializeClass;
begin
/// <summary>
/// See ISO 16022:2006, Annex C Table C.1
/// The C40 Basic Character Set (*'s used for placeholders for the shift values)
/// </summary>
C40_BASIC_SET_CHARS := TArray<Char>.Create('*', '*', '*', ' ', '0', '1', '2',
'3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z');
C40_SHIFT2_SET_CHARS := TArray<Char>.Create('!', '"', '#', '$', '%', '&',
'''', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
'@', '[', '\', ']', '^', '_');
/// <summary>
/// See ISO 16022:2006, Annex C Table C.2
/// The Text Basic Character Set (*'s used for placeholders for the shift values)
/// </summary>
TEXT_BASIC_SET_CHARS := TArray<Char>.Create('*', '*', '*', ' ', '0', '1', '2',
'3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z');
// Shift 2 for Text is the same encoding as C40
TEXT_SHIFT2_SET_CHARS := C40_SHIFT2_SET_CHARS;
TEXT_SHIFT3_SET_CHARS := TArray<Char>.Create('`', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', '');
end;
class procedure TDecodedBitStreamParser.FinalizeClass;
begin
C40_BASIC_SET_CHARS := nil;
C40_SHIFT2_SET_CHARS := nil;
TEXT_BASIC_SET_CHARS := nil;
TEXT_SHIFT2_SET_CHARS := nil;
TEXT_SHIFT3_SET_CHARS := nil;
end;
initialization
TDecodedBitStreamParser.InitializeClass;
finalization
TDecodedBitStreamParser.FinalizeClass;
end.
@@ -0,0 +1,229 @@
{
* 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.
* Original Author: bbrown@google.com (Brian Brown)
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.Decoder;
interface
uses
System.SysUtils,
System.Generics.Collections,
ZXing.DecodeHintType,
ZXing.Common.BitMatrix,
ZXing.Datamatrix.Internal.BitMatrixParser,
ZXing.Datamatrix.Internal.DataBlock,
ZXing.Common.ReedSolomon.ReedSolomonDecoder,
ZXing.Common.ReedSolomon.GenericGF,
ZXing.DecoderResult,
ZXing.Datamatrix.Internal.DecodedBitStreamParser;
type
/// <summary>
/// <p>The main class which implements Data Matrix Code decoding -- as opposed to locating and extracting
/// the Data Matrix Code from an image.</p>
/// </summary>
TDataMatrixDecoder = class sealed
private
rsDecoder: TReedSolomonDecoder;
public
constructor Create;
destructor Destroy; override;
function correctErrors(codewordBytes: TArray<Byte>; numDataCodewords: Integer): boolean;
function decode(bits: TBitMatrix): TDecoderResult; overload;
function decode(image: TArray < TArray < boolean >> ): TDecoderResult; overload;
end;
implementation
{ TDataMatrixDecoder }
/// <summary>
/// Initializes a new instance of the <see cref="Decoder"/> class.
/// </summary>
constructor TDataMatrixDecoder.Create;
begin
rsDecoder := TReedSolomonDecoder.Create(TGenericGF.DATA_MATRIX_FIELD_256);
end;
destructor TDataMatrixDecoder.Destroy;
begin
FreeAndNil(rsDecoder);
inherited;
end;
/// <summary>
/// <p>Convenience method that can decode a Data Matrix Code represented as a 2D array of booleans.
/// "true" is taken to mean a black module.</p>
///
/// <param name="image">booleans representing white/black Data Matrix Code modules</param>
/// <returns>text and bytes encoded within the Data Matrix Code</returns>
/// <exception cref="FormatException">if the Data Matrix Code cannot be decoded</exception>
/// <exception cref="ChecksumException">if error correction fails</exception>
/// </summary>
function TDataMatrixDecoder.decode(image: TArray < TArray < boolean >> ): TDecoderResult;
var
i, j: Integer;
dimension: Integer;
bits: TBitMatrix;
begin
dimension := Length(image);
bits := TBitMatrix.Create(dimension);
for i := 0 to Pred(dimension) do
begin
for j := 0 to Pred(dimension) do
begin
if (image[i][j]) then
bits[j, i] := true;
end;
end;
Result := decode(bits);
end;
/// <summary>
/// <p>Decodes a Data Matrix Code represented as a <see cref="BitMatrix" />. A 1 or "true" is taken
/// to mean a black module.</p>
/// </summary>
/// <param name="bits">booleans representing white/black Data Matrix Code modules</param>
/// <returns>text and bytes encoded within the Data Matrix Code</returns>
function TDataMatrixDecoder.decode(bits: TBitMatrix): TDecoderResult;
var
i, j: Integer;
db: TDataBlock;
parser: TBitMatrixParser;
codewords: TArray<Byte>;
dataBlocks: TArray<TDataBlock>;
dataBlocksCount, totalBytes: Integer;
resultBytes: TArray<Byte>;
DataBlock: TDataBlock;
codewordBytes: TArray<Byte>;
numDataCodewords: Integer;
begin
// Construct a parser and read version, error-correction level
parser := TBitMatrixParser.Create(bits);
DataBlock := nil;
try
if (parser.Version = nil) then
begin
Result := nil;
exit;
end;
// Read codewords
codewords := parser.readCodewords;
if (codewords = nil) then
begin
Result := nil;
exit;
end;
// Separate into data blocks
dataBlocks := TDataBlock.getDataBlocks(codewords, parser.Version);
dataBlocksCount := Length(dataBlocks);
// Count total number of data bytes
totalBytes := 0;
for db in dataBlocks do
begin
Inc(totalBytes, db.numDataCodewords)
end;
resultBytes := TArray<Byte>.Create();
SetLength(resultBytes, totalBytes);
// Error-correct and copy data blocks together into a stream of bytes
for j := 0 to Pred(dataBlocksCount) do
begin
DataBlock := dataBlocks[j];
codewordBytes := DataBlock.codewords;
numDataCodewords := DataBlock.numDataCodewords;
if (not correctErrors(codewordBytes, numDataCodewords)) then
begin
if Assigned(DataBlock) then
DataBlock.Free;
DataBlock := nil;
resultBytes := nil;
codewordBytes := nil;
Result := nil;
exit;
end;
for i := 0 to Pred(numDataCodewords) do
begin
// De-interlace data blocks.
resultBytes[(i * dataBlocksCount) + j] := codewordBytes[i];
end;
DataBlock.Free;
DataBlock := nil;
end;
// Decode the contents of that stream of bytes
Result := TDecodedBitStreamParser.decode(resultBytes);
finally
if Assigned(DataBlock) then
DataBlock.Free;
resultBytes := nil;
codewordBytes := nil;
parser.Free;
end;
end;
/// <summary>
/// <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
/// correct the errors in-place using Reed-Solomon error correction.</p>
///
/// <param name="codewordBytes">data and error correction codewords</param>
/// <param name="numDataCodewords">number of codewords that are data bytes</param>
/// </summary>
function TDataMatrixDecoder.correctErrors(codewordBytes: TArray<Byte>; numDataCodewords: Integer): boolean;
var
i, numCodewords, numECCodewords: Integer;
codewordsInts: TArray<Integer>;
begin
numCodewords := Length(codewordBytes);
// First read into an array of ints
codewordsInts := TArray<Integer>.Create();
SetLength(codewordsInts, numCodewords);
for i := 0 to Pred(numCodewords) do
begin
codewordsInts[i] := (codewordBytes[i] and $FF);
end;
numECCodewords := (Length(codewordBytes) - numDataCodewords);
if (not rsDecoder.decode(codewordsInts, numECCodewords)) then
begin
Result := false;
exit;
end;
// Copy back into array of bytes -- only need to worry about the bytes that were data
// We don't care about errors in the error-correction codewords
for i := 0 to Pred(numDataCodewords) do
begin
codewordBytes[i] := Byte(codewordsInts[i]);
end;
Result := true;
end;
end.
@@ -0,0 +1,290 @@
{
* Copyright 2007 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.
* Original Author: bbrown@google.com (Brian Brown)
* Delphi Implementation by K. Gossens
}
unit ZXing.Datamatrix.Internal.Version;
interface
uses
System.SysUtils,
ZXing.Common.BitMatrix,
ZXing.QrCode.Internal.FormatInformation,
ZXing.Common.Detector.MathUtils;
type
/// <summary>
/// The Version object encapsulates attributes about a particular
/// size Data Matrix Code.
/// </summary>
TVersion = class sealed
type
/// <summary>
/// <p>Encapsualtes the parameters for one error-correction block in one symbol version.
/// This includes the number of data codewords, and the number of times a block with these
/// parameters is used consecutively in the Data Matrix code version's format.</p>
/// </summary>
TECB = class
private
Fcount: Integer;
FdataCodewords: Integer;
public
constructor Create(count: Integer; dataCodewords: Integer);
property count: Integer read Fcount;
property dataCodewords: Integer read FdataCodewords;
end;
/// <summary>
/// <p>Encapsulates a set of error-correction blocks in one symbol version. Most versions will
/// use blocks of differing sizes within one version, so, this encapsulates the parameters for
/// each set of blocks. It also holds the number of error-correction codewords per block since it
/// will be the same across all blocks within one version.</p>
/// </summary>
TECBlocks = class
private
_ecBlocksValue: TArray<TECB>;
_ecCodewords: Integer;
public
property ECBBlocksValue: TArray<TECB> read _ecBlocksValue;
property ECCodewords: Integer read _ecCodewords;
constructor Create(ECCodewords: Integer; ecBlocks: TECB); overload;
constructor Create(ECCodewords: Integer; ecBlocks1: TECB;
ecBlocks2: TECB); overload;
destructor Destroy; override;
end;
private
FdataRegionSizeColumns: Integer;
FdataRegionSizeRows: Integer;
FecBlocks: TECBlocks;
FsymbolSizeColumns: Integer;
FsymbolSizeRows: Integer;
FtotalCodewords: Integer;
FversionNumber: Integer;
class var buildVersions: TArray<TVersion>;
class procedure ClassInit; static;
class procedure ClassFinal; static;
class function GetBuildVersions: TArray<TVersion>; static;
constructor Create(versionNumber, symbolSizeRows, symbolSizeColumns,
dataRegionSizeRows, dataRegionSizeColumns: Integer; ecBlocks: TECBlocks);
public
destructor Destroy; override;
function ToString: string; override;
class function getVersionForDimensions(numRows: Integer;
numColumns: Integer): TVersion;
property dataRegionSizeColumns: Integer read FdataRegionSizeColumns;
property dataRegionSizeRows: Integer read FdataRegionSizeRows;
property ecBlocks: TECBlocks read FecBlocks;
property symbolSizeColumns: Integer read FsymbolSizeColumns;
property symbolSizeRows: Integer read FsymbolSizeRows;
property totalCodewords: Integer read FtotalCodewords;
property versionNumber: Integer read FversionNumber;
end;
implementation
{ TVersion.TECB }
constructor TVersion.TECB.Create(count, dataCodewords: Integer);
begin
self.Fcount := count;
self.FdataCodewords := dataCodewords;
end;
{ TVersion.TECBlocks }
constructor TVersion.TECBlocks.Create(ECCodewords: Integer; ecBlocks: TECB);
begin
self._ecCodewords := ECCodewords;
self._ecBlocksValue := TArray<TECB>.Create(ecBlocks);
end;
constructor TVersion.TECBlocks.Create(ECCodewords: Integer; ecBlocks1: TECB;
ecBlocks2: TECB);
begin
self._ecCodewords := ECCodewords;
self._ecBlocksValue := TArray<TECB>.Create(ecBlocks1, ecBlocks2);
end;
destructor TVersion.TECBlocks.Destroy;
var
ecb: TECB;
i: Integer;
begin
for i := 0 to Length(_ecBlocksValue) - 1 do
begin
ecb := _ecBlocksValue[i];
FreeAndNil(ecb);
end;
_ecBlocksValue := nil;
inherited;
end;
{ TVersion }
class procedure TVersion.ClassInit();
begin
TVersion.buildVersions := TVersion.GetBuildVersions;
end;
class procedure TVersion.ClassFinal();
var
Version: TVersion;
begin
for Version in TVersion.buildVersions do
begin
Version.Free;
end;
TVersion.buildVersions := nil;
end;
constructor TVersion.Create(versionNumber, symbolSizeRows, symbolSizeColumns,
dataRegionSizeRows, dataRegionSizeColumns: Integer; ecBlocks: TECBlocks);
var
ecBlock: TECB;
total: Integer;
ECCodewords: Integer;
begin
self.FversionNumber := versionNumber;
self.FsymbolSizeRows := symbolSizeRows;
self.FsymbolSizeColumns := symbolSizeColumns;
self.FdataRegionSizeRows := dataRegionSizeRows;
self.FdataRegionSizeColumns := dataRegionSizeColumns;
self.FecBlocks := ecBlocks;
total := 0;
ECCodewords := ecBlocks.ECCodewords;
for ecBlock in ecBlocks.ECBBlocksValue do
begin
Inc(total, (ecBlock.count * (ecBlock.dataCodewords + ECCodewords)))
end;
self.FtotalCodewords := total;
end;
destructor TVersion.Destroy;
begin
FreeAndNil(FecBlocks);
inherited;
end;
/// <summary>
/// See ISO 16022:2006 5.5.1 Table 7
/// </summary>
class function TVersion.GetBuildVersions: TArray<TVersion>;
begin
Result := TArray<TVersion>.Create(TVersion.Create(1, 10, 10, 8, 8,
TECBlocks.Create(5, TECB.Create(1, 3))), TVersion.Create(2, 12, 12, 10, 10,
TECBlocks.Create(7, TECB.Create(1, 5))), TVersion.Create(3, 14, 14, 12, 12,
TECBlocks.Create(10, TECB.Create(1, 8))), TVersion.Create(4, $10, $10, 14,
14, TECBlocks.Create(12, TECB.Create(1, 12))), TVersion.Create(5, $12, $12,
$10, $10, TECBlocks.Create(14, TECB.Create(1, $12))),
TVersion.Create(6, 20, 20, $12, $12, TECBlocks.Create($12,
TECB.Create(1, $16))), TVersion.Create(7, $16, $16, 20, 20,
TECBlocks.Create(20, TECB.Create(1, 30))), TVersion.Create(8, $18, $18, $16,
$16, TECBlocks.Create($18, TECB.Create(1, $24))),
TVersion.Create(9, $1A, $1A, $18, $18, TECBlocks.Create($1C,
TECB.Create(1, $2C))), TVersion.Create(10, $20, $20, 14, 14,
TECBlocks.Create($24, TECB.Create(1, $3E))), TVersion.Create(11, $24, $24,
$10, $10, TECBlocks.Create($2A, TECB.Create(1, $56))),
TVersion.Create(12, 40, 40, $12, $12, TECBlocks.Create($30,
TECB.Create(1, $72))), TVersion.Create(13, $2C, $2C, 20, 20,
TECBlocks.Create($38, TECB.Create(1, $90))), TVersion.Create(14, $30, $30,
$16, $16, TECBlocks.Create($44, TECB.Create(1, $AE))),
TVersion.Create(15, $34, $34, $18, $18, TECBlocks.Create($2A,
TECB.Create(2, $66))), TVersion.Create($10, $40, $40, 14, 14,
TECBlocks.Create($38, TECB.Create(2, 140))), TVersion.Create($11, $48, $48,
$10, $10, TECBlocks.Create($24, TECB.Create(4, $5C))),
TVersion.Create($12, 80, 80, $12, $12, TECBlocks.Create($30,
TECB.Create(4, $72))), TVersion.Create($13, $58, $58, 20, 20,
TECBlocks.Create($38, TECB.Create(4, $90))), TVersion.Create(20, $60, $60,
$16, $16, TECBlocks.Create($44, TECB.Create(4, $AE))),
TVersion.Create($15, $68, $68, $18, $18, TECBlocks.Create($38,
TECB.Create(6, $88))), TVersion.Create($16, 120, 120, $12, $12,
TECBlocks.Create($44, TECB.Create(6, $AF))), TVersion.Create($17, $84, $84,
20, 20, TECBlocks.Create($3E, TECB.Create(8, $A3))),
TVersion.Create($18, $90, $90, $16, $16, TECBlocks.Create($3E,
TECB.Create(8, $9C), TECB.Create(2, $9B))), TVersion.Create($19, 8, $12, 6,
$10, TECBlocks.Create(7, TECB.Create(1, 5))), TVersion.Create($1A, 8, $20,
6, 14, TECBlocks.Create(11, TECB.Create(1, 10))),
TVersion.Create($1B, 12, $1A, 10, $18, TECBlocks.Create(14,
TECB.Create(1, $10))), TVersion.Create($1C, 12, $24, 10, $10,
TECBlocks.Create($12, TECB.Create(1, $16))), TVersion.Create($1D, $10, $24,
14, $10, TECBlocks.Create($18, TECB.Create(1, $20))),
TVersion.Create(30, $10, $30, 14, $16, TECBlocks.Create($1C,
TECB.Create(1, $31))));
end;
/// <summary>
/// <p>Deduces version information from Data Matrix dimensions.</p>
///
/// <param name="numRows">Number of rows in modules</param>
/// <param name="numColumns">Number of columns in modules</param>
/// <returns>Version for a Data Matrix Code of those dimensions</returns>
/// <exception cref="FormatException">if dimensions do correspond to a valid Data Matrix size</exception>
/// </summary>
class function TVersion.getVersionForDimensions(numRows: Integer;
numColumns: Integer): TVersion;
var
Version: TVersion;
begin
Result := nil;
if (((numRows and $01) = 0) and ((numColumns and $01) = 0)) then
begin
for Version in TVersion.buildVersions do
begin
if ((Version.symbolSizeRows = numRows) and
(Version.symbolSizeColumns = numColumns)) then
begin
Result := Version;
break;
end;
end;
end;
end;
function TVersion.ToString: string;
begin
Result := versionNumber.ToString();
end;
Initialization
TVersion.ClassInit();
Finalization
TVersion.ClassFinal();
end.
@@ -0,0 +1,361 @@
{
* Copyright 2007 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.BitMatrixParser;
interface
uses
SysUtils,
Generics.Collections,
ZXing.DecodeHintType,
ZXing.Common.BitMatrix,
ZXing.QrCode.Internal.Version,
ZXing.QrCode.Internal.FormatInformation,
ZXing.Common.Detector.MathUtils,
ZXing.QrCode.Internal.DataMask;
type
TBitMatrixParser = class
private
BitMatrix: TBitMatrix;
mirrored: boolean;
parsedVersion: TVersion;
function copyBit(const i, j, versionBits: Integer): Integer;
public
parsedFormatInfo: TFormatInformation;
constructor Create(const BitMatrix: TBitMatrix);
/// <param name="bitMatrix">{@link TBitMatrix} to parse</param>
/// <throws>ReaderException if dimension is not >= 21 and 1 mod 4</throws>
class function createBitMatrixParser(
const bitMatrix: TBitMatrix): TBitMatrixParser; static;
(** Mirror the bit matrix in order to attempt a second reading. *)
procedure mirror;
(**
* Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
*)
procedure remask;
(**
* Prepare the parser for a mirrored operation.
* This flag has effect only on the {@link #readFormatInformation()} and the
* {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
* {@link #mirror()} method should be called.
*
* @param mirror Whether to read version and format information mirrored.
*)
procedure setMirror(const mirror: Boolean);
/// <summary> <p>Reads format information from one of its two locations within the QR Code.</p>
///
/// </summary>
/// <returns> {@link FormatInformation} encapsulating the QR Code's format info
/// </returns>
/// <throws> ReaderException if both format information locations cannot be parsed as </throws>
/// <summary> the valid encoding of format information
/// </summary>
function readFormatInformation: TFormatInformation;
/// <summary> <p>Reads version information from one of its two locations within the QR Code.</p>
///
/// </summary>
/// <returns> {@link Version} encapsulating the QR Code's version
/// </returns>
/// <throws> ReaderException if both version information locations cannot be parsed as </throws>
/// <summary> the valid encoding of version information
/// </summary>
function readVersion: TVersion;
// <summary> <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
// correct order in order to reconstruct the codewords bytes contained within the
/// QR Code.</p>
///
/// </summary>
/// <returns> bytes encoded within the QR Code
/// </returns>
/// <throws> ReaderException if the exact number of bytes expected is not read </throws>
function readCodewords: TArray<Byte>;
end;
implementation
{ TBitMatrixParser }
constructor TBitMatrixParser.Create(const bitMatrix: TBitMatrix);
begin
// Should only be called from createBitMatrixParser with the important checks before
Self.bitMatrix := BitMatrix;
end;
class function TBitMatrixParser.createBitMatrixParser(
const bitMatrix: TBitMatrix): TBitMatrixParser;
var
dimension: Integer;
begin
dimension := BitMatrix.Height;
if ((dimension < 21) and ((dimension and $03) <> 1))
then
Result := nil
else
Result := TBitMatrixParser.Create(bitMatrix);
end;
function TBitMatrixParser.copyBit(const i, j, versionBits: Integer): Integer;
var
bit: Boolean;
begin
if (mirrored)
then
bit := BitMatrix[j, i]
else
bit := BitMatrix[i, j];
if (bit)
then
Result := ((versionBits shl 1) or $1)
else
Result := (versionBits shl 1);
end;
procedure TBitMatrixParser.mirror;
var
x, y: Integer;
begin
for x := 0 to Pred(bitMatrix.width) do
for y := (x + 1) to Pred(bitMatrix.height) do
if (bitMatrix[x, y] <> bitMatrix[y, x]) then
begin
bitMatrix.flip(y, x);
bitMatrix.flip(x, y);
end;
end;
function TBitMatrixParser.readCodewords: TArray<Byte>;
var
formatInfo: TFormatInformation;
dimension, resultOffset,
currentByte, bitsRead,
j, i, count, col: Integer;
functionPattern: TBitMatrix;
readingUp: boolean;
Version: TVersion;
DataMask: TDataMask;
begin
Result := nil;
formatInfo := readFormatInformation;
if (formatInfo = nil)
then
exit;
Version := readVersion;
if (Version = nil)
then
exit;
// Get the data mask for the format used in this QR Code. This will exclude
// some bits from reading as we wind through the bit matrix.
DataMask := TDataMask.forReference(formatInfo.DataMask);
dimension := bitMatrix.Height;
DataMask.unmaskBitMatrix(BitMatrix, dimension);
functionPattern := Version.buildFunctionPattern;
try
readingUp := true;
Result := TArray<Byte>.Create();
SetLength(Result, version.TotalCodewords);
resultOffset := 0;
currentByte := 0;
bitsRead := 0;
j := (dimension - 1);
// Read columns in pairs, from right to left
while ((j > 0)) do
begin
if (j = 6)
then
// Skip whole column with vertical alignment pattern;
// saves time and makes the other code proceed more cleanly
Dec(j);
// Read alternatingly from bottom to top then top to bottom
for count := 0 to Pred(dimension) do
begin
if (readingUp)
then
i := ((dimension - 1) - count)
else
i := count;
for col := 0 to Pred(2) do
begin
// Ignore bits covered by the function pattern
if (not functionPattern[(j - col), i]) then
begin
// Read a bit
Inc(bitsRead);
currentByte := (currentByte shl 1);
if (bitMatrix[(j - col), i])
then
currentByte := (currentByte or 1);
// If we've made a whole byte, save it off
if (bitsRead = 8) then
begin
// if resultoffset > high(Result) then
// raise EProgrammerNotFound.CreateFmt('resultoffset %d> high(Result) %d',[resultOffset,high(Result)])
// else if resultoffset < 0 then
// raise EProgrammerNotFound.CreateFmt('resultoffset %d<0!',[resultOffset]);
if (resultoffset>=0) and (resultoffset <= high(Result)) then // minchiata aggiunta da carlo
Result[resultOffset] := Byte(currentByte);
Inc(resultOffset);
bitsRead := 0;
currentByte := 0;
end
end;
end;
end;
readingUp := not readingUp; // readingUp = !readingUp; // switch directions
Dec(j, 2);
end;
if (resultOffset <> version.TotalCodewords)
then
Result := nil;
finally
FreeAndNil(functionPattern);
end;
end;
function TBitMatrixParser.readFormatInformation: TFormatInformation;
var
formatInfoBits1,
formatInfoBits2,
dimension,
i, j, jMin: Integer;
begin
if (Self.parsedFormatInfo <> nil) then
begin
Result := Self.parsedFormatInfo;
exit;
end;
// Read top-left format info bits
formatInfoBits1 := 0;
for i := 0 to Pred(6) do
formatInfoBits1 := self.copyBit(i, 8, formatInfoBits1);
// .. and skip a bit in the timing pattern ...
formatInfoBits1 := self.copyBit(7, 8, formatInfoBits1);
formatInfoBits1 := self.copyBit(8, 8, formatInfoBits1);
formatInfoBits1 := self.copyBit(8, 7, formatInfoBits1);
// .. and skip a bit in the timing pattern ...
for j := 5 downto 0 do
formatInfoBits1 := self.copyBit(8, j, formatInfoBits1);
// Read the top-right/bottom-left pattern too
dimension := Self.bitMatrix.Height;
formatInfoBits2 := 0;
jMin := (dimension - 7);
for j := (dimension - 1) downto jMin do
formatInfoBits2 := self.copyBit(8, j, formatInfoBits2);
for i := (dimension - 8) to Pred(dimension) do
formatInfoBits2 := self.copyBit(i, 8, formatInfoBits2);
parsedFormatInfo := TFormatInformation.DecodeFormatInformation(formatInfoBits1, formatInfoBits2);
if (parsedFormatInfo <> nil)
then
Result := Self.parsedFormatInfo
else
Result := nil;
end;
function TBitMatrixParser.readVersion: TVersion;
var
dimension,
provisionalVersion,
versionBits,
i, j, ijMin: Integer;
begin
if (Self.parsedVersion <> nil) then
begin
Result := Self.parsedVersion;
exit;
end;
dimension := Self.bitMatrix.Height;
provisionalVersion := TMathUtils.Asr((dimension - 17), 2);
if (provisionalVersion <= 6) then
begin
Result := TVersion.getVersionForNumber(provisionalVersion);
exit;
end;
// Read top-right version info: 3 wide by 6 tall
versionBits := 0;
ijMin := (dimension - 11);
for j := 5 downto 0 do
for i := (dimension - 9) downto ijMin do
versionBits := Self.copyBit(i, j, versionBits);
parsedVersion := TVersion.decodeVersionInformation(versionBits);
if ((parsedVersion <> nil) and
(parsedVersion.DimensionForVersion = dimension)) then
begin
Result := parsedVersion;
exit;
end;
// Hmm, failed. Try bottom left: 6 wide by 3 tall
versionBits := 0;
for i := 5 downto 0 do
for j := (dimension - 9) downto ijMin do
versionBits := self.copyBit(i, j, versionBits);
parsedVersion := TVersion.decodeVersionInformation(versionBits);
if ((parsedVersion <> nil) and
(parsedVersion.DimensionForVersion = dimension))
then
Result := self.parsedVersion
else
Result := nil;
end;
procedure TBitMatrixParser.remask;
var
dimension: Integer;
begin
if (parsedFormatInfo <> nil) then
begin
dimension := bitMatrix.Height;
TDataMask.forReference(parsedFormatInfo.DataMask)
.unmaskBitMatrix(bitMatrix, dimension);
end;
end;
procedure TBitMatrixParser.setMirror(const mirror: Boolean);
begin
Self.parsedVersion := nil;
Self.parsedFormatInfo := nil;
Self.mirrored := mirror;
end;
end.
@@ -0,0 +1,187 @@
{
* Copyright 2007 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.
* Original Author: Sean Owen
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.DataBlock;
interface
uses
System.SysUtils,
ZXing.QrCode.Internal.Version,
ZXing.QrCode.Internal.ErrorCorrectionLevel;
type
/// <summary> <p>Encapsulates a block of data within a QR Code. QR Codes may split their data into
/// multiple blocks, each of which is a unit of data and error-correction codewords. Each
/// is represented by an instance of this class.</p>
/// </summary>
TDataBlock = class sealed
private
Fcodewords: TArray<Byte>;
FnumDataCodewords: Integer;
public
constructor Create(const codewords: TArray<Byte>;
const NumDataCodewords: Integer);
destructor Destroy; override;
/// <summary> <p>When QR Codes use multiple data blocks, they are actually interleaved.
/// That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
/// method will separate the data into original blocks.</p>
///
/// </summary>
/// <param name="rawCodewords">bytes as read directly from the QR Code
/// </param>
/// <param name="version">version of the QR Code
/// </param>
/// <param name="ecLevel">error-correction level of the QR Code
/// </param>
/// <returns> {@link TDataBlock}s containing original bytes, "de-interleaved" from representation in the
/// QR Code
/// </returns>
class function getDataBlocks(const rawCodewords: TArray<Byte>;
const version: TVersion; const ecLevel: TErrorCorrectionLevel): TArray<TDataBlock>; static;
property codewords : TArray<Byte> read Fcodewords;
property numDataCodewords : Integer read FnumDataCodewords;
end;
implementation
{ TDataBlock }
constructor TDataBlock.Create(const codewords: TArray<Byte>;
const numDataCodewords: Integer);
begin
Fcodewords := codewords;
FnumDataCodewords := numDataCodewords;
end;
destructor TDataBlock.Destroy;
begin
Fcodewords := nil;
inherited;
end;
class function TDataBlock.getDataBlocks(const rawCodewords: TArray<Byte>;
const version: TVersion; const ecLevel: TErrorCorrectionLevel): TArray<TDataBlock>;
var
numResultBlocks,
shorterBlocksTotalCodewords,
longerBlocksStartAt,
shorterBlocksNumDataCodewords,
max, i, j,
totalBlocks,
numBlockCodewords,
iOffset,
rawCodewordsOffset,
NumDataCodewords: Integer;
ecBlock: Tversion.TECB;
ecBlocks: Tversion.TECBlocks;
ecBlockArray: TArray<Tversion.TECB>;
numBlockCodewordsBytes: TArray<Byte>;
begin
if (Length(rawCodewords) <> Version.TotalCodewords)
then
raise EArgumentException.Create('Invalid arguments');
// Figure out the number and size of data blocks used by this version and
// error correction level
ecBlocks := version.getECBlocksForLevel(ecLevel);
// First count the total number of data blocks
totalBlocks := 0;
ecBlockArray := ecBlocks.getECBlocks;
for ecBlock in ecBlockArray do
Inc(totalBlocks, ecBlock.Count);
// Now establish DataBlocks of the appropriate size and number of data codewords
Result := TArray<TDataBlock>.Create();
SetLength(result, totalBlocks);
numResultBlocks := 0;
for ecBlock in ecBlockArray do
begin
for i := 0 to Pred(ecBlock.count) do
begin
numDataCodewords := ecBlock.DataCodewords;
numBlockCodewords := ecBlocks.ECCodewordsPerBlock + numDataCodewords;
numBlockCodewordsBytes := TArray<Byte>.Create();
SetLength(numBlockCodewordsBytes, numBlockCodewords);
Result[numResultBlocks] := TDataBlock.Create(numBlockCodewordsBytes, numDataCodewords);
Inc(numResultBlocks);
end
end;
numBlockCodewordsBytes := nil;
// All blocks have the same amount of data, except that the last n
// (where n may be 0) have 1 more byte. Figure out where these start.
shorterBlocksTotalCodewords := Length(Result[0].codewords);
longerBlocksStartAt := Length(result) - 1;
while ((longerBlocksStartAt >= 0)) do
begin
if (Length(Result[longerBlocksStartAt].codewords) = shorterBlocksTotalCodewords)
then
break;
Dec(longerBlocksStartAt);
end;
Inc(longerBlocksStartAt);
shorterBlocksNumDataCodewords := (shorterBlocksTotalCodewords - ecBlocks.ECCodewordsPerBlock);
// The last elements of result may be 1 element longer;
// first fill out as many elements as all of them have
rawCodewordsOffset := 0;
for i := 0 to Pred(shorterBlocksNumDataCodewords) do
begin
for j := 0 to Pred(numResultBlocks) do
begin
Result[j].codewords[i] := rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
end;
// Fill out the last data block in the longer ones
for j := longerBlocksStartAt to Pred(numResultBlocks) do
begin
result[j].codewords[shorterBlocksNumDataCodewords] :=
rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
// Now add in error correction blocks
max := Length(result[0].codewords);
for i := shorterBlocksNumDataCodewords to Pred(max) do
begin
for j := 0 to Pred(numResultBlocks) do
begin
if (j < longerBlocksStartAt)
then
iOffset := i
else
iOffset := i + 1;
Result[j].codewords[iOffset] := rawCodewords[rawCodewordsOffset];
Inc(rawCodewordsOffset);
end;
end;
end;
end.
@@ -0,0 +1,226 @@
{
* Copyright 2007 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.
* Original Author: Sean Owen
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.DataMask;
interface
uses
SysUtils,
ZXing.Common.BitMatrix,
ZXing.Common.Detector.MathUtils;
type
/// <summary> <p>Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
/// of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
/// including areas used for finder patterns, timing patterns, etc. These areas should be unused
/// after the point they are unmasked anyway.</p>
///
/// <p>Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
/// and j is row position. In fact, as the text says, i is row position and j is column position.</p>
/// </summary>
TDataMask = class abstract
private
/// <summary> See ISO 18004:2006 6.8.1</summary>
class var DATA_MASKS: TArray<TDataMask>;
class procedure InitializeClass;
class procedure FinalizeClass;
function isMasked(const i, j: Integer): Boolean; virtual; abstract;
public
/// <param name="reference">a value between 0 and 7 indicating one of the eight possible
/// data mask patterns a QR Code may use
/// </param>
/// <returns> {@link DataMask} encapsulating the data mask pattern
/// </returns>
class function forReference(const reference: Integer): TDataMask; static;
/// <summary> <p>Implementations of this method reverse the data masking process applied to a QR Code and
/// make its bits ready to read.</p>
/// </summary>
/// <param name="bits">representation of QR Code bits
/// </param>
/// <param name="dimension">dimension of QR Code, represented by bits, being unmasked
/// </param>
procedure unmaskBitMatrix(const bits: TBitMatrix; const dimension: Integer);
end;
/// <summary> 000: mask bits for which (x + y) mod 2 == 0</summary>
TDataMask000 = class(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 001: mask bits for which x mod 2 == 0</summary>
TDataMask001 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 010: mask bits for which y mod 3 == 0</summary>
TDataMask010 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 011: mask bits for which (x + y) mod 3 == 0</summary>
TDataMask011 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 100: mask bits for which (x/2 + y/3) mod 2 == 0</summary>
TDataMask100 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 101: mask bits for which xy mod 2 + xy mod 3 == 0</summary>
TDataMask101 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0</summary>
TDataMask110 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
/// <summary> 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0</summary>
TDataMask111 = class sealed(TDataMask)
private
function isMasked(const i, j: Integer): Boolean; override;
end;
implementation
{ TDataMask }
class procedure TDataMask.InitializeClass;
begin
TDataMask.DATA_MASKS := TArray<TDataMask>.Create(TDataMask000.Create,
TDataMask001.Create,
TDataMask010.Create,
TDataMask011.Create,
TDataMask100.Create,
TDataMask101.Create,
TDataMask110.Create,
TDataMask111.Create);
end;
class procedure TDataMask.FinalizeClass;
var
dam: TDataMask;
begin
for dam in TDataMask.DATA_MASKS do
dam.Free;
DATA_MASKS := nil;
end;
class function TDataMask.forReference(const reference: Integer): TDataMask;
begin
if ((reference < 0) or (reference > 7))
then
raise EArgumentException.Create('Invalid arguments');
Result := TDataMask.DATA_MASKS[reference];
end;
procedure TDataMask.unmaskBitMatrix(const bits: TBitMatrix;
const dimension: Integer);
var
i, j: Integer;
begin
for i := 0 to Pred(dimension) do
for j := 0 to Pred(dimension) do
if (self.isMasked(i, j))
then
bits.flip(j, i);
end;
{ TDataMask000 }
function TDataMask000.isMasked(const i, j: Integer): Boolean;
begin
Result := ((i + j) and $01) = 0;
end;
{ TDataMask001 }
function TDataMask001.isMasked(const i, j: Integer): Boolean;
begin
Result := (i and $01) = 0;
end;
{ TDataMask010 }
function TDataMask010.isMasked(const i, j: Integer): Boolean;
begin
Result := (j mod 3) = 0;
end;
{ TDataMask011 }
function TDataMask011.isMasked(const i, j: Integer): Boolean;
begin
Result := (((i + j) mod 3) = 0)
end;
{ TDataMask100 }
function TDataMask100.isMasked(const i, j: Integer): Boolean;
begin
Result := (( TMathUtils.Asr(UInt32(i),1) + (j div 3)) and $01) = 0;
end;
{ TDataMask101 }
function TDataMask101.isMasked(const i, j: Integer): Boolean;
var
temp: Integer;
begin
temp := (i * j);
Result := ((temp and $01) + (temp mod 3)) = 0;
end;
{ TDataMask110 }
function TDataMask110.isMasked(const i, j: Integer): Boolean;
var
temp: Integer;
begin
temp := (i * j);
Result := (((temp and $01) + (temp mod 3)) and 1) = 0;
end;
{ TDataMask111 }
function TDataMask111.isMasked(const i, j: Integer): Boolean;
begin
Result := ((((i + j) and $01) + ((i * j) mod 3)) and $01) = 0;
end;
initialization
TDataMask.InitializeClass;
finalization
TDataMask.FinalizeClass;
end.
@@ -0,0 +1,587 @@
{
* Copyright 2007 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.DecodedBitStreamParser;
interface
uses
ZXing.BitSource,
SysUtils,
ZXing.DecodeHintType,
Generics.Collections,
ZXing.QrCode.Internal.ErrorCorrectionLevel,
ZXing.QrCode.Internal.Version,
ZXing.DecoderResult,
ZXing.CharacterSetECI,
ZXing.StringUtils,
ZXing.ByteSegments,
ZXing.QrCode.Internal.Mode,
ZXing.Common.Detector.MathUtils;
type
/// <summary> <p>QR Codes can encode text as bits in one of several modes, and can use multiple modes
/// in one QR Code. This class decodes the bits back into text.</p>
///
/// <p>See ISO 18004:2006, 6.4.3 - 6.4.7</p>
/// </summary>
TDecodedBitStreamParser = class abstract
private
/// <summary>
/// See ISO 18004:2006, 6.4.4 Table 5
/// </summary>
class var ALPHANUMERIC_CHARS: TArray<Char>;
const
GB2312_SUBSET: Integer = 1;
class procedure InitializeClass;
class procedure FinalizeClass;
class function decodeAlphanumericSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer;
const fc1InEffect: Boolean): Boolean; static;
class function decodeByteSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer;
const currentCharacterSetECI: TCharacterSetECI;
const byteSegments: IByteSegments;
const hints: TDictionary<TDecodeHintType, TObject>): Boolean; static;
/// <summary>
/// See specification GBT 18284-2000
/// </summary>
/// <param name="bits">The bits.</param>
/// <param name="result">The result.</param>
/// <param name="count">The count.</param>
/// <returns></returns>
class function decodeHanziSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer): Boolean; static;
class function decodeKanjiSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer): boolean; static;
class function decodeNumericSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer): Boolean; static;
class function parseECIValue(const bits: TBitSource): Integer; static;
class function toAlphaNumericChar(const value: Integer): Char; static;
public
class function decode(const bytes: TArray<Byte>; const version: TVersion;
const ecLevel: TErrorCorrectionLevel;
const hints: TDictionary<TDecodeHintType, TObject>): TDecoderResult; static;
end;
implementation
{ TDecodedBitStreamParser }
class procedure TDecodedBitStreamParser.InitializeClass;
begin
ALPHANUMERIC_CHARS := TArray<Char>.Create('0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ',
'$', '%', '*', '+', '-', '.', '/', ':');
end;
class procedure TDecodedBitStreamParser.FinalizeClass;
begin
ALPHANUMERIC_CHARS := nil;
end;
class function TDecodedBitStreamParser.decode(const bytes: TArray<Byte>;
const version: TVersion; const ecLevel: TErrorCorrectionLevel;
const hints: TDictionary<TDecodeHintType, TObject>): TDecoderResult;
var
Mode: TMode;
bits: TBitSource;
byteSegments: IByteSegments;
fc1InEffect: Boolean;
symbolSequence,
parityData,
count, value,
countHanzi, subSet: Integer;
currentCharacterSetECI: TCharacterSetECI;
ecstring,s: String;
res: TStringBuilder;
begin
Result := nil;
bits := TBitSource.Create(bytes);
res := TStringBuilder.Create(50);
byteSegments := ByteSegmentsCreate();
byteSegments.Capacity := 1;
symbolSequence := -1;
parityData := -1;
try
try
currentCharacterSetECI := nil;
fc1InEffect := false;
repeat
// While still another segment to read...
if (bits.available < 4)
then
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
Mode := TMode.TERMINATOR
else
begin
try
Mode := TMode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
except
on E: EArgumentException do
exit;
end;
end;
if (Mode <> TMode.TERMINATOR) then
begin
if ((Mode = TMode.FNC1_FIRST_POSITION) or
(Mode = TMode.FNC1_SECOND_POSITION)) then
begin
// We do little with FNC1 except alter the parsed result a bit according to the spec
fc1InEffect := true;
end else
begin
if (Mode = TMode.STRUCTURED_APPEND) then
begin
if (bits.available() < 16)
then
exit;
// not really supported; but sequence number and parity is added later to the result metadata
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
symbolSequence := bits.readBits(8);
parityData := bits.readBits(8);
end else
begin
if (Mode = TMode.ECI) then
begin
// Count doesn't apply to ECI
value := parseECIValue(bits);
currentCharacterSetECI := TCharacterSetECI.getCharacterSetECIByValue(value);
if (currentCharacterSetECI = nil)
then
exit;
end else
begin
// First handle Hanzi mode which does not start with character count
if (Mode = TMode.HANZI) then
begin
// chinese mode contains a sub set indicator right after mode indicator
subSet := bits.readBits(4);
countHanzi := bits.readBits(Mode.getCharacterCountBits(version));
if (subSet = GB2312_SUBSET) then
begin
if (not TDecodedBitStreamParser.decodeHanziSegment(bits,
res, countHanzi))
then
exit;
end;
end else
begin
// "Normal" QR code modes:
// How many characters will follow, encoded in this mode?
count := bits.readBits(Mode.getCharacterCountBits(version));
if (Mode = TMode.NUMERIC) then
begin
if (not TDecodedBitStreamParser.decodeNumericSegment(bits,
res, count))
then
exit;
end else
begin
if (Mode = TMode.ALPHANUMERIC) then
begin
if (not TDecodedBitStreamParser.
decodeAlphanumericSegment(bits, res, count, fc1InEffect))
then
exit;
end else
begin
if (Mode = TMode.BYTE) then
begin
if (not TDecodedBitStreamParser.
decodeByteSegment(bits, res, count,
currentCharacterSetECI, byteSegments, hints))
then
exit;
end else
begin
if (Mode <> TMode.KANJI) then
begin
if (not TDecodedBitStreamParser.
decodeKanjiSegment(bits, res, count))
then
exit;
end else exit;
end;
end;
end;
end;
end;
end;
end;
end;
until (Mode = TMode.TERMINATOR)
except
on E: EArgumentException do
exit;
end;
if (byteSegments.count = 0)
then
byteSegments := nil;
if (ecLevel = nil)
then
ecstring := ''
else
ecstring := ecLevel.toString();
s:= res.toString.Replace(#13+#10, #10).Replace(#10, #13);
Result := TDecoderResult.Create(bytes, s, byteSegments, ecstring, symbolSequence,
parityData);
finally
// FreeAndNil(byteSegments);
res.Clear();
FreeAndNil(res);
FreeAndNil(bits);
end;
end;
class function TDecodedBitStreamParser.decodeAlphanumericSegment(
const bits: TBitSource; const res: TStringBuilder; count: Integer;
const fc1InEffect: Boolean): Boolean;
var
start, i, nextTwoCharsBits: Integer;
charArray: TArray<Char>;
begin
Result := false;
// Read two characters at a time
start := res.Length;
while ((count > 1)) do
begin
if (bits.available < 11)
then
exit;
nextTwoCharsBits := bits.readBits(11);
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((nextTwoCharsBits div 45)));
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((nextTwoCharsBits mod 45)));
Dec(count, 2);
end;
if (count = 1) then
begin
// special case: one character left
if (bits.available < 6)
then
exit;
res.Append(TDecodedBitStreamParser.toAlphaNumericChar(bits.readBits(6)))
end;
// See section 6.4.8.1, 6.4.8.2
if (fc1InEffect) then
begin
// We need to massage the result a bit if in an FNC1 mode:
for i := start to Pred(res.Length) do
begin
if (res.Chars[i] = '%') then
begin
if ((i < (res.Length - 1)) and (res.Chars[(i + 1)] = '%'))
then
// %% is rendered as %
res.Remove((i + 1), 1)
else
begin
// In alpha mode, % should be converted to FNC1 separator 0x1D
res.Remove(i, 1);
charArray := TArray<Char>.Create(Char($1D));
res.Insert(i, charArray);
end;
end;
end;
end;
Result := true;
end;
class function TDecodedBitStreamParser.decodeByteSegment(const bits: TBitSource;
const res: TStringBuilder; count: Integer;
const currentCharacterSetECI: TCharacterSetECI;
const byteSegments: IByteSegments;
const hints: TDictionary<TDecodeHintType, TObject>): Boolean;
var
Enc:TEncoding;
encodingS, s: string;
readBytes: TArray<Byte>;
i: Integer;
begin
Result := false;
// Don't crash trying to read more bits than we have available.
if ((count shl 3) > bits.available)
then
exit;
readBytes := TArray<Byte>.Create();
SetLength(readBytes, count);
for i := 0 to Pred(count) do
readBytes[i] := Byte(bits.readBits(8));
if (currentCharacterSetECI = nil)
then
// The spec isn't clear on this mode; see
// section 6.4.5: t does not say which encoding to assuming
// upon decoding. I have seen ISO-8859-1 used as well as
// Shift_JIS -- without anything like an ECI designator to
// give a hint.
encodingS := TStringUtils.guessEncoding(readBytes, hints)
else
encodingS := currentCharacterSetECI.EncodingName;
try
enc := TEncoding.GetEncoding(encodingS);
try
s := enc.GetString(readBytes, 0, Length(readBytes));
res.Append(s);
finally
FreeAndNil(enc);
end;
except
on E: Exception do
exit;
end;
byteSegments.Add(readBytes);
result := true;
end;
class function TDecodedBitStreamParser.decodeHanziSegment(
const bits: TBitSource; const res: TStringBuilder; count: Integer): Boolean;
var
buffer: TArray<Byte>;
offset, twoBytes,
assembledTwoBytes: Integer;
begin
Result := false;
// Don't crash trying to read more bits than we have available.
if ((count * 13) > bits.available)
then
exit;
// Each character will require 2 bytes. Read the characters as 2-byte pairs
// and decode as GB2312 afterwards
buffer := TArray<Byte>.Create();
SetLength(buffer, 2 * count);
offset := 0;
while ((count > 0)) do
begin
// Each 13 bits encodes a 2-byte character
twoBytes := bits.readBits(13);
assembledTwoBytes := (((twoBytes div $60) shl 8) or (twoBytes mod $60));
if (assembledTwoBytes < $00A00)
then
// In the 0xA1A1 to 0xAAFE range
Inc(assembledTwoBytes, $0A1A1)
else
// In the 0xB0A1 to 0xFAFE range
Inc(assembledTwoBytes, $0A6A1);
buffer[offset] := Byte(TMathUtils.Asr(assembledTwoBytes, 8) and $FF);
buffer[(offset + 1)] := Byte(assembledTwoBytes and $FF);
Inc(offset, 2);
Dec(count);
end;
try
res.Append(Tencoding.GetEncoding(TStringUtils.GB2312).GetString(buffer, 0,
Length(buffer)))
except
on E: Exception do
exit;
end;
Result := true;
end;
class function TDecodedBitStreamParser.decodeKanjiSegment(
const bits: TBitSource; const res: TStringBuilder; count: Integer): Boolean;
var
buffer: TArray<Byte>;
twoBytes, offset,
assembledTwoBytes: Integer;
begin
Result := false;
// Don't crash trying to read more bits than we have available.
if ((count * 13) > bits.available)
then
exit;
// Each character will require 2 bytes. Read the characters as 2-byte pairs
// and decode as Shift_JIS afterwards
buffer := TArray<Byte>.Create();
SetLength(buffer, 2 * count);
offset := 0;
while ((count > 0)) do
begin
// Each 13 bits encodes a 2-byte character
twoBytes := bits.readBits(13);
assembledTwoBytes := (((twoBytes div $0C0) shl 8) or (twoBytes mod $0C0));
if (assembledTwoBytes < $1F00)
then
// In the 0x8140 to 0x9FFC range
Inc(assembledTwoBytes, $08140)
else
Inc(assembledTwoBytes, $0C140);
buffer[offset] := Byte( TMathUtils.Asr(assembledTwoBytes,8));
buffer[(offset + 1)] := Byte(assembledTwoBytes);
Inc(offset, 2);
Dec(count);
end;
try
res.Append(Tencoding.GetEncoding(TStringUtils.SHIFT_JIS).GetString(buffer,
0, Length(buffer)))
except
on E: Exception do
exit;
end;
Result := true;
end;
class function TDecodedBitStreamParser.decodeNumericSegment(
const bits: TBitSource; const res: TStringBuilder; count: Integer): Boolean;
var
threeDigitsBits,
twoDigitsBits,
digitBits: Integer;
begin
Result := false;
// Read three digits at a time
while ((count >= 3)) do
begin
if (bits.available < 10)
then
exit;
threeDigitsBits := bits.readBits(10);
if (threeDigitsBits >= 1000)
then
exit;
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((threeDigitsBits div 100)));
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
(((threeDigitsBits div 10) mod 10)));
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((threeDigitsBits mod 10)));
Dec(count, 3)
end;
if (count = 2) then
begin
// Two digits left over to read, encoded in 7 bits
if (bits.available < 7)
then
exit;
twoDigitsBits := bits.readBits(7);
if (twoDigitsBits >= 100)
then
exit;
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((twoDigitsBits div 10)));
res.Append(TDecodedBitStreamParser.toAlphaNumericChar
((twoDigitsBits mod 10)));
end else
begin
if (count = 1) then
begin
// One digit left over to read
if (bits.available < 4)
then
exit;
digitBits := bits.readBits(4);
if (digitBits >= 10)
then
exit;
res.Append(TDecodedBitStreamParser.toAlphaNumericChar(digitBits))
end;
end;
Result := true;
end;
class function TDecodedBitStreamParser.parseECIValue(
const bits: TBitSource): Integer;
var
firstByte,
secondByte,
SecondThirdBytes: Integer;
begin
firstByte := bits.readBits(8);
if ((firstByte and $80) = 0) then
begin
// just one byte
Result := (firstByte and $7F);
exit;
end;
if ((firstByte and $C0) = $80) then
begin
// two bytes
secondByte := bits.readBits(8);
Result := (((firstByte and $3F) shl 8) or secondByte);
exit;
end;
if ((firstByte and $E0) = $C0) then
begin
// three bytes
SecondThirdBytes := bits.readBits(16);
Result := (((firstByte and $1F) shl 16) or SecondThirdBytes);
exit;
end;
raise EArgumentException.Create('Bad ECI bits starting with byte ' +
firstByte.toString());
end;
class function TDecodedBitStreamParser.toAlphaNumericChar(
const value: Integer): Char;
begin
if (value >= Length(ALPHANUMERIC_CHARS))
then
raise Exception.Create('Format exception');
Result := ALPHANUMERIC_CHARS[value];
end;
initialization
TDecodedBitStreamParser.InitializeClass;
finalization
TDecodedBitStreamParser.FinalizeClass;
end.
@@ -0,0 +1,278 @@
{
* Copyright 2007 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.
* Original Author: Sean Owen
* Delphi Implementation by E.Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.Decoder;
interface
uses
System.SysUtils,
System.Generics.Collections,
ZXing.DecodeHintType,
ZXing.Common.BitMatrix,
ZXing.QrCode.Internal.BitMatrixParser,
ZXing.DecoderResult,
ZXing.Common.ReedSolomon.ReedSolomonDecoder,
ZXing.Common.ReedSolomon.GenericGF,
ZXing.QrCode.Internal.QRCodeDecoderMetaData,
ZXing.QrCode.Internal.Version,
ZXing.QrCode.Internal.FormatInformation,
ZXing.QrCode.Internal.ErrorCorrectionLevel,
ZXing.QrCode.Internal.DataBlock,
ZXing.QrCode.Internal.DecodedBitStreamParser;
type
/// <summary>
/// <p>The main class which implements QR Code decoding -- as opposed to locating and extracting
/// the QR Code from an image.</p>
/// </summary>
TQRDecoder = class
private
rsDecoder: TReedSolomonDecoder;
/// <summary>
/// <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to
/// correct the errors in-place using Reed-Solomon error correction.</p>
/// </summary>
/// <param name="codewordBytes">data and error correction codewords</param>
/// <param name="numDataCodewords">number of codewords that are data bytes</param>
/// <returns></returns>
function correctErrors(const codewordBytes: TArray<Byte>;
const numDataCodewords: Integer): Boolean;
function decode(const parser: TBitMatrixParser;
const hints: TDictionary<TDecodeHintType, TObject>)
: TDecoderResult; overload;
public
/// <summary>
/// Initializes a new instance of the <see cref="Decoder"/> class.
/// </summary>
constructor Create;
destructor Destroy; override;
/// <summary>
/// <p>Convenience method that can decode a QR Code represented as a 2D array of booleans.
/// "true" is taken to mean a black module.</p>
/// </summary>
/// <param name="image">booleans representing white/black QR Code modules</param>
/// <param name="hints">decoding hints that should be used to influence decoding</param>
/// <returns>
/// text and bytes encoded within the QR Code
/// </returns>
function decode(const image: TArray<TArray<Boolean>>;
const hints: TDictionary<TDecodeHintType, TObject>)
: TDecoderResult; overload;
/// <summary>
/// <p>Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.</p>
/// </summary>
/// <param name="bits">booleans representing white/black QR Code modules</param>
/// <param name="hints">decoding hints that should be used to influence decoding</param>
/// <returns>
/// text and bytes encoded within the QR Code
/// </returns>
function decode(const bits: TBitMatrix;
const hints: TDictionary<TDecodeHintType, TObject>)
: TDecoderResult; overload;
end;
implementation
{ TQRDecoder }
constructor TQRDecoder.Create;
begin
rsDecoder := TReedSolomonDecoder.Create(TGenericGF.QR_CODE_FIELD_256);
end;
destructor TQRDecoder.Destroy;
begin
FreeAndNil(rsDecoder);
inherited;
end;
function TQRDecoder.correctErrors(const codewordBytes: TArray<Byte>;
const numDataCodewords: Integer): Boolean;
var
i, numCodewords, numECCodewords: Integer;
codewordsInts: TArray<Integer>;
begin
Result := false;
numCodewords := Length(codewordBytes);
// First read into an array of ints
codewordsInts := TArray<Integer>.Create();
SetLength(codewordsInts, numCodewords);
for i := 0 to Pred(numCodewords) do
codewordsInts[i] := (codewordBytes[i] and $FF);
numECCodewords := Length(codewordBytes) - numDataCodewords;
if (not rsDecoder.decode(codewordsInts, numECCodewords)) then
exit;
// Copy back into array of bytes -- only need to worry about the bytes that were data
// We don't care about errors in the error-correction codewords
for i := 0 to Pred(numDataCodewords) do
codewordBytes[i] := Byte(codewordsInts[i]);
Result := true;
end;
function TQRDecoder.decode(const image: TArray<TArray<Boolean>>;
const hints: TDictionary<TDecodeHintType, TObject>): TDecoderResult;
var
dimension, i, j: Integer;
bits: TBitMatrix;
begin
dimension := Length(image);
bits := TBitMatrix.Create(dimension);
try
for i := 0 to Pred(dimension) do
for j := 0 to Pred(dimension) do
bits[j, i] := image[i][j];
Result := decode(bits, hints);
finally
bits.Free;
end;
end;
function TQRDecoder.decode(const bits: TBitMatrix;
const hints: TDictionary<TDecodeHintType, TObject>): TDecoderResult;
var
parser: TBitMatrixParser;
begin
Result := nil;
// Construct a parser and read version, error-correction level
parser := TBitMatrixParser.createBitMatrixParser(bits);
if Assigned(parser) then
begin
try
Result := decode(parser, hints);
if (Result = nil) then
begin
// Revert the bit matrix
parser.remask;
// Will be attempting a mirrored reading of the version and format info.
parser.setMirror(true);
// Preemptively read the version.
if (parser.readVersion = nil) then
exit;
// Preemptively read the format information.
if (parser.readFormatInformation = nil) then
exit;
(*
* Since we're here, this means we have successfully detected some kind
* of version and format information when mirrored. This is a good sign,
* that the QR code may be mirrored, and we should try once more with a
* mirrored content.
*)
// Prepare for a mirrored reading.
parser.mirror;
Result := decode(parser, hints);
if (Result <> nil) then
// Success! Notify the caller that the code was mirrored.
Result.Other := TQRCodeDecoderMetaData.Create(true);
end;
finally
parser.Free;
end;
end;
end;
function TQRDecoder.decode(const parser: TBitMatrixParser;
const hints: TDictionary<TDecodeHintType, TObject>): TDecoderResult;
var
DataBlock: TDataBlock;
dataBlocks: TArray<TDataBlock>;
Version: TVersion;
formatInfo: TFormatInformation;
ecLevel: TErrorCorrectionLevel;
codeWords, resultBytes, codewordBytes: TArray<Byte>;
totalBytes, resultOffset, i, numDataCodewords: Integer;
begin
Result := nil;
Version := parser.readVersion;
if (Version = nil) then
exit;
formatInfo := parser.readFormatInformation;
if (formatInfo = nil) then
exit;
ecLevel := formatInfo.ErrorCorrectionLevel;
// Read codewords
codeWords := parser.readCodewords;
if (codeWords = nil) then
begin
FreeAndNil(formatInfo);
exit;
end;
// Separate into data blocks
dataBlocks := TDataBlock.getDataBlocks(codeWords, Version, ecLevel);
// Count total number of data bytes
totalBytes := 0;
for DataBlock in dataBlocks do
Inc(totalBytes, DataBlock.numDataCodewords);
resultBytes := TArray<Byte>.Create();
SetLength(resultBytes, totalBytes);
try
resultOffset := 0;
// Error-correct and copy data blocks together into a stream of bytes
for DataBlock in dataBlocks do
begin
codewordBytes := DataBlock.codeWords;
numDataCodewords := DataBlock.numDataCodewords;
if (not self.correctErrors(codewordBytes, numDataCodewords)) then
begin
exit(nil);
end;
for i := 0 to Pred(numDataCodewords) do
begin
resultBytes[resultOffset] := codewordBytes[i];
Inc(resultOffset);
end;
end;
// Decode the contents of that stream of bytes
Result := TDecodedBitStreamParser.decode(resultBytes, Version,
ecLevel, hints);
finally
for DataBlock in dataBlocks do
DataBlock.Free;
FreeAndNil(formatInfo);
end;
end;
end.
@@ -0,0 +1,149 @@
{
* Copyright 2007 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.ErrorCorrectionLevel;
interface
uses SysUtils;
type
/// <summary>
/// <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
/// defined by the QR code standard.</p>
/// </summary>
TErrorCorrectionLevel = class sealed
private
Fbits: Integer;
Fname: string;
Fordinal_Renamed_Field: Integer;
class var FOR_BITS: TArray<TErrorCorrectionLevel>;
class procedure InitalizeClass; static;
class procedure FinalizeClass; static;
public
constructor Create(const ordinal, bits: Integer; const name: String);
/// <summary>
/// Fors the bits.
/// </summary>
/// <param name="bits">int containing the two bits encoding a QR Code's error correction level</param>
/// <returns>
/// <see cref="ErrorCorrectionLevel"/> representing the encoded error correction level
/// </returns>
class function forBits(const bits: Integer): TErrorCorrectionLevel; static;
/// <summary> L = ~7% correction</summary>
class var L: TErrorCorrectionLevel;
/// <summary> M = ~15% correction</summary>
class var M: TErrorCorrectionLevel;
/// <summary> Q = ~25% correction</summary>
class var Q: TErrorCorrectionLevel;
/// <summary> H = ~30% correction</summary>
class var H: TErrorCorrectionLevel;
/// <summary>
/// Ordinals this instance.
/// </summary>
/// <returns></returns>
function ordinal: Integer;
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
function ToString: String; override;
/// <summary>
/// Gets the bits.
/// </summary>
property Bits: Integer read Fbits;
/// <summary>
/// Gets the name.
/// </summary>
property Name: String read Fname;
end;
implementation
{ TErrorCorrectionLevel }
constructor TErrorCorrectionLevel.Create(const ordinal, bits: Integer;
const name: String);
begin
Fordinal_Renamed_Field := ordinal;
Fbits := bits;
Fname := name
end;
class procedure TErrorCorrectionLevel.InitalizeClass;
begin
H := TErrorCorrectionLevel.Create(3, 2, 'H');
L := TErrorCorrectionLevel.Create(0, 1, 'L');
M := TErrorCorrectionLevel.Create(1, 0, 'M');
Q := TErrorCorrectionLevel.Create(2, 3, 'Q');
FOR_BITS := TArray<TErrorCorrectionLevel>.Create(M, L, H, Q);
end;
class procedure TErrorCorrectionLevel.FinalizeClass;
begin
H.FOR_BITS := nil;
L.FOR_BITS := nil;
M.FOR_BITS := nil;
Q.FOR_BITS := nil;
FreeAndNil(H);
FreeAndNil(L);
FreeAndNil(M);
FreeAndNil(Q);
FOR_BITS := nil;
end;
class function TErrorCorrectionLevel.forBits(const bits: Integer): TErrorCorrectionLevel;
begin
if ((bits < 0) or
(bits >= Length(TErrorCorrectionLevel.FOR_BITS)))
then
raise EArgumentException.Create('Invalid argument');
Result := TErrorCorrectionLevel.FOR_BITS[bits];
end;
function TErrorCorrectionLevel.ordinal: Integer;
begin
Result := Fordinal_Renamed_Field;
end;
function TErrorCorrectionLevel.ToString: string;
begin
Result := Fname
end;
initialization
TErrorCorrectionLevel.InitalizeClass;
finalization
TErrorCorrectionLevel.FinalizeClass;
end.
@@ -0,0 +1,256 @@
{
* Copyright 2007 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.
* Original Author: Sean Owen
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.FormatInformation;
interface
uses
Generics.Collections,
ZXing.QrCode.Internal.ErrorCorrectionLevel,
Classes,
ZXing.Common.Detector.MathUtils;
type
/// <summary> <p>Encapsulates a QR Code's format information, including the data mask used and
/// error correction level.</p>
///
/// </summary>
/// <seealso cref="TDataMask">
/// </seealso>
/// <seealso cref="TErrorCorrectionLevel">
/// </seealso>
TFormatInformation = class sealed
private
FErrorCorrectionLevel: TErrorCorrectionLevel;
FDataMask: Byte;
const
FORMAT_INFO_MASK_QR: Integer = $5412;
/// <summary> See ISO 18004:2006, Annex C, Table C.1</summary>
class var FORMAT_INFO_DECODE_LOOKUP: TArray<TArray<Integer>>;
/// <summary> Offset i holds the number of 1 bits in the binary representation of i</summary>
class var BITS_SET_IN_HALF_BYTE: TArray<Integer>;
class procedure InitializeClass; static;
class procedure FinalizeClass; static;
class function doDecodeFormatInformation(const maskedFormatInfo1,
maskedFormatInfo2: Integer): TFormatInformation; static;
public
constructor Create(const formatInfo: Integer);
function Equals(o: TObject): Boolean; override;
function GetHashCode: Integer; override;
class function numBitsDiffering(a, b: Integer): Integer; static;
/// <summary>
/// Decodes the format information.
/// </summary>
/// <param name="maskedFormatInfo1">format info indicator, with mask still applied</param>
/// <param name="maskedFormatInfo2">The masked format info2.</param>
/// <returns>
/// information about the format it specifies, or <code>null</code>
/// if doesn't seem to match any known pattern
/// </returns>
class function decodeFormatInformation(const maskedFormatInfo1,
maskedFormatInfo2: Integer): TFormatInformation; static;
property ErrorCorrectionLevel: TErrorCorrectionLevel
read FErrorCorrectionLevel;
property DataMask: Byte read FDataMask;
end;
implementation
{ TFormatInformation }
constructor TFormatInformation.Create(const formatInfo: Integer);
begin
// Bits 3,4
FErrorCorrectionLevel := TErrorCorrectionLevel.forBits
(TMathUtils.Asr(formatInfo, 3) and $03);
// Bottom 3 bits
FDataMask := Byte(formatInfo and 7);
end;
class function TFormatInformation.decodeFormatInformation
(const maskedFormatInfo1, maskedFormatInfo2: Integer): TFormatInformation;
var
formatInfo: TFormatInformation;
begin
formatInfo := doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
if (formatInfo <> nil) then
Result := formatInfo
else
// Should return null, but, some QR codes apparently
// do not mask this info. Try again by actually masking the pattern
// first
Result := TFormatInformation.doDecodeFormatInformation
((maskedFormatInfo1 xor FORMAT_INFO_MASK_QR),
(maskedFormatInfo2 xor FORMAT_INFO_MASK_QR));
end;
class function TFormatInformation.doDecodeFormatInformation
(const maskedFormatInfo1, maskedFormatInfo2: Integer): TFormatInformation;
var
bestDifference, bestFormatInfo, bitsDifference, targetInfo: Integer;
decodeInfo: TArray<Integer>;
begin
Result := nil;
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
bestDifference := High(Integer);
bestFormatInfo := 0;
for decodeInfo in FORMAT_INFO_DECODE_LOOKUP do
begin
targetInfo := decodeInfo[0];
if ((targetInfo = maskedFormatInfo1) or (targetInfo = maskedFormatInfo2))
then
begin
// Found an exact match
Result := TFormatInformation.Create(decodeInfo[1]);
exit;
end;
bitsDifference := numBitsDiffering(maskedFormatInfo1, targetInfo);
if (bitsDifference < bestDifference) then
begin
bestFormatInfo := decodeInfo[1];
bestDifference := bitsDifference;
end;
if (maskedFormatInfo1 <> maskedFormatInfo2) then
begin
// also try the other option
bitsDifference := numBitsDiffering(maskedFormatInfo2, targetInfo);
if (bitsDifference < bestDifference) then
begin
bestFormatInfo := decodeInfo[1];
bestDifference := bitsDifference;
end;
end;
end;
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
// differing means we found a match
if (bestDifference <= 3) then
begin
Result := TFormatInformation.Create(bestFormatInfo);
exit;
end;
end;
function TFormatInformation.Equals(o: TObject): Boolean;
var
other: TFormatInformation;
begin
Result := false;
if (not(o is TFormatInformation)) then
exit;
other := TFormatInformation(o);
Result := ((FErrorCorrectionLevel = other.ErrorCorrectionLevel) and
(FDataMask = other.DataMask));
end;
function TFormatInformation.GetHashCode: Integer;
begin
Result := ((FErrorCorrectionLevel.ordinal shl 3) or FDataMask)
end;
class procedure TFormatInformation.InitializeClass;
begin
SetLength(FORMAT_INFO_DECODE_LOOKUP, $20);
FORMAT_INFO_DECODE_LOOKUP[0] := TArray<Integer>.Create($5412, 0);
FORMAT_INFO_DECODE_LOOKUP[1] := TArray<Integer>.Create($5125, 1);
FORMAT_INFO_DECODE_LOOKUP[2] := TArray<Integer>.Create($5E7C, 2);
FORMAT_INFO_DECODE_LOOKUP[3] := TArray<Integer>.Create($5B4B, 3);
FORMAT_INFO_DECODE_LOOKUP[4] := TArray<Integer>.Create($45F9, 4);
FORMAT_INFO_DECODE_LOOKUP[5] := TArray<Integer>.Create($40CE, 5);
FORMAT_INFO_DECODE_LOOKUP[6] := TArray<Integer>.Create($4F97, 6);
FORMAT_INFO_DECODE_LOOKUP[7] := TArray<Integer>.Create($4AA0, 7);
FORMAT_INFO_DECODE_LOOKUP[8] := TArray<Integer>.Create($77C4, 8);
FORMAT_INFO_DECODE_LOOKUP[9] := TArray<Integer>.Create($72F3, 9);
FORMAT_INFO_DECODE_LOOKUP[10] := TArray<Integer>.Create($7DAA, 10);
FORMAT_INFO_DECODE_LOOKUP[11] := TArray<Integer>.Create($789D, 11);
FORMAT_INFO_DECODE_LOOKUP[12] := TArray<Integer>.Create($662F, 12);
FORMAT_INFO_DECODE_LOOKUP[13] := TArray<Integer>.Create($6318, 13);
FORMAT_INFO_DECODE_LOOKUP[14] := TArray<Integer>.Create($6C41, 14);
FORMAT_INFO_DECODE_LOOKUP[15] := TArray<Integer>.Create($6976, 15);
FORMAT_INFO_DECODE_LOOKUP[$10] := TArray<Integer>.Create($1689, $10);
FORMAT_INFO_DECODE_LOOKUP[$11] := TArray<Integer>.Create($13BE, $11);
FORMAT_INFO_DECODE_LOOKUP[$12] := TArray<Integer>.Create($1CE7, $12);
FORMAT_INFO_DECODE_LOOKUP[$13] := TArray<Integer>.Create($19D0, $13);
FORMAT_INFO_DECODE_LOOKUP[20] := TArray<Integer>.Create($762, 20);
FORMAT_INFO_DECODE_LOOKUP[$15] := TArray<Integer>.Create($255, $15);
FORMAT_INFO_DECODE_LOOKUP[$16] := TArray<Integer>.Create($D0C, $16);
FORMAT_INFO_DECODE_LOOKUP[$17] := TArray<Integer>.Create($83B, $17);
FORMAT_INFO_DECODE_LOOKUP[$18] := TArray<Integer>.Create($355F, $18);
FORMAT_INFO_DECODE_LOOKUP[$19] := TArray<Integer>.Create($3068, $19);
FORMAT_INFO_DECODE_LOOKUP[$1A] := TArray<Integer>.Create($3F31, $1A);
FORMAT_INFO_DECODE_LOOKUP[$1B] := TArray<Integer>.Create($3A06, $1B);
FORMAT_INFO_DECODE_LOOKUP[$1C] := TArray<Integer>.Create($24B4, $1C);
FORMAT_INFO_DECODE_LOOKUP[$1D] := TArray<Integer>.Create($2183, $1D);
FORMAT_INFO_DECODE_LOOKUP[30] := TArray<Integer>.Create($2EDA, 30);
FORMAT_INFO_DECODE_LOOKUP[$1F] := TArray<Integer>.Create($2BED, $1F);
BITS_SET_IN_HALF_BYTE := TArray<Integer>.Create(0, 1, 1, 2, 1, 2, 2, 3, 1, 2,
2, 3, 2, 3, 3, 4);
end;
class procedure TFormatInformation.FinalizeClass;
var
i: Integer;
begin
for i := 0 to Pred(Length(FORMAT_INFO_DECODE_LOOKUP)) do
FORMAT_INFO_DECODE_LOOKUP[i] := nil;
FORMAT_INFO_DECODE_LOOKUP := nil;
BITS_SET_IN_HALF_BYTE := nil;
end;
class function TFormatInformation.numBitsDiffering(a, b: Integer): Integer;
begin
a := (a xor b); // a now has a 1 bit exactly where its bit differs with b's
// Count bits set quickly with a series of lookups:
Result := BITS_SET_IN_HALF_BYTE[(a and $0F)] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 4) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 8) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 12) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 16) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 20) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 24) and $0F] + BITS_SET_IN_HALF_BYTE
[TMathUtils.Asr(UInt32(a), 28) and $0F];
end;
initialization
TFormatInformation.InitializeClass;
finalization
TFormatInformation.FinalizeClass;
end.
@@ -0,0 +1,248 @@
{
* Copyright 2007 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.
* Original Author: Sean Owen
* Delphi Implementation by E.Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.Mode;
interface
uses
System.SysUtils,
ZXing.QrCode.Internal.Version;
type
/// <summary>
/// <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
/// data can be encoded to bits in the QR code standard.</p>
/// </summary>
TMode = class sealed
private
Fbits : Integer;
Fname : String;
characterCountBitsForVersions: TArray<Integer>;
class procedure InitializeClass; static;
class procedure FinalizeClass; static;
public
constructor Create(const characterCountBitsForVersions: TArray<Integer>;
const bits: Integer; const name: String); overload;
// No, we can't use an enum here. J2ME doesn't support it.
/// <summary>
///
/// </summary>
class var TERMINATOR: TMode;
/// <summary>
///
/// </summary>
class var NUMERIC: TMode;
/// <summary>
///
/// </summary>
class var ALPHANUMERIC: TMode;
/// <summary>
///
/// </summary>
class var STRUCTURED_APPEND: TMode;
/// <summary>
///
/// </summary>
class var BYTE: TMode;
/// <summary>
///
/// </summary>
class var ECI: TMode; // character counts don't apply
/// <summary>
///
/// </summary>
class var KANJI: TMode;
/// <summary>
///
/// </summary>
class var FNC1_FIRST_POSITION: TMode;
/// <summary>
///
/// </summary>
class var FNC1_SECOND_POSITION: TMode;
/// <summary>See GBT 18284-2000; "Hanzi" is a transliteration of this mode name.</summary>
class var HANZI: TMode;
/// <summary>
/// Fors the bits.
/// </summary>
/// <param name="bits">four bits encoding a QR Code data mode</param>
/// <returns>
/// <see cref="Mode"/> encoded by these bits
/// </returns>
/// <exception cref="ArgumentException">if bits do not correspond to a known mode</exception>
class function forBits(const bits: Integer): TMode; static;
/// <param name="version">version in question
/// </param>
/// <returns> number of bits used, in this QR Code symbol {@link Version}, to encode the
/// count of characters that will follow encoded in this {@link Mode}
/// </returns>
function getCharacterCountBits(const version: TVersion): Integer;
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
function ToString: String; override;
/// <summary>
/// Gets the name.
/// </summary>
property Name: String read Fname;
/// <summary>
/// Gets the bits.
/// </summary>
property Bits: Integer read Fbits;
end;
implementation
{ TMode }
constructor TMode.Create(const characterCountBitsForVersions: TArray<Integer>;
const bits: Integer; const name: String);
begin
Self.characterCountBitsForVersions := characterCountBitsForVersions;
Self.Fbits := bits;
Self.Fname := name
end;
class procedure TMode.InitializeClass;
begin
TMode.TERMINATOR := TMode.Create(TArray<Integer>.Create(0, 0, 0), 0, 'TERMINATOR');
TMode.NUMERIC := TMode.Create(TArray<Integer>.Create(10, 12, 14), 1,
'NUMERIC');
TMode.ALPHANUMERIC := TMode.Create(TArray<Integer>.Create(9, 11, 13), 2,
'ALPHANUMERIC');
TMode.STRUCTURED_APPEND := TMode.Create(TArray<Integer>.Create(0, 0, 0), 3,
'STRUCTURED_APPEND');
TMode.BYTE := TMode.Create(TArray<Integer>.Create(8, $10, $10), 4, 'BYTE');
TMode.ECI := TMode.Create(nil, 7, 'ECI');
TMode.KANJI := TMode.Create(TArray<Integer>.Create(8, 10, 12), 8, 'KANJI');
TMode.FNC1_FIRST_POSITION := TMode.Create(nil, 5, 'FNC1_FIRST_POSITION');
TMode.FNC1_SECOND_POSITION := TMode.Create(nil, 9, 'FNC1_SECOND_POSITION');
TMode.HANZI := TMode.Create(TArray<Integer>.Create(8, 10, 12), 13, 'HANZI')
end;
class procedure TMode.FinalizeClass;
begin
TMode.TERMINATOR.characterCountBitsForVersions := nil;
TMode.NUMERIC.characterCountBitsForVersions := nil;
TMode.ALPHANUMERIC.characterCountBitsForVersions := nil;
TMode.STRUCTURED_APPEND.characterCountBitsForVersions := nil;
TMode.BYTE.characterCountBitsForVersions := nil;
TMode.ECI.characterCountBitsForVersions := nil;
TMode.KANJI.characterCountBitsForVersions := nil;
TMode.FNC1_FIRST_POSITION.characterCountBitsForVersions := nil;
TMode.FNC1_SECOND_POSITION.characterCountBitsForVersions := nil;
TMode.HANZI.characterCountBitsForVersions := nil;
// SetLength(TMode.TERMINATOR.name, 0);
// SetLength(TMode.NUMERIC.name, 0);
// SetLength(TMode.ALPHANUMERIC.name, 0);
// SetLength(TMode.STRUCTURED_APPEND.name, 0);
// SetLength(TMode.BYTE.name, 0);
// SetLength(TMode.ECI.name, 0);
// SetLength(TMode.KANJI.name, 0);
// SetLength(TMode.FNC1_FIRST_POSITION.name, 0);
// SetLength(TMode.FNC1_SECOND_POSITION.name, 0);
// SetLength(TMode.HANZI.name, 0);
FreeAndNil(TMode.TERMINATOR);
FreeAndNil(TMode.NUMERIC);
FreeAndNil(TMode.ALPHANUMERIC);
FreeAndNil(TMode.STRUCTURED_APPEND);
FreeAndNil(TMode.BYTE);
FreeAndNil(TMode.ECI);
FreeAndNil(TMode.KANJI);
FreeAndNil(TMode.FNC1_FIRST_POSITION);
FreeAndNil(TMode.FNC1_SECOND_POSITION);
FreeAndNil(TMode.HANZI);
end;
class function TMode.forBits(const bits: Integer): TMode;
begin
case bits of
$0: Result := TMode.TERMINATOR;
$1: Result := TMode.NUMERIC;
$2: Result := TMode.ALPHANUMERIC;
$3: Result := TMode.STRUCTURED_APPEND;
$4: Result := TMode.BYTE;
$5: Result := TMode.FNC1_FIRST_POSITION;
$7: Result := TMode.ECI;
$8: Result := TMode.KANJI;
$9: Result := TMode.FNC1_SECOND_POSITION;
$D: Result := TMode.HANZI; // 0xD is defined in GBT 18284-2000, may not be supported in foreign country
else
raise EArgumentException.Create('Invalid arguments');
end;
end;
function TMode.getCharacterCountBits(const version: TVersion): Integer;
var
offset,
number : Integer;
begin
if (Self.characterCountBitsForVersions = nil)
then
raise EArgumentException.Create('Character count doesn''t apply to this mode');
number := version.VersionNumber;
if (number <= 9)
then
offset := 0
else
if (number <= 26)
then
offset := 1
else
offset := 2;
Result := Self.characterCountBitsForVersions[offset];
end;
function TMode.ToString: string;
begin
Result := Self.name;
end;
initialization
TMode.InitializeClass;
finalization
TMode.FinalizeClass;
end.
@@ -0,0 +1,77 @@
{
* Copyright 2013 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.
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.QRCodeDecoderMetaData;
interface
uses
ZXing.ResultPoint;
type
/// <summary>
/// Meta-data container for QR Code decoding. Instances of this class may be used to convey information back to the
/// decoding caller. Callers are expected to process this.
/// </summary>
TQRCodeDecoderMetaData = class sealed
private
Fmirrored: Boolean;
public
/// <summary>
/// Initializes a new instance of the <see cref="TQRCodeDecoderMetaData"/> class.
/// </summary>
/// <param name="mirrored">if set to <c>true</c> [mirrored].</param>
constructor Create(const mirrored: Boolean);
/// <summary>
/// Apply the result points' order correction due to mirroring.
/// </summary>
/// <param name="points">Array of points to apply mirror correction to.</param>
procedure applyMirroredCorrection(const points: TArray<IResultPoint>);
/// <summary>
/// true if the QR Code was mirrored.
/// </summary>
property IsMirrored: boolean read Fmirrored;
end;
implementation
{ TQRCodeDecoderMetaData }
constructor TQRCodeDecoderMetaData.Create(const mirrored: Boolean);
begin
Fmirrored := mirrored;
end;
procedure TQRCodeDecoderMetaData.applyMirroredCorrection(
const points: TArray<IResultPoint>);
var
bottomLeft: IResultPoint;
begin
if ((FMirrored and (points <> nil)) and (Length(points) >= 3)) then
begin
bottomLeft := points[0];
points[0] := points[2];
points[2] := bottomLeft;
// No need to 'fix' top-left and alignment pattern.
end
end;
end.
@@ -0,0 +1,681 @@
{
* Copyright 2007 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.
* Original Author: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.QrCode.Internal.Version;
interface
uses
System.SysUtils,
ZXing.Common.Bitmatrix,
ZXing.QrCode.Internal.ErrorCorrectionLevel,
ZXing.QrCode.Internal.FormatInformation,
ZXing.Common.Detector.MathUtils;
type
/// <summary>
/// See ISO 18004:2006 Annex D
/// </summary>
TVersion = class
type
TECB = class
public
count: Integer;
dataCodewords: Integer;
constructor Create(count: Integer; dataCodewords: Integer);
end;
TECBlocks = class sealed
private
ecBlocks: TArray<TECB>;
function getTotalECCodeWords: Integer;
function get_NumBlocks: Integer;
public
ecCodewordsPerBlock: Integer;
constructor Create(ecCodewordsPerBlock: Integer; ecBlocks: TArray<TECB>);
destructor Destroy; override;
function getECBlocks: TArray<TECB>;
{ property ecCodewordsPerBlock: Integer read get_ECCodewordsPerBlock;
property NumBlocks: Integer read get_NumBlocks;
property TotalECCodewords: Integer read get_TotalECCodewords;
}
property NumBlocks: Integer read get_NumBlocks;
property TotalECCodewords: Integer read getTotalECCodeWords;
end;
private
FalignmentPatternCenters: TArray<Integer>;
FecBlocks: TArray<TECBlocks>;
FTotalCodewords: Integer;
FversionNumber: Integer;
class var BuildVersions : TArray<TVersion>;
/// <summary> See ISO 18004:2006 Annex D.
/// Element i represents the raw version bits that specify version i + 7
/// </summary>
class var VERSION_DECODE_INFO: TArray<Integer>;
function CalcDimensionForVersion: Integer;
class function GetBuildVersions: TArray<TVersion>; static;
class procedure ClassInit; static;
class procedure ClassFinal; static;
public
constructor Create(const versionNumber: Integer;
const alignmentPatternCenters: TArray<Integer>;
const ecBlocks: TArray<TECBlocks>);
destructor Destroy; override;
class function decodeVersionInformation(versionBits: Integer)
: TVersion; static;
function buildFunctionPattern: TBitMatrix;
function getECBlocksForLevel(ecLevel: TErrorCorrectionLevel): TECBlocks;
class function getProvisionalVersionForDimension(
const dimension: Integer): TVersion; static;
class function getVersionForNumber(
const versionNumber: Integer): TVersion; static;
function ToString: String; override;
property DimensionForVersion: Integer read CalcDimensionForVersion;
property alignmentPatternCenters: TArray<Integer>
read FalignmentPatternCenters;
property TotalCodewords: Integer read FTotalCodewords;
property versionNumber: Integer read FversionNumber;
end;
implementation
{ TVersion.TECB }
constructor TVersion.TECB.Create(count, dataCodewords: Integer);
begin
self.count := count;
self.dataCodewords := dataCodewords;
end;
{ TVersion.TECBlocks }
constructor TVersion.TECBlocks.Create(ecCodewordsPerBlock: Integer;
ecBlocks: TArray<TECB>);
begin
self.ecBlocks := ecBlocks;
self.ecCodewordsPerBlock := ecCodewordsPerBlock;
end;
destructor TVersion.TECBlocks.Destroy;
begin
ecBlocks :=nil;
inherited;
end;
function TVersion.TECBlocks.getECBlocks: TArray<TECB>;
begin
result := self.ecBlocks;
end;
function TVersion.TECBlocks.getTotalECCodeWords: Integer;
begin
result := ecCodewordsPerBlock * NumBlocks;
end;
function TVersion.TECBlocks.get_NumBlocks: Integer;
var
total: Integer;
ecBlock: TECB;
begin
total := 0;
for ecBlock in ecBlocks do
begin
inc(total, ecBlock.count);
end;
result := total;
end;
{ TVersion }
constructor TVersion.Create(const versionNumber: Integer;
const alignmentPatternCenters: TArray<Integer>;
const ecBlocks: TArray<TECBlocks>);
var
ecBlock: TECB;
total, ecCodewords: Integer;
ecbArray: TArray<TECB>;
begin
FversionNumber := versionNumber;
FalignmentPatternCenters := alignmentPatternCenters;
FecBlocks := ecBlocks;
total := 0;
ecCodewords := ecBlocks[0].ecCodewordsPerBlock;
ecbArray := ecBlocks[0].getECBlocks;
for ecBlock in ecbArray do
Inc(total, (ecBlock.count * (ecBlock.dataCodewords + ecCodewords)));
FTotalCodewords := total;
ecbArray := nil;
end;
destructor TVersion.Destroy;
var
ecBlocks : TECBlocks;
ECB : TECB;
begin
FalignmentPatternCenters := nil;
for ecBlocks in FecBlocks do
begin
for ecb in ecBlocks.ecBlocks do
begin
ECB.Free;
//ECB := nil;
end;
ecBlocks.free;
end;
FecBlocks := nil;
inherited;
end;
function TVersion.buildFunctionPattern: TBitMatrix;
var
dimension, max, x, i, y: Integer;
begin
dimension := DimensionForVersion;
result := TBitMatrix.Create(dimension);
result.setRegion(0, 0, 9, 9);
result.setRegion((dimension - 8), 0, 8, 9);
result.setRegion(0, (dimension - 8), 9, 8);
max := Length(FalignmentPatternCenters);
x := 0;
while ((x < max)) do
begin
i := (FalignmentPatternCenters[x] - 2);
y := 0;
while ((y < max)) do
begin
if (((x <> 0) or ((y <> 0) and (y <> (max - 1)))) and
((x <> (max - 1)) or (y <> 0))) then
result.setRegion((self.alignmentPatternCenters[y] - 2), i, 5, 5);
inc(y)
end;
inc(x)
end;
result.setRegion(6, 9, 1, (dimension - $11));
result.setRegion(9, 6, (dimension - $11), 1);
if (self.versionNumber > 6) then
begin
result.setRegion((dimension - 11), 0, 3, 6);
result.setRegion(0, (dimension - 11), 6, 3)
end;
end;
class function TVersion.GetBuildVersions: TArray<TVersion>;
begin
Result := TArray<TVersion>.Create(
TVersion.Create(1, TArray<Integer>.Create(),
TArray<TECBlocks>.Create(TECBlocks.Create(7,
TArray<TECB>.Create(TECB.Create(1, 19))), TECBlocks.Create(10,
TArray<TECB>.Create(TECB.Create(1, 16))), TECBlocks.Create(13,
TArray<TECB>.Create(TECB.Create(1, 13))), TECBlocks.Create(17,
TArray<TECB>.Create(TECB.Create(1, 9))))),
TVersion.Create(2, TArray<Integer>.Create(6, 18),
TArray<TECBlocks>.Create(TECBlocks.Create(10,
TArray<TECB>.Create(TECB.Create(1, 34))), TECBlocks.Create(16,
TArray<TECB>.Create(TECB.Create(1, 28))), TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(1, 22))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(1, 16))))),
TVersion.Create(3, TArray<Integer>.Create(6, 22),
TArray<TECBlocks>.Create(TECBlocks.Create(15,
TArray<TECB>.Create(TECB.Create(1, 55))), TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(1, 44))), TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 17))), TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(2, 13))))),
TVersion.Create(4, TArray<Integer>.Create(6, 26),
TArray<TECBlocks>.Create(TECBlocks.Create(20,
TArray<TECB>.Create(TECB.Create(1, 80))), TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 32))), TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(2, 24))), TECBlocks.Create(16,
TArray<TECB>.Create(TECB.Create(4, 9))))),
TVersion.Create(5, TArray<Integer>.Create(6, 30),
TArray<TECBlocks>.Create(TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(1, 108))), TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(2, 43))), TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 15), TECB.Create(2, 16))),
TECBlocks.Create(22, TArray<TECB>.Create(TECB.Create(2, 11),
TECB.Create(2, 12))))),
TVersion.Create(6, TArray<Integer>.Create(6, 34),
TArray<TECBlocks>.Create(TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 68))), TECBlocks.Create(16,
TArray<TECB>.Create(TECB.Create(4, 27))), TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(4, 19))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(4, 15))))),
TVersion.Create(7, TArray<Integer>.Create(6, 22, 38),
TArray<TECBlocks>.Create(TECBlocks.Create(20,
TArray<TECB>.Create(TECB.Create(2, 78))), TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(4, 31))), TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 14), TECB.Create(4, 15))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(4, 13),
TECB.Create(1, 14))))),
TVersion.Create(8, TArray<Integer>.Create(6, 24, 42),
TArray<TECBlocks>.Create(TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(2, 97))), TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(2, 38), TECB.Create(2, 39))),
TECBlocks.Create(22, TArray<TECB>.Create(TECB.Create(4, 18), TECB.Create(2,
19))), TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(4, 14),
TECB.Create(2, 15))))),
TVersion.Create(9, TArray<Integer>.Create(6, 26, 46),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(2, 116))), TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(3, 36), TECB.Create(2, 37))),
TECBlocks.Create(20, TArray<TECB>.Create(TECB.Create(4, 16), TECB.Create(4,
17))), TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(4, 12),
TECB.Create(4, 13))))),
TVersion.Create(10, TArray<Integer>.Create(6, 28, 50),
TArray<TECBlocks>.Create(TECBlocks.Create(18,
TArray<TECB>.Create(TECB.Create(2, 68), TECB.Create(2, 69))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(4, 43), TECB.Create(1,
44))), TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(6, 19),
TECB.Create(2, 20))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(6, 15), TECB.Create(2, 16))))),
TVersion.Create(11, TArray<Integer>.Create(6, 30, 54),
TArray<TECBlocks>.Create(TECBlocks.Create(20,
TArray<TECB>.Create(TECB.Create(4, 81))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(1, 50), TECB.Create(4, 51))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(4, 22), TECB.Create(4,
23))), TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(3, 12),
TECB.Create(8, 13))))),
TVersion.Create(12, TArray<Integer>.Create(6, 32, 58),
TArray<TECBlocks>.Create(TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(2, 92), TECB.Create(2, 93))),
TECBlocks.Create(22, TArray<TECB>.Create(TECB.Create(6, 36), TECB.Create(2,
37))), TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(4, 20),
TECB.Create(6, 21))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(7, 14), TECB.Create(4, 15))))),
TVersion.Create(13, TArray<Integer>.Create(6, 34, 62),
TArray<TECBlocks>.Create(TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(4, 107))), TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(8, 37), TECB.Create(1, 38))),
TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(8, 20), TECB.Create(4,
21))), TECBlocks.Create(22, TArray<TECB>.Create(TECB.Create(12, 11),
TECB.Create(4, 12))))),
TVersion.Create(14, TArray<Integer>.Create(6, 26, 46, 66),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(3, 115), TECB.Create(1, 116))),
TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(4, 40), TECB.Create(5,
41))), TECBlocks.Create(20, TArray<TECB>.Create(TECB.Create(11, 16),
TECB.Create(5, 17))), TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(11, 12), TECB.Create(5, 13))))),
TVersion.Create(15, TArray<Integer>.Create(6, 26, 48, 70),
TArray<TECBlocks>.Create(TECBlocks.Create(22,
TArray<TECB>.Create(TECB.Create(5, 87), TECB.Create(1, 88))),
TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(5, 41), TECB.Create(5,
42))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(5, 24),
TECB.Create(7, 25))), TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(11, 12), TECB.Create(7, 13))))),
TVersion.Create(16, TArray<Integer>.Create(6, 26, 50, 74),
TArray<TECBlocks>.Create(TECBlocks.Create(24,
TArray<TECB>.Create(TECB.Create(5, 98), TECB.Create(1, 99))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(7, 45), TECB.Create(3,
46))), TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(15, 19),
TECB.Create(2, 20))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(3, 15), TECB.Create(13, 16))))),
TVersion.Create(17, TArray<Integer>.Create(6, 30, 54, 78),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(1, 107), TECB.Create(5, 108))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(10, 46), TECB.Create(1,
47))), TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(1, 22),
TECB.Create(15, 23))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(2, 14), TECB.Create(17, 15))))),
TVersion.Create(18, TArray<Integer>.Create(6, 30, 56, 82),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(5, 120), TECB.Create(1, 121))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(9, 43), TECB.Create(4,
44))), TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(17, 22),
TECB.Create(1, 23))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(2, 14), TECB.Create(19, 15))))),
TVersion.Create(19, TArray<Integer>.Create(6, 30, 58, 86),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(3, 113), TECB.Create(4, 114))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(3, 44), TECB.Create(11,
45))), TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(17, 21),
TECB.Create(4, 22))), TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(9, 13), TECB.Create(16, 14))))),
TVersion.Create(20, TArray<Integer>.Create(6, 34, 62, 90),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(3, 107), TECB.Create(5, 108))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(3, 41), TECB.Create(13,
42))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(15, 24),
TECB.Create(5, 25))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(15, 15), TECB.Create(10, 16))))),
TVersion.Create(21, TArray<Integer>.Create(6, 28, 50, 72, 94),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(4, 116), TECB.Create(4, 117))),
TECBlocks.Create(26, TArray<TECB>.Create(TECB.Create(17, 42))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(17, 22), TECB.Create(6,
23))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(19, 16),
TECB.Create(6, 17))))),
TVersion.Create(22, TArray<Integer>.Create(6, 26, 50, 74, 98),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(2, 111), TECB.Create(7, 112))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(17, 46))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(7, 24), TECB.Create(16,
25))), TECBlocks.Create(24, TArray<TECB>.Create(TECB.Create(34, 13))))),
TVersion.Create(23, TArray<Integer>.Create(6, 30, 54, 78, 102),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(4, 121), TECB.Create(5, 122))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(4, 47), TECB.Create(14,
48))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(11, 24),
TECB.Create(14, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(16, 15), TECB.Create(14, 16))))),
TVersion.Create(24, TArray<Integer>.Create(6, 28, 54, 80, 106),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(6, 117), TECB.Create(4, 118))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(6, 45), TECB.Create(14,
46))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(11, 24),
TECB.Create(16, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(30, 16), TECB.Create(2, 17))))),
TVersion.Create(25, TArray<Integer>.Create(6, 32, 58, 84, 110),
TArray<TECBlocks>.Create(TECBlocks.Create(26,
TArray<TECB>.Create(TECB.Create(8, 106), TECB.Create(4, 107))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(8, 47), TECB.Create(13,
48))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(7, 24),
TECB.Create(22, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(22, 15), TECB.Create(13, 16))))),
TVersion.Create(26, TArray<Integer>.Create(6, 30, 58, 86, 114),
TArray<TECBlocks>.Create(TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(10, 114), TECB.Create(2, 115))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(19, 46), TECB.Create(4,
47))), TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(28, 22),
TECB.Create(6, 23))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(33, 16), TECB.Create(4, 17))))),
TVersion.Create(27, TArray<Integer>.Create(6, 34, 62, 90, 118),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(8, 122), TECB.Create(4, 123))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(22, 45), TECB.Create(3,
46))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(8, 23),
TECB.Create(26, 24))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(12, 15), TECB.Create(28, 16))))),
TVersion.Create(28, TArray<Integer>.Create(6, 26, 50, 74, 98, 122),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(3, 117), TECB.Create(10, 118))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(3, 45), TECB.Create(23,
46))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(4, 24),
TECB.Create(31, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(11, 15), TECB.Create(31, 16))))),
TVersion.Create(29, TArray<Integer>.Create(6, 30, 54, 78, 102, 126),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(7, 116), TECB.Create(7, 117))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(21, 45), TECB.Create(7,
46))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(1, 23),
TECB.Create(37, 24))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(19, 15), TECB.Create(26, 16))))),
TVersion.Create(30, TArray<Integer>.Create(6, 26, 52, 78, 104, 130),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(5, 115), TECB.Create(10, 116))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(19, 47),
TECB.Create(10, 48))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(15, 24), TECB.Create(25, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(23, 15),
TECB.Create(25, 16))))),
TVersion.Create(31, TArray<Integer>.Create(6, 30, 56, 82, 108, 134),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(13, 115), TECB.Create(3, 116))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(2, 46), TECB.Create(29,
47))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(42, 24),
TECB.Create(1, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(23, 15), TECB.Create(28, 16))))),
TVersion.Create(32, TArray<Integer>.Create(6, 34, 60, 86, 112, 138),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(17, 115))), TECBlocks.Create(28,
TArray<TECB>.Create(TECB.Create(10, 46), TECB.Create(23, 47))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(10, 24),
TECB.Create(35, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(19, 15), TECB.Create(35, 16))))),
TVersion.Create(33, TArray<Integer>.Create(6, 30, 58, 86, 114, 142),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(17, 115), TECB.Create(1, 116))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(14, 46),
TECB.Create(21, 47))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(29, 24), TECB.Create(19, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(11, 15),
TECB.Create(46, 16))))),
TVersion.Create(34, TArray<Integer>.Create(6, 34, 62, 90, 118, 146),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(13, 115), TECB.Create(6, 116))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(14, 46),
TECB.Create(23, 47))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(44, 24), TECB.Create(7, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(59, 16),
TECB.Create(1, 17))))),
TVersion.Create(35, TArray<Integer>.Create(6, 30, 54, 78, 102, 126, 150),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(12, 121), TECB.Create(7, 122))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(12, 47),
TECB.Create(26, 48))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(39, 24), TECB.Create(14, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(22, 15),
TECB.Create(41, 16))))),
TVersion.Create(36, TArray<Integer>.Create(6, 24, 50, 76, 102, 128, 154),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(6, 121), TECB.Create(14, 122))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(6, 47), TECB.Create(34,
48))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(46, 24),
TECB.Create(10, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(2, 15), TECB.Create(64, 16))))),
TVersion.Create(37, TArray<Integer>.Create(6, 28, 54, 80, 106, 132, 158),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(17, 122), TECB.Create(4, 123))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(29, 46),
TECB.Create(14, 47))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(49, 24), TECB.Create(10, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(24, 15),
TECB.Create(46, 16))))),
TVersion.Create(38, TArray<Integer>.Create(6, 32, 58, 84, 110, 136, 162),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(4, 122), TECB.Create(18, 123))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(13, 46),
TECB.Create(32, 47))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(48, 24), TECB.Create(14, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(42, 15),
TECB.Create(32, 16))))),
TVersion.Create(39, TArray<Integer>.Create(6, 26, 54, 82, 110, 138, 166),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(20, 117), TECB.Create(4, 118))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(40, 47), TECB.Create(7,
48))), TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(43, 24),
TECB.Create(22, 25))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(10, 15), TECB.Create(67, 16))))),
TVersion.Create(40, TArray<Integer>.Create(6, 30, 58, 86, 114, 142, 170),
TArray<TECBlocks>.Create(TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(19, 118), TECB.Create(6, 119))),
TECBlocks.Create(28, TArray<TECB>.Create(TECB.Create(18, 47),
TECB.Create(31, 48))), TECBlocks.Create(30,
TArray<TECB>.Create(TECB.Create(34, 24), TECB.Create(34, 25))),
TECBlocks.Create(30, TArray<TECB>.Create(TECB.Create(20, 15),
TECB.Create(61, 16)))))
);
end;
function TVersion.CalcDimensionForVersion: Integer;
begin
result := 17 + 4 * versionNumber;
end;
class function TVersion.decodeVersionInformation(versionBits: Integer)
: TVersion;
var
bestDifference, bestversion, i, targetVersion, bitsDifference: Integer;
begin
bestDifference := $7FFFFFFF;
bestversion := 0;
i := 0;
while ((i < Length(TVersion.VERSION_DECODE_INFO))) do
begin
targetVersion := TVersion.VERSION_DECODE_INFO[i];
if (targetVersion = versionBits) then
begin
result := TVersion.getVersionForNumber((i + 7));
exit
end;
bitsDifference := TFormatInformation.numBitsDiffering(versionBits,
targetVersion);
if (bitsDifference < bestDifference) then
begin
bestversion := (i + 7);
bestDifference := bitsDifference
end;
inc(i)
end;
if (bestDifference <= 3) then
begin
result := TVersion.getVersionForNumber(bestversion);
exit
end;
result := nil;
end;
function TVersion.getECBlocksForLevel(ecLevel: TErrorCorrectionLevel)
: TECBlocks;
begin
result := FecBlocks[ecLevel.ordinal]
end;
class function TVersion.getProvisionalVersionForDimension(
const dimension: Integer): TVersion;
begin
Result := nil;
if ((dimension mod 4) <> 1)
then
exit;
try
Result := TVersion.getVersionForNumber(TMathUtils.Asr(dimension - $11, 2));
except
on E: EArgumentException do
Result := nil;
end
end;
class function TVersion.getVersionForNumber(
const versionNumber: Integer): TVersion;
begin
if ((versionNumber < 1) or (versionNumber > 40)) then
raise EArgumentException.Create
('version number needs to be between 1 and 40');
Result := TVersion.BuildVersions[(versionNumber - 1)];
end;
function TVersion.ToString: string;
begin
result := self.versionNumber.ToString();
end;
class procedure TVersion.ClassInit();
begin
TVersion.VERSION_DECODE_INFO := TArray<Integer>.Create($7C94, $85BC, $9A99,
$A4D3, $BBF6, $C762, $D847, $E60D, $F928, $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);
TVersion.BuildVersions := TVersion.GetBuildVersions;
end;
class procedure TVersion.ClassFinal();
var version :Tversion;
begin
TVersion.VERSION_DECODE_INFO := nil;
for version in TVersion.BuildVersions do
begin
version.Free;
end;
TVersion.BuildVersions :=nil;
end;
Initialization
TVersion.ClassInit();
Finalization
TVersion.ClassFinal();
end.