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:
Vendored
+564
@@ -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.
|
||||
Vendored
+179
@@ -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.
|
||||
+903
@@ -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.
|
||||
+229
@@ -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.
|
||||
+290
@@ -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.
|
||||
Vendored
+361
@@ -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.
|
||||
+187
@@ -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.
|
||||
+226
@@ -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.
|
||||
android-client/ThirdParty/ZXing/2D Barcodes/Decoder/ZXing.QrCode.Internal.DecodedBitStreamParser.pas
Vendored
+587
@@ -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.
|
||||
+278
@@ -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.
|
||||
Vendored
+149
@@ -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.
|
||||
Vendored
+256
@@ -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.
|
||||
+248
@@ -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.
|
||||
Vendored
+77
@@ -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.
|
||||
+681
@@ -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.
|
||||
Reference in New Issue
Block a user