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
+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.
|
||||
Reference in New Issue
Block a user