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

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

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-17 18:38:41 +01:00
parent 780eecc11b
commit dd9fd62dcb
81 changed files with 22141 additions and 19 deletions
@@ -0,0 +1,83 @@
unit ZXing.Common.Detector.MathUtils;
{
* 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.
* Implemented by E. Spelt for Delphi
* Restructured by K. Gossens
}
interface
uses
System.SysUtils;
type
TMathUtils = class sealed
public
class function distance(const aX, aY, bX, bY: Double): Single; static;
class function round(d: Single): Integer; static;
// class function Asr(x, y: integer): integer; overload; static;
class function Asr(Value: Int64; ShiftBits: integer): Int64; static;
end;
implementation
{ TMathUtils }
class function TMathUtils.distance(const aX, aY, bX, bY: Double): Single;
var
xDiff, yDiff: double;
begin
xDiff := aX - bX;
yDiff := aY - bY;
result := Sqrt(xDiff * xDiff + yDiff * yDiff);
end;
class function TMathUtils.round(d: Single): Integer;
begin
if (Single.IsNaN(d)) then
begin
Result := 0;
exit
end;
if (Single.IsPositiveInfinity(d)) then
begin
Result := $7fffffff;
exit;
end;
if (d < 0)
then
Result := Trunc(d + (-0.5))
else
Result := Trunc(d + 0.5);
end;
{
class function TMathUtils.Asr(x, y: integer): integer;
begin
result := x div (1 shl y);
end;
}
class function TMathUtils.Asr(Value: Int64; ShiftBits: integer): Int64;
begin
result := Value shr ShiftBits;
if (Value and $8000000000000000) > 0 then
result := result or ($FFFFFFFFFFFFFFFF shl (64 - ShiftBits));
end;
end.
@@ -0,0 +1,398 @@
{
* 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.
* Implemented by E. Spelt for Delphi
* Restructured by K. Gossens
}
unit ZXing.Common.Detector.WhiteRectangleDetector;
interface
uses
System.SysUtils,
ZXing.Common.BitMatrix,
ZXing.Common.Detector.MathUtils,
ZXing.ResultPoint;
type
TWhiteRectangleDetector = class sealed
private const
CORR: Integer = 1;
INIT_SIZE: Integer = 10;
var
downInit: Integer;
height: Integer;
image: TBitMatrix;
leftInit: Integer;
rightInit: Integer;
upInit: Integer;
width: Integer;
constructor Create(image: TBitMatrix); overload;
constructor Create(image: TBitMatrix; initSize: Integer; x: Integer;
y: Integer); overload;
function centerEdges(y: IResultPoint; z: IResultPoint; x: IResultPoint;
t: IResultPoint): TArray<IResultPoint>;
function containsBlackPoint(a: Integer; b: Integer; fixed: Integer;
horizontal: boolean): boolean;
function getBlackPointOnSegment(aX: Single; aY: Single; bX: Single;
bY: Single): IResultPoint;
public
class function New(image: TBitMatrix): TWhiteRectangleDetector; overload;
class function New(image: TBitMatrix; initSize: Integer; x: Integer;
y: Integer): TWhiteRectangleDetector; overload; static;
function detect(): TArray<IResultPoint>;
end;
implementation
{ TWhiteRectangleDetector }
constructor TWhiteRectangleDetector.Create(image: TBitMatrix);
begin
Self.Create(image, 10, image.width div 2, image.height div 2);
end;
constructor TWhiteRectangleDetector.Create(image: TBitMatrix; initSize: Integer;
x: Integer; y: Integer);
var
halfsize: Integer;
begin
Self.image := image;
Self.height := image.height;
Self.width := image.width;
halfsize := (initSize div 2);
Self.leftInit := (x - halfsize);
Self.rightInit := (x + halfsize);
Self.upInit := (y - halfsize);
Self.downInit := (y + halfsize);
end;
function TWhiteRectangleDetector.centerEdges(y: IResultPoint; z: IResultPoint;
x: IResultPoint; t: IResultPoint): TArray<IResultPoint>;
var
yi, yj, zi, zj, xi, xj, ti, tj: Single;
begin
yi := y.x;
yj := y.y;
zi := z.x;
zj := z.y;
xi := x.x;
xj := x.y;
ti := t.x;
tj := t.y;
if (yi < (Self.width div 2)) then
begin
Result := TArray<IResultPoint>.Create(TResultPointHelpers.CreateResultPoint((ti - 1), (tj + 1)
), TResultPointHelpers.CreateResultPoint((zi + 1), (zj + 1)), TResultPointHelpers.CreateResultPoint((xi - 1),
(xj - 1)), TResultPointHelpers.CreateResultPoint((yi + 1), (yj - 1)));
exit;
end;
Result := TArray<IResultPoint>.Create(TResultPointHelpers.CreateResultPoint((ti + 1), (tj + 1)),
TResultPointHelpers.CreateResultPoint((zi + 1), (zj - 1)), TResultPointHelpers.CreateResultPoint((xi - 1),
(xj + 1)), TResultPointHelpers.CreateResultPoint((yi - 1), (yj - 1)));
end;
function TWhiteRectangleDetector.containsBlackPoint(a: Integer; b: Integer;
fixed: Integer; horizontal: boolean): boolean;
var
x, y: Integer;
begin
if (horizontal) then
begin
x := a;
while ((x <= b)) do
begin
if (Self.image[x, fixed]) then
begin
Result := true;
exit;
end;
Inc(x)
end;
end
else
begin
y := a;
while ((y <= b)) do
begin
if (Self.image[fixed, y]) then
begin
Result := true;
exit;
end;
Inc(y);
end;
end;
Result := false;
end;
class function TWhiteRectangleDetector.New(image: TBitMatrix)
: TWhiteRectangleDetector;
var
instance: TWhiteRectangleDetector;
begin
Result := nil;
if (image <> nil) then
begin
instance := TWhiteRectangleDetector.Create(image);
if (((instance.upInit >= 0) and (instance.leftInit >= 0)) and
((instance.downInit < instance.height) and
(instance.rightInit < instance.width))) then
Result := instance
else
instance.Free;
end;
end;
class function TWhiteRectangleDetector.New(image: TBitMatrix; initSize: Integer;
x: Integer; y: Integer): TWhiteRectangleDetector;
var
instance: TWhiteRectangleDetector;
begin
instance := TWhiteRectangleDetector.Create(image, initSize, x, y);
if (((instance.upInit >= 0) and (instance.leftInit >= 0)) and
((instance.downInit < instance.height) and
(instance.rightInit < instance.width))) then
Result := instance
else
begin
instance.Free;
Result := nil;
end;
end;
function TWhiteRectangleDetector.detect: TArray<IResultPoint>;
var
left, right, up, down: Integer;
sizeExceeded, aBlackPointFoundOnBorder, atLeastOneBlackPointFoundOnBorder,
atLeastOneBlackPointFoundOnRight, atLeastOneBlackPointFoundOnBottom,
atLeastOneBlackPointFoundOnLeft, atLeastOneBlackPointFoundOnTop,
rightBorderNotWhite, bottomBorderNotWhite, leftBorderNotWhite,
topBorderNotWhite: boolean;
i: Integer;
z, t, x, y: IResultPoint;
maxSize: Integer;
begin
z := nil;
t := nil;
x := nil;
y := nil;
left := Self.leftInit;
right := Self.rightInit;
up := Self.upInit;
down := Self.downInit;
sizeExceeded := false;
aBlackPointFoundOnBorder := true;
atLeastOneBlackPointFoundOnBorder := false;
atLeastOneBlackPointFoundOnRight := false;
atLeastOneBlackPointFoundOnBottom := false;
atLeastOneBlackPointFoundOnLeft := false;
atLeastOneBlackPointFoundOnTop := false;
while (aBlackPointFoundOnBorder) do
begin
aBlackPointFoundOnBorder := false;
rightBorderNotWhite := true;
while ((rightBorderNotWhite or (not atLeastOneBlackPointFoundOnRight)) and
(right < Self.width)) do
begin
rightBorderNotWhite := Self.containsBlackPoint(up, down, right, false);
if (rightBorderNotWhite) then
begin
Inc(right);
aBlackPointFoundOnBorder := true;
atLeastOneBlackPointFoundOnRight := true;
end
else if (not atLeastOneBlackPointFoundOnRight) then
Inc(right);
end;
if (right >= Self.width) then
begin
sizeExceeded := true;
break;
end;
bottomBorderNotWhite := true;
while ((bottomBorderNotWhite or (not atLeastOneBlackPointFoundOnBottom)) and
(down < Self.height)) do
begin
bottomBorderNotWhite := Self.containsBlackPoint(left, right, down, true);
if (bottomBorderNotWhite) then
begin
Inc(down);
aBlackPointFoundOnBorder := true;
atLeastOneBlackPointFoundOnBottom := true;
end
else if (not atLeastOneBlackPointFoundOnBottom) then
Inc(down);
end;
if (down >= Self.height) then
begin
sizeExceeded := true;
break;
end;
leftBorderNotWhite := true;
while ((leftBorderNotWhite or (not atLeastOneBlackPointFoundOnLeft)) and
(left >= 0)) do
begin
leftBorderNotWhite := Self.containsBlackPoint(up, down, left, false);
if (leftBorderNotWhite) then
begin
Dec(left);
aBlackPointFoundOnBorder := true;
atLeastOneBlackPointFoundOnLeft := true;
end
else if (not atLeastOneBlackPointFoundOnLeft) then
Dec(left);
end;
if (left < 0) then
begin
sizeExceeded := true;
break;
end;
topBorderNotWhite := true;
while ((topBorderNotWhite or (not atLeastOneBlackPointFoundOnTop)) and
(up >= 0)) do
begin
topBorderNotWhite := Self.containsBlackPoint(left, right, up, true);
if (topBorderNotWhite) then
begin
Dec(up);
aBlackPointFoundOnBorder := true;
atLeastOneBlackPointFoundOnTop := true;
end
else if (not atLeastOneBlackPointFoundOnTop) then
Dec(up);
end;
if (up < 0) then
begin
sizeExceeded := true;
break;
end;
if (aBlackPointFoundOnBorder) then
atLeastOneBlackPointFoundOnBorder := true;
end;
if (not(not sizeExceeded and atLeastOneBlackPointFoundOnBorder)) then
begin
Result := nil;
exit;
end;
maxSize := (right - left);
i := 1;
while ((i < maxSize)) do
begin
z := Self.getBlackPointOnSegment(left, (down - i), (left + i), down);
if (z <> nil) then
break;
Inc(i);
end;
if (z = nil) then
begin
Result := nil;
exit;
end;
i := 1;
while ((i < maxSize)) do
begin
t := Self.getBlackPointOnSegment(left, (up + i), (left + i), up);
if (t <> nil) then
break;
Inc(i);
end;
if (t = nil) then
begin
Result := nil;
exit;
end;
i := 1;
while ((i < maxSize)) do
begin
x := Self.getBlackPointOnSegment(right, (up + i), (right - i), up);
if (x <> nil) then
break;
Inc(i);
end;
if (x = nil) then
begin
Result := nil;
exit;
end;
y := nil;
i := 1;
while ((i < maxSize)) do
begin
y := Self.getBlackPointOnSegment(right, (down - i), (right - i), down);
if (y <> nil) then
break;
Inc(i);
end;
if (y = nil) then
begin
Result := nil;
exit;
end;
Result := Self.centerEdges(y, z, x, t);
end;
function TWhiteRectangleDetector.getBlackPointOnSegment(aX: Single; aY: Single;
bX: Single; bY: Single): IResultPoint;
var
i, x, y, dist: Integer;
xStep, yStep: Single;
begin
dist := TMathUtils.round(TMathUtils.distance(aX, aY, bX, bY));
xStep := ((bX - aX) / dist);
yStep := ((bY - aY) / dist);
i := 0;
while ((i < dist)) do
begin
x := TMathUtils.round((aX + (i * xStep)));
y := TMathUtils.round((aY + (i * yStep)));
if (Self.image[x, y]) then
begin
Result := TResultPointHelpers.CreateResultPoint(x, y);
exit;
end;
Inc(i);
end;
Result := nil;
end;
end.
@@ -0,0 +1,693 @@
unit ZXing.Common.ReedSolomon.GenericGF;
{
* 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
}
// ES: two classes in one unit. This is to avoid circulair reference.
interface
uses SysUtils;
type
TGenericGF = class;
IGenericGFPoly = interface
['{9E56C626-16C5-4A5A-99B5-84559780EB9D}']
function get_Degree: Integer;
function get_isZero: boolean;
function get_field: TGenericGF;
function get_coefficients: TArray<Integer>;
property coefficients: TArray<Integer> read get_coefficients;
property degree: Integer read get_Degree;
property isZero: boolean read get_isZero;
property Field: TGenericGF read get_field;
function addOrSubtract(other: IGenericGFPoly): IGenericGFPoly;
function divide(other: IGenericGFPoly): TArray<IGenericGFPoly>;
function evaluateAt(a: Integer): Integer;
function getCoefficient(degree: Integer): Integer;
function multiply(scalar: Integer): IGenericGFPoly; overload;
function multiply(other: IGenericGFPoly): IGenericGFPoly; overload;
function multiplyByMonomial(degree: Integer; coefficient: Integer)
: IGenericGFPoly;
function ToString: string;
end;
/// <summary>
/// <p>This class contains utility methods for performing mathematical operations over
/// the Galois Fields. Operations use a given primitive polynomial in calculations.</p>
/// <p>Throughout this package, elements of the GF are represented as an {@code int}
/// for convenience and speed (but at the cost of memory).
/// </p>
/// </summary>
TGenericGF = class sealed
private
FexpTable: TArray<Integer>;
FGeneratorBase: Integer;
FlogTable: TArray<Integer>;
FZero: IGenericGFPoly;
FOne: IGenericGFPoly;
Fprimitive: Integer;
Fsize: Integer;
function get_GeneratorBase: Integer;
function get_One: IGenericGFPoly;
function get_Zero: IGenericGFPoly;
function get_Size: Integer;
class function get_AZTEC_DATA_10: TGenericGF; static;
class function get_AZTEC_DATA_12: TGenericGF; static;
class function get_AZTEC_DATA_6: TGenericGF; static;
class function get_AZTEC_PARAM: TGenericGF; static;
class function get_QR_CODE_FIELD_256: TGenericGF; static;
class function get_DATA_MATRIX_FIELD_256: TGenericGF; static;
class procedure InitializeClass(); static;
class procedure FinalizeClass(); static;
public
class var AZTEC_DATA_10: TGenericGF;
class var AZTEC_DATA_12: TGenericGF;
class var AZTEC_DATA_6: TGenericGF;
class var AZTEC_PARAM: TGenericGF;
class var DATA_MATRIX_FIELD_256: TGenericGF;
class var QR_CODE_FIELD_256: TGenericGF;
class var MAXICODE_FIELD_64: TGenericGF;
class var AZTEC_DATA_8: TGenericGF;
constructor Create(primitive, size, genBase: Integer);
destructor Destroy(); override;
function ToString: string; override;
class function addOrSubtract(a: Integer; b: Integer): Integer; static;
function multiply(a: Integer; b: Integer): Integer;
function buildMonomial(degree: Integer; coefficient: Integer)
: IGenericGFPoly;
function log(a: Integer): Integer;
function exp(a: Integer): Integer;
function inverse(a: Integer): Integer;
property GeneratorBase: Integer read get_GeneratorBase;
property size: Integer read get_Size;
property One: IGenericGFPoly read get_One;
property Zero: IGenericGFPoly read get_Zero;
end;
TGenericGFPoly = class sealed(TInterfacedObject, IGenericGFPoly)
private
Fcoefficients: TArray<Integer>;
Ffield: TGenericGF;
public
function get_Degree: Integer;
function get_isZero: boolean;
function get_field: TGenericGF;
function get_coefficients: TArray<Integer>;
property coefficients: TArray<Integer> read get_coefficients;
property degree: Integer read get_Degree;
property isZero: boolean read get_isZero;
property Field: TGenericGF read get_field;
constructor Create(Field: TGenericGF; coefficients: TArray<Integer>);
destructor Destroy(); override;
function addOrSubtract(other: IGenericGFPoly): IGenericGFPoly;
function divide(other: IGenericGFPoly): TArray<IGenericGFPoly>;
function evaluateAt(a: Integer): Integer;
function getCoefficient(degree: Integer): Integer;
function multiply(scalar: Integer): IGenericGFPoly; overload;
function multiply(other: IGenericGFPoly): IGenericGFPoly; overload;
function multiplyByMonomial(degree: Integer; coefficient: Integer)
: IGenericGFPoly;
function ToString: string; override;
end;
implementation
{ TGenericGF }
class procedure TGenericGF.InitializeClass();
begin
AZTEC_DATA_10 := get_AZTEC_DATA_10();
AZTEC_DATA_12 := get_AZTEC_DATA_12;
AZTEC_DATA_6 := get_AZTEC_DATA_6;
AZTEC_PARAM := get_AZTEC_PARAM;
DATA_MATRIX_FIELD_256 := get_DATA_MATRIX_FIELD_256;
QR_CODE_FIELD_256 := get_QR_CODE_FIELD_256;
MAXICODE_FIELD_64 := get_AZTEC_DATA_6;
AZTEC_DATA_8 := get_DATA_MATRIX_FIELD_256;
end;
class procedure TGenericGF.FinalizeClass;
begin
FreeAndNil(AZTEC_DATA_10);
FreeAndNil(AZTEC_DATA_12);
FreeAndNil(AZTEC_DATA_6);
FreeAndNil(AZTEC_PARAM);
FreeAndNil(DATA_MATRIX_FIELD_256);
FreeAndNil(QR_CODE_FIELD_256);
FreeAndNil(MAXICODE_FIELD_64);
FreeAndNil(AZTEC_DATA_8);
end;
constructor TGenericGF.Create(primitive, size, genBase: Integer);
var
x, i: Integer;
begin
self.Fprimitive := primitive;
Fsize := size;
FGeneratorBase := genBase;
self.FexpTable := TArray<Integer>.Create();
self.FlogTable := TArray<Integer>.Create();
SetLength(self.FexpTable, size);
SetLength(self.FlogTable, size);
x := 1;
i := 0;
while ((i < size)) do
begin
self.FexpTable[i] := x;
x := (x shl 1);
if (x >= size) then
begin
x := (x xor primitive);
x := (x and (size - 1))
end;
inc(i)
end;
i := 0;
while ((i < (size - 1))) do
begin
self.FlogTable[self.FexpTable[i]] := i;
inc(i)
end;
FZero := TGenericGFPoly.Create(self, TArray<Integer>.Create(0));
FOne := TGenericGFPoly.Create(self, TArray<Integer>.Create(1));
end;
destructor TGenericGF.Destroy;
begin
self.FexpTable := nil;
self.FlogTable := nil;
inherited;
end;
class function TGenericGF.addOrSubtract(a, b: Integer): Integer;
begin
Result := (a xor b)
end;
function TGenericGF.buildMonomial(degree, coefficient: Integer): IGenericGFPoly;
var
coefficients: TArray<Integer>;
begin
if (degree < 0) then
raise EArgumentException.Create('Wrong argument');
if (coefficient = 0) then
begin
Result := self.Zero;
exit
end;
coefficients := TArray<Integer>.Create();
SetLength(coefficients, degree + 1);
coefficients[0] := coefficient;
Result := TGenericGFPoly.Create(self, coefficients);
end;
function TGenericGF.exp(a: Integer): Integer;
begin
Result := self.FexpTable[a]
end;
class function TGenericGF.get_AZTEC_DATA_10: TGenericGF;
begin
Result := TGenericGF.Create($409, $400, 1);
end;
class function TGenericGF.get_AZTEC_DATA_12: TGenericGF;
begin
Result := TGenericGF.Create($1069, $1000, 1);
end;
class function TGenericGF.get_AZTEC_DATA_6: TGenericGF;
begin
Result := TGenericGF.Create($43, $40, 1);
end;
class function TGenericGF.get_AZTEC_PARAM: TGenericGF;
begin
Result := TGenericGF.Create($13, $10, 1);
end;
class function TGenericGF.get_DATA_MATRIX_FIELD_256: TGenericGF;
begin
Result := TGenericGF.Create($12D, $100, 1);
end;
function TGenericGF.get_GeneratorBase: Integer;
begin
Result := FGeneratorBase;
end;
function TGenericGF.get_One: IGenericGFPoly;
begin
Result := FOne;
end;
class function TGenericGF.get_QR_CODE_FIELD_256: TGenericGF;
begin
Result := TGenericGF.Create($11D, $100, 0);
end;
function TGenericGF.get_Size: Integer;
begin
Result := Fsize;
end;
function TGenericGF.get_Zero: IGenericGFPoly;
begin
Result := FZero;
end;
function TGenericGF.inverse(a: Integer): Integer;
begin
if (a = 0) then
raise Exception.Create('Artithmetic Exception');
Result := self.FexpTable[((self.size - self.FlogTable[a]) - 1)];
end;
function TGenericGF.log(a: Integer): Integer;
begin
if (a = 0) then
raise EArgumentException.Create('wrong argument');
Result := self.FlogTable[a];
end;
function TGenericGF.multiply(a, b: Integer): Integer;
begin
if ((a = 0) or (b = 0)) then
begin
Result := 0;
exit
end;
Result := self.FexpTable
[((self.FlogTable[a] + self.FlogTable[b]) mod (self.Fsize - 1))];
end;
function TGenericGF.ToString: string;
begin
Result := 'GF(0x' + Fprimitive.ToString() + ',' + size.ToString() + ')';
end;
/// /////////////////////////////////////////////////////////////////////////////
constructor TGenericGFPoly.Create(Field: TGenericGF;
coefficients: TArray<Integer>);
var
coefficientsLength, firstNonZero: Integer;
begin
coefficientsLength := Length(coefficients);
if (coefficientsLength = 0) then
raise EArgumentException.Create('Wrong arguments');
self.Ffield := Field;
if ((coefficientsLength > 1) and (coefficients[0] = 0)) then
begin
firstNonZero := 1;
while (((firstNonZero < coefficientsLength) and
(coefficients[firstNonZero] = 0))) do
begin
inc(firstNonZero)
end;
Fcoefficients := TArray<Integer>.Create();
if (firstNonZero = coefficientsLength) then
begin
SetLength(Fcoefficients, 1);
end
else
begin
SetLength(Fcoefficients, coefficientsLength - firstNonZero);
Fcoefficients := Copy(coefficients, firstNonZero, Length(Fcoefficients));
end
end
else
Fcoefficients := coefficients
end;
destructor TGenericGFPoly.Destroy;
begin
SetLength(Fcoefficients, 0);
Fcoefficients := nil;
Ffield := nil;
inherited;
end;
function TGenericGFPoly.addOrSubtract(other: IGenericGFPoly): IGenericGFPoly;
var
lengthDiff, i, y: Integer;
smallerCoefficients, largerCoefficients, temp, sumDiff: TArray<Integer>;
begin
if (not self.Ffield.Equals(other.field)) then
raise EArgumentException.Create
('GenericGFPolys do not have same GenericGF field');
if (self.isZero) then
begin
Result := other;
exit
end;
if (other.isZero) then
begin
Result := self;
exit
end;
smallerCoefficients := self.coefficients;
largerCoefficients := other.coefficients;
if (Length(smallerCoefficients) > Length(largerCoefficients)) then
begin
temp := smallerCoefficients;
smallerCoefficients := largerCoefficients;
largerCoefficients := temp
end;
sumDiff := TArray<Integer>.Create();
SetLength(sumDiff, Length(largerCoefficients));
lengthDiff := Length(largerCoefficients) - Length(smallerCoefficients);
for y := 0 to lengthDiff - 1 do
begin
sumDiff[y] := largerCoefficients[y]
end;
i := lengthDiff;
while (i < Length(largerCoefficients)) do
begin
sumDiff[i] := TGenericGF.addOrSubtract(smallerCoefficients[(i - lengthDiff)
], largerCoefficients[i]);
inc(i)
end;
Result := TGenericGFPoly.Create(self.Ffield, sumDiff);
end;
function TGenericGFPoly.divide(other: IGenericGFPoly): TArray<IGenericGFPoly>;
var
term, quotient, remainder, iterationQuotient: IGenericGFPoly;
degreeDifference, denominatorLeadingTerm, scale,
inverseDenominatorLeadingTerm: Integer;
begin
if (not self.Ffield.Equals(other.field)) then
raise EArgumentException.Create
('GenericGFPolys do not have same GenericGF field');
if (other.isZero) then
raise EArgumentException.Create('Divide by 0');
quotient := self.Ffield.Zero;
remainder := self;
denominatorLeadingTerm := other.getCoefficient(other.degree);
inverseDenominatorLeadingTerm := self.Ffield.inverse(denominatorLeadingTerm);
while (((remainder.degree >= other.degree) and not remainder.isZero)) do
begin
degreeDifference := (remainder.degree - other.degree);
scale := self.Ffield.multiply(remainder.getCoefficient(remainder.degree),
inverseDenominatorLeadingTerm);
term := other.multiplyByMonomial(degreeDifference, scale);
iterationQuotient := self.Ffield.buildMonomial(degreeDifference, scale);
quotient := quotient.addOrSubtract(iterationQuotient);
remainder := remainder.addOrSubtract(term)
end;
Result := TArray<IGenericGFPoly>.Create(quotient, remainder);
end;
function TGenericGFPoly.evaluateAt(a: Integer): Integer;
var
size, coefficient, i, y, l: Integer;
begin
Result := 0;
if (a = 0) then
begin
Result := self.getCoefficient(0);
exit
end;
size := Length(self.coefficients);
if (a = 1) then
begin
l := Length(self.coefficients) - 1;
for y := 0 to l do
begin
coefficient := coefficients[y];
Result := TGenericGF.addOrSubtract(Result, coefficient)
end;
exit
end;
Result := self.coefficients[0];
i := 1;
while ((i < size)) do
begin
Result := TGenericGF.addOrSubtract(self.Ffield.multiply(a, Result),
self.coefficients[i]);
inc(i)
end;
end;
function TGenericGFPoly.getCoefficient(degree: Integer): Integer;
begin
Result := Fcoefficients[Length(Fcoefficients) - 1 - degree]
end;
function TGenericGFPoly.get_coefficients: TArray<Integer>;
begin
Result := fcoefficients;
end;
function TGenericGFPoly.get_Degree: Integer;
begin
Result := Length(Fcoefficients) - 1;
end;
function TGenericGFPoly.get_field: TGenericGF;
begin
Result := FField;
end;
function TGenericGFPoly.get_isZero: boolean;
begin
Result := Fcoefficients[0] = 0;
end;
function TGenericGFPoly.multiply(other: IGenericGFPoly): IGenericGFPoly;
var
product, aCoefficients, bCoefficients: TArray<Integer>;
aLength, bLength, i, aCoeff, j: Integer;
begin
if (not self.Ffield.Equals(other.field)) then
raise EArgumentException.Create
('GenericGFPolys do not have same GenericGF field');
if (self.isZero or other.isZero) then
begin
Result := self.Ffield.Zero;
exit
end;
aCoefficients := self.coefficients;
aLength := Length(aCoefficients);
bCoefficients := other.coefficients;
bLength := Length(bCoefficients);
product := TArray<Integer>.Create();
SetLength(product, (aLength + bLength) - 1);
i := 0;
while ((i < aLength)) do
begin
aCoeff := aCoefficients[i];
j := 0;
while ((j < bLength)) do
begin
product[(i + j)] := TGenericGF.addOrSubtract(product[(i + j)],
self.Ffield.multiply(aCoeff, bCoefficients[j]));
inc(j)
end;
inc(i)
end;
Result := TGenericGFPoly.Create(self.Ffield, product);
end;
function TGenericGFPoly.multiply(scalar: Integer): IGenericGFPoly;
var
product: TArray<Integer>;
size, i: Integer;
begin
if (scalar = 0) then
begin
Result := self.Ffield.Zero;
exit
end;
if (scalar = 1) then
begin
Result := self;
exit
end;
size := Length(self.coefficients);
product := TArray<Integer>.Create();
SetLength(product, size);
i := 0;
while ((i < size)) do
begin
product[i] := self.Ffield.multiply(self.coefficients[i], scalar);
inc(i)
end;
Result := TGenericGFPoly.Create(self.Ffield, product);
end;
function TGenericGFPoly.multiplyByMonomial(degree, coefficient: Integer)
: IGenericGFPoly;
var
product: TArray<Integer>;
size, i: Integer;
begin
if (degree < 0) then
raise EArgumentException.Create('Wrong arguments');
if (coefficient = 0) then
begin
Result := self.Ffield.Zero;
exit
end;
size := Length(self.coefficients);
product := TArray<Integer>.Create();
SetLength(product, size + degree);
i := 0;
while ((i < size)) do
begin
product[i] := self.Ffield.multiply(self.coefficients[i], coefficient);
inc(i)
end;
Result := TGenericGFPoly.Create(self.Ffield, product);
end;
function TGenericGFPoly.ToString: string;
var
lResult: TStringBuilder;
degree, coefficient, alphaPower: Integer;
begin
lResult := TStringBuilder.Create((8 * self.degree));
degree := self.degree;
while ((degree >= 0)) do
begin
coefficient := self.getCoefficient(degree);
if (coefficient <> 0) then
begin
if (coefficient < 0) then
begin
lResult.Append(' - ');
coefficient := -1 * coefficient
end
else if (Result.Length > 0) then
lResult.Append(' + ');
if ((degree = 0) or (coefficient <> 1)) then
begin
alphaPower := self.Ffield.log(coefficient);
if (alphaPower = 0) then
lResult.Append('1')
else if (alphaPower = 1) then
lResult.Append('a')
else
begin
lResult.Append('a^');
lResult.Append(alphaPower)
end
end;
if (degree <> 0) then
if (degree = 1) then
lResult.Append('x')
else
begin
lResult.Append('x^');
lResult.Append(degree)
end
end;
dec(degree)
end;
Result := lResult.ToString();
end;
initialization
TGenericGF.InitializeClass();
finalization
TGenericGF.FinalizeClass();
end.
@@ -0,0 +1,297 @@
{
* 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: Sean Owen and William Rucklidge
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.Common.ReedSolomon.ReedSolomonDecoder;
interface
uses
System.SysUtils,
// Hint: ZXing.Common.ReedSolomon.GenericGFPoly is implemented in GenericGF as second class
ZXing.Common.ReedSolomon.GenericGF;
type
/// <summary>
/// Implements Reed-Solomon encoding, as the name implies.
/// </summary>
TReedSolomonDecoder = class sealed
public
constructor Create(field: TGenericGF);
public
function decode(received: TArray<Integer>; twoS: Integer): boolean;
strict private
function findErrorLocations(errorLocator: IGenericGFPoly): TArray<Integer>;
strict private
function findErrorMagnitudes(errorEvaluator: IGenericGFPoly;
errorLocations: TArray<Integer>): TArray<Integer>;
private
function runEuclideanAlgorithm(a: IGenericGFPoly; b: IGenericGFPoly;
pR: Integer): TArray<IGenericGFPoly>;
strict private
field: TGenericGF;
end;
implementation
{ TReedSolomonDecoder }
constructor TReedSolomonDecoder.Create(field: TGenericGF);
begin
self.field := field
end;
function TReedSolomonDecoder.decode(received: TArray<Integer>;
twoS: Integer): boolean;
var
i, eval, position: Integer;
poly, syndrome, sigma, omega: IGenericGFPoly;
sigmaOmega: TArray<IGenericGFPoly>;
errorLocations: TArray<Integer>;
syndromeCoefficients, errorMagnitudes: TArray<Integer>;
noError: boolean;
begin
poly := TGenericGFPoly.Create(self.field, received);
syndromeCoefficients := TArray<Integer>.Create();
SetLength(syndromeCoefficients, twoS);
try
noError := true;
i := 0;
while ((i < twoS)) do
begin
eval := poly.evaluateAt(self.field.exp((i + self.field.GeneratorBase)));
syndromeCoefficients[((Length(syndromeCoefficients) - 1) - i)] := eval;
if (eval <> 0) then
noError := false;
inc(i)
end;
if (noError) then
begin
Result := true;
Exit;
end;
syndrome := TGenericGFPoly.Create(self.field, syndromeCoefficients);
sigmaOmega := self.runEuclideanAlgorithm(self.field.buildMonomial(twoS, 1),
syndrome, twoS);
if (sigmaOmega = nil) then
begin
Result := false;
Exit
end;
sigma := sigmaOmega[0];
errorLocations := self.findErrorLocations(sigma);
if (errorLocations = nil) then
begin
Result := false;
Exit
end;
omega := sigmaOmega[1];
errorMagnitudes := self.findErrorMagnitudes(omega, errorLocations);
i := 0;
while (i < Length(errorLocations)) do
begin
position := ((Length(received) - 1) - self.field.log(errorLocations[i]));
if (position < 0) then
begin
Result := false;
Exit
end;
received[position] := TGenericGF.addOrSubtract(received[position],
errorMagnitudes[i]);
inc(i)
end;
finally
SetLength(sigmaOmega, 0);
SetLength(syndromeCoefficients, 0);
end;
Result := true;
end;
function TReedSolomonDecoder.findErrorLocations(errorLocator: IGenericGFPoly)
: TArray<Integer>;
var
numErrors, e, i: Integer;
begin
numErrors := errorLocator.Degree;
if (numErrors = 1) then
begin
Result := TArray<Integer>.Create(errorLocator.getCoefficient(1));
Exit
end;
Result := TArray<Integer>.Create();
SetLength(Result, numErrors);
e := 0;
i := 1;
while (((i < self.field.Size) and (e < numErrors))) do
begin
if (errorLocator.evaluateAt(i) = 0) then
begin
Result[e] := self.field.inverse(i);
inc(e)
end;
inc(i);
end;
if (e <> numErrors) then
begin
Result := nil;
end;
end;
function TReedSolomonDecoder.findErrorMagnitudes(errorEvaluator: IGenericGFPoly;
errorLocations: TArray<Integer>): TArray<Integer>;
var
s, i, xiInverse, denominator, j, term, termPlus1: Integer;
begin
s := Length(errorLocations);
Result := TArray<Integer>.Create();
SetLength(Result, s);
i := 0;
while ((i < s)) do
begin
xiInverse := self.field.inverse(errorLocations[i]);
denominator := 1;
j := 0;
while ((j < s)) do
begin
if (i <> j) then
begin
term := self.field.multiply(errorLocations[j], xiInverse);
if ((term and 1) = 0) then
begin
termPlus1 := (term or 1)
end
else
begin
termPlus1 := (term and (not 1));
end;
denominator := self.field.multiply(denominator, termPlus1)
end;
inc(j)
end;
Result[i] := self.field.multiply(errorEvaluator.evaluateAt(xiInverse),
self.field.inverse(denominator));
if (self.field.GeneratorBase <> 0) then
Result[i] := self.field.multiply(Result[i], xiInverse);
inc(i)
end;
end;
function TReedSolomonDecoder.runEuclideanAlgorithm(a, b: IGenericGFPoly;
pR: Integer): TArray<IGenericGFPoly>;
var
temp, rLastLast, tLastLast, rLast, tLast, t, q, sigma, omega,
R: IGenericGFPoly;
denominatorLeadingTerm, dltInverse, degreeDiff, scale, inverse,
sigmaTildeAtZero: Integer;
begin
if (a.Degree < b.Degree) then
begin
temp := a;
a := b;
b := temp
end;
rLast := a;
R := b;
tLast := self.field.Zero;
t := self.field.One;
while ((R.Degree >= (pR div 2))) do
begin
rLastLast := rLast;
tLastLast := tLast;
rLast := R;
tLast := t;
if (rLast.isZero) then
begin
Result := nil;
Exit
end;
R := rLastLast;
q := self.field.Zero;
denominatorLeadingTerm := rLast.getCoefficient(rLast.Degree);
dltInverse := self.field.inverse(denominatorLeadingTerm);
while (((R.Degree >= rLast.Degree) and not R.isZero)) do
begin
degreeDiff := (R.Degree - rLast.Degree);
scale := self.field.multiply(R.getCoefficient(R.Degree), dltInverse);
q := q.addOrSubtract(self.field.buildMonomial(degreeDiff, scale));
R := R.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale))
end;
t := q.multiply(tLast).addOrSubtract(tLastLast);
if (R.Degree >= rLast.Degree) then
begin
Result := nil;
Exit
end
end;
sigmaTildeAtZero := t.getCoefficient(0);
if (sigmaTildeAtZero = 0) then
begin
Result := nil;
Exit
end;
inverse := self.field.inverse(sigmaTildeAtZero);
sigma := t.multiply(inverse);
omega := R.multiply(inverse);
Result := TArray<IGenericGFPoly>.Create(sigma, omega);
end;
end.
@@ -0,0 +1,96 @@
unit ZXIng.ByteSegments;
interface
type
/// <summary>
/// implements the ByteSegments (which was declared as a TList<TArray<Byte>>
/// throughout the code as reference-counted interface object)
/// </summary>
IByteSegments = Interface
['{0994FC90-E8F5-40D8-8A48-9B05DFFF2635}']
function Count:integer;
procedure Clear;
function GetCapacity: integer;
procedure SetCapacity(const Value: integer);
property Capacity:integer read GetCapacity write SetCapacity;
function Add(const item:TArray<byte>):integer;
function Get(index: integer):TArray<byte>;
end;
function ByteSegmentsCreate:IByteSegments;
implementation
uses system.SysUtils,
Generics.Collections;
type
TByteSegments = class(TInterfacedObject,IByteSegments)
private
FList: TList<TArray<Byte>>;
function Count:integer;
procedure Clear;
constructor Create;
function GetCapacity: integer;
procedure SetCapacity(const Value: integer);
function Add(const item:TArray<byte>):integer;
function Get(index: integer):TArray<byte>;
public
destructor Destroy; override;
end;
function ByteSegmentsCreate:IByteSegments;
begin
result := TByteSegments.Create;
end;
{ TByteSegments }
function TByteSegments.Add(const item: TArray<byte>): integer;
begin
result := FList.Add(item);
end;
procedure TByteSegments.Clear;
begin
FList.Clear;
end;
function TByteSegments.Count: integer;
begin
result := FList.Count;
end;
constructor TByteSegments.Create;
begin
FList := TList<TArray<Byte>>.Create;
inherited Create;
end;
destructor TByteSegments.Destroy;
begin
FList.Free;
inherited;
end;
function TByteSegments.GetCapacity: integer;
begin
result := FList.Capacity;
end;
procedure TByteSegments.SetCapacity(const Value: integer);
begin
FList.Capacity := value;
end;
function TByteSegments.Get(index: Integer) : TArray<byte>;
begin
Result := FList[index];
end;
end.
@@ -0,0 +1,89 @@
{
* 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.
* Implemented by E. Spelt for Delphi
}
unit ZXing.BarcodeFormat;
interface
type
TBarcodeFormat = (
/// <summary>No format set. All formats will be used</summary>
Auto = 0,
/// <summary>Aztec 2D barcode format.</summary>
AZTEC = 1,
/// <summary>CODABAR 1D format.</summary>
CODABAR = 2,
/// <summary>Code 39 1D format.</summary>
CODE_39 = 4,
/// <summary>Code 93 1D format.</summary>
CODE_93 = 8,
/// <summary>Code 128 1D format.</summary>
CODE_128 = 16,
/// <summary>Data Matrix 2D barcode format.</summary>
DATA_MATRIX = 32,
/// <summary>EAN-8 1D format.</summary>
EAN_8 = 64,
/// <summary>EAN-13 1D format.</summary>
EAN_13 = 128,
/// <summary>ITF (Interleaved Two of Five) 1D format.</summary>
ITF = 256,
/// <summary>MaxiCode 2D barcode format.</summary>
MAXICODE = 512,
/// <summary>PDF417 format.</summary>
PDF_417 = 1024,
/// <summary>QR Code 2D barcode format.</summary>
QR_CODE = 2048,
/// <summary>RSS 14</summary>
RSS_14 = 4096,
/// <summary>RSS EXPANDED</summary>
RSS_EXPANDED = 8192,
/// <summary>UPC-A 1D format.</summary>
UPC_A = 16384,
/// <summary>UPC-E 1D format.</summary>
UPC_E = 32768,
/// <summary>UPC/EAN extension format. Not a stand-alone format.</summary>
UPC_EAN_EXTENSION = 65536,
/// <summary>MSI</summary>
MSI = 131072,
/// <summary>Plessey</summary>
PLESSEY = 262144
);
implementation
end.
@@ -0,0 +1,110 @@
unit ZXing.BitSource;
{
* 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.
* Implemented by E. Spelt for Delphi
}
interface
uses
SysUtils,
ZXing.Common.Detector.MathUtils;
type
TBitSource = class sealed
private
bytes: TArray<Byte>;
public
BitOffset: Integer;
ByteOffset: Integer;
constructor Create(bytes: TArray<Byte>);
function available: Integer;
function readBits(numBits: Integer): Integer;
end;
implementation
{ TBitSource }
function TBitSource.available: Integer;
begin
Result := 8 * (Length(self.bytes) - self.ByteOffset) - self.BitOffset;
end;
constructor TBitSource.Create(bytes: TArray<Byte>);
begin
self.bytes := bytes;
end;
function TBitSource.readBits(numBits: Integer): Integer;
var
bitsToNotRead, bitsLeft, toRead, mask: Integer;
begin
if (((numBits < 1) or (numBits > $20)) or (numBits > available())) then
raise EArgumentException.Create(numBits.ToString + 'numBits');
Result := 0;
if (BitOffset > 0) then
begin
bitsLeft := 8 - BitOffset;
if (numBits < bitsLeft) then
toRead := numBits
else
toRead := bitsLeft;
bitsToNotRead := (bitsLeft - toRead);
mask := TMathUtils.Asr($FF, ((8 - toRead) and $1F)) shl bitsToNotRead;
Result := TMathUtils.Asr((self.bytes[self.ByteOffset] and mask),
bitsToNotRead);
dec(numBits, toRead);
inc(BitOffset, toRead);
if (BitOffset = 8) then
begin
BitOffset := 0;
inc(ByteOffset)
end
end;
if (numBits > 0) then
begin
while ((numBits >= 8)) do
begin
Result := ((Result shl 8) or (self.bytes[self.ByteOffset] and $FF));
inc(self.ByteOffset);
dec(numBits, 8)
end;
if (numBits > 0) then
begin
bitsToNotRead := 8 - numBits;
mask := TMathUtils.Asr($FF, bitsToNotRead) shl bitsToNotRead;
Result := (Result shl numBits) or
TMathUtils.Asr((bytes[ByteOffset] and mask), bitsToNotRead);
inc(BitOffset, numBits)
end
end;
end;
end.
@@ -0,0 +1,180 @@
unit ZXing.CharacterSetECI;
{
* 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.
* Implemented by E. Spelt for Delphi
}
interface
uses SysUtils, Generics.Collections;
type
TECI = class abstract
private
value_Renamed: Integer;
constructor Create(value_Renamed: Integer);
public
class function getECIByValue(value_Renamed: Integer): TECI; static;
property value: Integer read value_Renamed;
end;
TCharacterSetECI = class sealed(TECI)
strict private
FEncodingName: string;
class var NAME_TO_ECI: TDictionary<string, TCharacterSetECI>;
class var VALUE_TO_ECI: TDictionary<Integer, TCharacterSetECI>;
///<summary>
/// this objectlist is just for doing proper deallocation of TCharacterSetECI instances under Win32/Win64,
/// without having to mess with TInterfacedObject. Since instances are meant to exist for the whole program life, there's no need of doing
/// anything much more complicated than this.
/// </summary>
class var AllocatedInstances:TObjectList<TCharacterSetECI>;
class constructor Create;
constructor Create(value: Integer; encodingName: string); overload;
class destructor Destroy;
class procedure addCharacterSet(value: Integer; encodingNames: TArray<string>); static;
public
property encodingName: string read FEncodingName;
class function getCharacterSetECIByName(name: string)
: TCharacterSetECI; static;
class function getCharacterSetECIByValue(value: Integer)
: TCharacterSetECI; static;
end;
implementation
{ TECI }
constructor TECI.Create(value_Renamed: Integer);
begin
self.value_Renamed := value_Renamed
end;
class function TECI.getECIByValue(value_Renamed: Integer): TECI;
begin
if ((value_Renamed < 0) or (value_Renamed > $F423F)) then
raise EArgumentException.Create('Bad ECI value: ' +
value_Renamed.ToString());
if (value_Renamed < 900) then
begin
Result := TCharacterSetECI.getCharacterSetECIByValue(value_Renamed);
exit
end;
Result := nil;
end;
{ TCharacterSetECI }
constructor TCharacterSetECI.Create(value: Integer; encodingName: string);
begin
inherited Create(value);
FEncodingName := encodingName;
AllocatedInstances.Add(self); // add the instance to the global list of all created instances
end;
class destructor TCharacterSetECI.Destroy;
begin
NAME_TO_ECI.Free;
VALUE_TO_ECI.Free;
if AllocatedInstances<>nil then begin
AllocatedInstances.Clear;
AllocatedInstances.Free;
end;
end;
class constructor TCharacterSetECI.Create;
begin
NAME_TO_ECI:=TDictionary<string, TCharacterSetECI>.Create;
VALUE_TO_ECI:=TDictionary<Integer, TCharacterSetECI>.Create;
// under Win32/64 TObjectList by default "OWNS" the objects it contains:
// if we clear the list, the objects contained get automatically destroyed
AllocatedInstances:= TObjectList<TCharacterSetECI>.Create;
TCharacterSetECI.addCharacterSet( 0, ['CP437']);
TCharacterSetECI.addCharacterSet( 1, ['ISO-8859-1','ISO8859_1']);
TCharacterSetECI.addCharacterSet( 2, ['CP437']);
TCharacterSetECI.addCharacterSet( 3, ['ISO-8859-1', 'ISO8859_1']);
TCharacterSetECI.addCharacterSet( 4, ['ISO-8859-2', 'ISO8859_2']);
TCharacterSetECI.addCharacterSet( 5, ['ISO-8859-3', 'ISO8859_3']);
TCharacterSetECI.addCharacterSet( 6, ['ISO-8859-4', 'ISO8859_4']);
TCharacterSetECI.addCharacterSet( 7, ['ISO-8859-5', 'ISO8859_5']);
TCharacterSetECI.addCharacterSet( 8, ['ISO-8859-6', 'ISO8859_6']);
TCharacterSetECI.addCharacterSet( 9, ['ISO-8859-7', 'ISO8859_7']);
TCharacterSetECI.addCharacterSet( 10, ['ISO-8859-8', 'ISO8859_8']);
TCharacterSetECI.addCharacterSet( 11, ['ISO-8859-9', 'ISO8859_9']);
TCharacterSetECI.addCharacterSet( 12, ['ISO-8859-4', 'ISO-8859-10', 'ISO8859_10']);
TCharacterSetECI.addCharacterSet( 13, ['ISO-8859-11', 'ISO8859_11']);
TCharacterSetECI.addCharacterSet( 15, ['ISO-8859-13', 'ISO8859_13']);
TCharacterSetECI.addCharacterSet($10, ['ISO-8859-1', 'ISO-8859-14', 'ISO8859_14']);
TCharacterSetECI.addCharacterSet($11, ['ISO-8859-15', 'ISO8859_15']);
TCharacterSetECI.addCharacterSet($12, ['ISO-8859-3', 'ISO-8859-16', 'ISO8859_16']);
TCharacterSetECI.addCharacterSet( 20, ['SJIS', 'Shift_JIS']);
TCharacterSetECI.addCharacterSet($15, ['WINDOWS-1250','CP1250']);
TCharacterSetECI.addCharacterSet($16, ['WINDOWS-1251','CP1251']);
TCharacterSetECI.addCharacterSet($17, ['WINDOWS-1252','CP1252']);
TCharacterSetECI.addCharacterSet($18, ['WINDOWS-1256', 'CP1256']);
TCharacterSetECI.addCharacterSet($19, ['UTF-16BE','UNICODEBIG']);
TCharacterSetECI.addCharacterSet($1A, ['UTF-8', 'UTF8']);
TCharacterSetECI.addCharacterSet($1B, ['US-ASCII']);
TCharacterSetECI.addCharacterSet(170, ['US-ASCII']);
TCharacterSetECI.addCharacterSet($1C, ['BIG5']);
TCharacterSetECI.addCharacterSet($1D, ['GB18030', 'GB2312', 'EUC_CN', 'GBK'] );
TCharacterSetECI.addCharacterSet( 30, ['EUC-KR', 'EUC_KR']);
end;
class procedure TCharacterSetECI.addCharacterSet(value: Integer; encodingNames: TArray<string>);
var
t: string;
Eci: TCharacterSetECI;
begin
Eci := TCharacterSetECI.Create(value, encodingNames[0]);
TCharacterSetECI.VALUE_TO_ECI.AddOrSetValue(value,Eci); // note : VALUE_TO_ECI[value] := Eci raises "key not found exception": it cant' be used for adding not existing values
for t in encodingNames do
TCharacterSetECI.NAME_TO_ECI.AddOrSetValue(t,Eci);
end;
class function TCharacterSetECI.getCharacterSetECIByName(name: string)
: TCharacterSetECI;
begin
Result := TCharacterSetECI.NAME_TO_ECI[name.ToUpper]
end;
class function TCharacterSetECI.getCharacterSetECIByValue(value: Integer)
: TCharacterSetECI;
begin
if ((value < 0) or (value >= 900)) then
begin
Result := nil;
exit
end;
Result := TCharacterSetECI.VALUE_TO_ECI[value];
end;
end.
@@ -0,0 +1,70 @@
{
* 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: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.Common.BitArray;
interface
type
IBitArray = interface
['{3D65F451-E408-4015-A637-73CD05877BCB}']
// property getters and setters
function GetBit(i: Integer): Boolean;
procedure SetBit(i: Integer; Value: Boolean);
function GetBits: TArray<Integer>;
function Size: Integer;
function SizeInBytes: Integer;
property Self[i: Integer]: Boolean read GetBit write SetBit; default;
property Bits: TArray<Integer> read GetBits;
function getNextSet(from: Integer): Integer;
function getNextUnset(from: Integer): Integer;
procedure setBulk(i, newBits: Integer);
procedure setRange(start, ending: Integer);
procedure appendBit(bit: Boolean);
procedure Reverse();
procedure clear();
function isRange(start, ending: Integer; const value: Boolean): Boolean;
end;
TBitArrayHelpers = class
class function CreateBitArray:IBitArray; overload;
class function CreateBitArray(const Size: Integer):IBitArray; overload;
end;
implementation
uses ZXing.Common.BitArrayImplementation;
class function TBitArrayHelpers.CreateBitArray:IBitArray;
begin
result := ZXing.Common.BitArrayImplementation.NewBitArray;
end;
class function TBitArrayHelpers.CreateBitArray(const Size: Integer):IBitArray;
begin
result := ZXing.Common.BitArrayImplementation.NewBitArray(size);
end;
end.
@@ -0,0 +1,468 @@
unit ZXing.Common.BitArrayImplementation;
interface
uses ZXing.Common.BitArray;
function NewBitArray:IBitArray; overload;
function NewBitArray(const Size: Integer):IBitArray; overload;
implementation
uses
System.SysUtils,
ZXing.Common.Detector.MathUtils,
Math;
type
/// <summary>
/// A simple, fast array of bits, represented compactly by an array of ints internally.
/// </summary>
TBitArrayImplementation = class(TInterfacedObject, IBitArray)
strict private
_lookup: TArray<Integer>;
Fbits: TArray<Integer>;
Fsize: Integer;
procedure InitLookup();
function GetBit(i: Integer): Boolean;
procedure SetBit(i: Integer; Value: Boolean);
function makeArray(Size: Integer): TArray<Integer>;
procedure ensureCapacity(size: Integer);
function numberOfTrailingZeros(num: Integer): Integer;
function GetBits: TArray<Integer>;
private
constructor Create(); overload;
constructor Create(const Size: Integer); overload;
public
function Size: Integer;
function SizeInBytes: Integer;
property Self[i: Integer]: Boolean read GetBit write SetBit; default;
property Bits: TArray<Integer> read Fbits;
destructor Destroy; override;
function getNextSet(from: Integer): Integer;
function getNextUnset(from: Integer): Integer;
procedure setBulk(i, newBits: Integer);
procedure setRange(start, ending: Integer);
procedure appendBit(bit: Boolean);
procedure Reverse();
procedure clear();
function isRange(start, ending: Integer;
const value: Boolean): Boolean;
end;
constructor TBitArrayImplementation.Create;
begin
Fsize := 0;
SetLength(Fbits, 1);
InitLookup();
end;
constructor TBitArrayImplementation.Create(const Size: Integer);
begin
if (Size < 1)
then
raise EArgumentException.Create('size must be at least 1.');
Fsize := Size;
Fbits := makeArray(Size);
InitLookup();
end;
procedure TBitArrayImplementation.ensureCapacity(size: Integer);
var
newBits : TArray<Integer>;
begin
if (size > TMathUtils.Asr(Length(Fbits), 5)) then
begin
newBits := makeArray(size);
Move(Fbits[0], newBits[0], Length(Fbits) * SizeOf(Fbits[0]));
Fbits := newBits;
end;
end;
destructor TBitArrayImplementation.Destroy;
begin
Fbits := nil;
_lookup := nil;
inherited;
end;
function TBitArrayImplementation.GetBit(i: Integer): Boolean;
begin
Result := ((Fbits[TMathUtils.Asr(i, 5)]) and (1 shl (i and $1F))) <> 0;
end;
function TBitArrayImplementation.GetBits: TArray<Integer>;
begin
result := FBits;
end;
/// <summary>
/// Gets the next set.
/// </summary>
/// <param name="from">first bit to check</param>
/// <returns>index of first bit that is set, starting from the given index, or size if none are set
/// at or beyond this given index</returns>
function TBitArrayImplementation.getNextSet(from: Integer): Integer;
var
bitsOffset,
currentBits : Integer;
begin
if (from >= Fsize) then
Exit(FSize);
bitsOffset := TMathUtils.Asr(from, 5);
currentBits := Fbits[bitsOffset];
// mask off lesser bits first
{$OVERFLOWCHECKS OFF}
currentBits := currentBits and -(1 shl (from and $1F));
{$OVERFLOWCHECKS ON}
while (currentBits = 0) do
begin
Inc(bitsOffset);
if (bitsOffset = Length(Fbits)) then
Exit(FSize);
currentBits := Fbits[bitsOffset];
end;
Result := (bitsOffset shl 5) + numberOfTrailingZeros(currentBits);
Result := Math.Min(Result, FSize);
end;
/// <summary>
/// see getNextSet(int)
/// </summary>
/// <param name="from">index to start looking for unset bit</param>
/// <returns>index of next unset bit, or <see cref="Size"/> if none are unset until the end</returns>
function TBitArrayImplementation.getNextUnset(from: Integer): Integer;
var
bitsOffset,
currentBits: Integer;
begin
if (from >= Fsize) then
begin
Result := Fsize;
exit;
end;
bitsOffset := TMathUtils.Asr(from, 5);
currentBits := not Fbits[bitsOffset];
// mask off lesser bits first
{$OVERFLOWCHECKS OFF}
currentBits := currentBits and -(1 shl (from and $1F));
{$OVERFLOWCHECKS ON}
while (currentBits = 0) do
begin
Inc(bitsOffset);
if (bitsOffset = Length(Fbits)) then
begin
Result := Size;
exit;
end;
currentBits := not Fbits[bitsOffset];
end;
Result := (bitsOffset shl 5) + numberOfTrailingZeros(currentBits);
if (Result > Size)
then
Result := Size;
end;
procedure TBitArrayImplementation.InitLookup;
begin
_lookup := TArray<Integer>.Create(32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30,
28, 11, 0, 13, 4, 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
20, 8, 19, 18);
end;
function TBitArrayImplementation.makeArray(Size: Integer): TArray<Integer>;
var
ar: TArray<Integer>;
begin
SetLength(ar, TMathUtils.Asr((Size + 31), 5));
Result := ar;
end;
function TBitArrayImplementation.numberOfTrailingZeros(num: Integer): Integer;
var
index: Integer;
begin
Result := 0;
{$OVERFLOWCHECKS OFF}
index := (-num and num) mod 37;
if (index < 0) then
begin
index := index * -1;
end;
if (index >= Low(_lookup)) and (index <= High(_lookup)) then
Result := _lookup[index];
{$OVERFLOWCHECKS ON}
end;
procedure TBitArrayImplementation.Reverse;
var
newBits: TArray<Integer>;
i, len, oldBitsLen, leftOffset, mask, nextInt, currentInt: Integer;
x: Int64;
begin
SetLength(newBits, Length(Fbits));
// reverse all int's first
len := TMathUtils.Asr((Size - 1), 5);
oldBitsLen := len + 1;
for i := 0 to oldBitsLen - 1 do
begin
x := Fbits[i];
x := (TMathUtils.Asr(x, 1) and $55555555) or ((x and $55555555) shl 1);
x := (TMathUtils.Asr(x, 2) and $33333333) or ((x and $33333333) shl 2);
x := (TMathUtils.Asr(x, 4) and $0F0F0F0F) or ((x and $0F0F0F0F) shl 4);
x := (TMathUtils.Asr(x, 8) and $00FF00FF) or ((x and $00FF00FF) shl 8);
x := (TMathUtils.Asr(x, 16) and $0000FFFF) or ((x and $0000FFFF) shl 16);
newBits[len - i] := integer(x) ;
end;
// now correct the int's if the bit size isn't a multiple of 32
if (Size <> oldBitsLen * 32) then
begin
leftOffset := oldBitsLen * 32 - Size;
mask := 1;
for i := 0 to 31 - leftOffset - 1 do
begin
mask := (mask shl 1) or 1;
end;
currentInt := TMathUtils.Asr(newBits[0], leftOffset) and mask;
for i := 1 to oldBitsLen - 1 do
begin
nextInt := newBits[i];
currentInt := currentInt or (nextInt shl (32 - leftOffset));
newBits[i - 1] := currentInt;
currentInt := TMathUtils.Asr(nextInt, leftOffset) and mask;
end;
newBits[oldBitsLen - 1] := currentInt;
end;
Fbits := newBits;
end;
procedure TBitArrayImplementation.SetBit(i: Integer; Value: Boolean);
var
index: Integer;
begin
if (Value) then
begin
index := TMathUtils.Asr(i, 5);
Fbits[index] := Fbits[index] or 1 shl (i and $1F);
end;
end;
function TBitArrayImplementation.Size: Integer;
begin
result := FSize;
end;
function TBitArrayImplementation.SizeInBytes: Integer;
begin
Result := TMathUtils.Asr(Size + 7, 3);
end;
/// <summary> Sets a block of 32 bits, starting at bit i.
///
/// </summary>
/// <param name="i">first bit to set
/// </param>
/// <param name="newBits">the new value of the next 32 bits. Note again that the least-significant bit
/// corresponds to bit i, the next-least-significant to i+1, and so on.
/// </param>
procedure TBitArrayImplementation.setBulk(i, newBits: Integer);
var
r: Integer;
begin
r := TMathUtils.Asr(i, 5);
Fbits[r] := newBits;
end;
/// <summary>
/// Sets a range of bits.
/// </summary>
/// <param name="start">start of range, inclusive.</param>
/// <param name="ending">end of range, exclusive</param>
procedure TBitArrayImplementation.setRange(start, ending: Integer);
var
firstInt,
lastInt,
mask,
i, j : Integer;
firstBit,
lastBit : Integer;
begin
if (ending < start)
then
raise EArgumentException.Create('Start is greater than end');
if (ending = start)
then
exit;
Dec(ending); // will be easier to treat this as the last actually set bit -- inclusive
firstInt := TMathUtils.Asr(start, 5);
lastInt := TMathUtils.Asr(ending, 5);
for i := firstInt to lastInt do
begin
if (i > firstInt)
then
firstBit := start
else
firstBit := $1F;
if (i < lastInt)
then
lastBit := 31
else
lastBit := (ending and $1F);
if ((firstBit = 0) and (lastBit = 31))
then
mask := -1
else
begin
mask := 0;
for j := firstBit to lastBit do
begin
mask := (mask or (1 shl j));
end;
end;
bits[i] := (bits[i] or mask);
end;
end;
/// <summary> Clears all bits (sets to false).</summary>
procedure TBitArrayImplementation.Clear;
var
max,
i: Integer;
begin
max := Length(Fbits);
for i := 0 to Pred(max) do
begin
Fbits[i] := 0;
end;
end;
/// <summary> Efficient method to check if a range of bits is set, or not set.
///
/// </summary>
/// <param name="start">start of range, inclusive.
/// </param>
/// <param name="ending">end of range, exclusive
/// </param>
/// <param name="value">if true, checks that bits in range are set, otherwise checks that they are not set
/// </param>
/// <returns> true iff all bits are set or not set in range, according to value argument
/// </returns>
/// <throws> EIllegalArgumentException if end is less than or equal to start </throws>
function TBitArrayImplementation.isRange(start, ending: Integer;
const value: Boolean): Boolean;
var
firstInt,
lastInt,
firstBit,
lastBit,
mask,
i, j,
temp: Integer;
begin
if (ending = start) then
begin
Result := true; // empty range matches
exit;
end;
Dec(ending); // will be easier to treat this as the last actually set bit -- inclusive
firstInt := TMathUtils.Asr(start, 5);
lastInt := TMathUtils.Asr(ending, 5);
for i := firstInt to lastInt do
begin
if (i > firstInt)
then
firstBit := 0
else
firstBit := (start and $1F);
if (i < lastInt)
then
lastBit := 31
else
lastBit := (ending and $1F);
if ((firstBit = 0) and (lastBit = 31))
then
mask := -1
else
begin
mask := 0;
for j := firstBit to lastBit do
mask := mask or (1 shl j);
end;
// Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is,
// equals the mask, or we're looking for 0s and the masked portion is not all 0s
if (Value)
then
temp := mask
else
temp := 0;
if ((Fbits[i] and mask) <> (temp)) then
begin
Result := False;
exit;
end;
end;
Result := true;
end;
/// <summary>
/// Appends the bit.
/// </summary>
/// <param name="bit">The bit.</param>
procedure TBitArrayImplementation.appendBit(bit: Boolean);
var
i: Integer;
begin
ensureCapacity(Fsize + 1);
if (bit) then
begin
i := TMathUtils.Asr(Fsize, 5);
bits[i] := (bits[i] or (Fsize and $1F));
end;
Dec(Fsize);
end;
function NewBitArray:IBitArray;
begin
result := TBitArrayImplementation.Create;
end;
function NewBitArray(const Size: Integer):IBitArray;
begin
result := TBitArrayImplementation.Create(size);
end;
end.
@@ -0,0 +1,480 @@
{
* 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: Sean Owen and dswitkin@google.com (Daniel Switkin)
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.Common.BitMatrix;
interface
uses
SysUtils,
{$IFDEF FRAMEWORK_FMX}
FMX.Graphics,
{$ENDIF}
{$IFDEF FRAMEWORK_VCL}
VCL.Graphics,
{$ENDIF}
Generics.Collections,
ZXing.Common.BitArray,
ZXing.BarcodeFormat,
ZXing.Helpers,
ZXing.Common.Detector.MathUtils;
type
/// <summary>
/// <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common
/// module, x is the column position, and y is the row position. The ordering is always x, y.
/// The origin is at the top-left.</p>
/// <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
/// with a new int. This is done intentionally so that we can copy out a row into a BitArray very
/// efficiently.</p>
/// <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,
/// meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
/// </summary>
TBitMatrix = class sealed
private
Fbits: TArray<Integer>;
Fheight: Integer;
FrowSize: Integer;
Fwidth: Integer;
function getBit(x, y: Integer): Boolean;
procedure setBit(x, y: Integer; const value: Boolean);
constructor Create(const width, height, rowSize: Integer;
const bits: TArray<Integer>); overload;
public
constructor Create(const width, height: Integer); overload;
constructor Create(const dimension: Integer); overload;
destructor Destroy; override;
procedure clear;
function Clone: TObject;
function Equals(obj: TObject): Boolean; override;
procedure flip(x: Integer; y: Integer);
function getBottomRightOnBit: TArray<Integer>;
function getEnclosingRectangle: TArray<Integer>;
function GetHashCode: Integer; override;
function getRow(const y: Integer; row: IBitArray): IBitArray;
function getTopLeftOnBit: TArray<Integer>;
procedure Rotate180;
procedure setRegion(left: Integer; top: Integer; width: Integer;
height: Integer);
procedure setRow(y: Integer; row: IBitArray);
function ToBitmap: TBitmap; overload;
function ToBitmap(format: TBarcodeFormat; content: string)
: TBitmap; overload;
property width: Integer read Fwidth;
property height: Integer read Fheight;
property Matrix[x, y: Integer]: Boolean read getBit write setBit; default;
// added for debugging
function ToString: string; override;
end;
implementation
{ TBitMatrix }
function TBitMatrix.getBit(x, y: Integer): Boolean;
var
offset, v, bits, shift: Int64;
uBits: Cardinal;
begin
offset := y * FrowSize + TMathUtils.Asr(x, 5);
if ( offset >= Low( FBits ) ) and ( offset <= High( FBits ) ) then
begin
bits := Fbits[offset];
uBits := Cardinal(bits);
shift := (x and $1F);
v := TMathUtils.Asr(uBits, shift);
Result := (v and 1) <> 0;
end
else
Result := False;
end;
procedure TBitMatrix.setBit(x, y: Integer; const value: Boolean);
var
offset: NativeInt;
begin
if (value) then
begin
offset := y * FrowSize + TMathUtils.Asr(x, 5);
Fbits[offset] := Fbits[offset] or (1 shl (x and $1F));
end
else
begin
offset := Trunc(y * FrowSize + (x / 32));
Fbits[offset] := Fbits[offset] and (not(1 shl (x and $1F)));
end;
end;
procedure TBitMatrix.clear;
var
max, i: Integer;
begin
max := Length(Fbits);
i := 0;
while ((i < max)) do
begin
Fbits[i] := 0;
Inc(i);
end;
end;
function TBitMatrix.Clone: TObject;
var
b: TArray<Integer>;
begin
b := TArray.Clone(Fbits);
Result := TBitMatrix.Create(Self.Fwidth, Self.Fheight, Self.FrowSize, b);
end;
constructor TBitMatrix.Create(const width, height, rowSize: Integer;
const bits: TArray<Integer>);
begin
if ((width < 1) or (height < 1)) then
raise EArgumentException.Create('Both dimensions must be greater than 0');
Self.Fwidth := width;
Self.Fheight := height;
Self.FrowSize := rowSize;
Self.Fbits := bits;
end;
constructor TBitMatrix.Create(const width, height: Integer);
begin
if ((width < 1) or (height < 1)) then
raise EArgumentException.Create('Both dimensions must be greater than 0');
Self.Fwidth := width;
Self.Fheight := height;
Self.FrowSize := TMathUtils.Asr((width + $1F), 5);
SetLength(Self.Fbits, Self.FrowSize * height);
Self.clear;
end;
constructor TBitMatrix.Create(const dimension: Integer);
begin
Self.Create(dimension, dimension);
end;
destructor TBitMatrix.Destroy;
begin
Self.Fbits := nil;
inherited;
end;
function TBitMatrix.Equals(obj: TObject): Boolean;
var
other: TBitMatrix;
i, l: Integer;
begin
if (not(obj is TBitMatrix)) then
begin
Result := false;
exit;
end;
other := (obj as TBitMatrix);
if ((((Fwidth <> other.Fwidth) or (Fheight <> other.Fheight)) or
(FrowSize <> other.FrowSize)) or (Length(Fbits) <> Length(other.Fbits)))
then
begin
Result := false;
exit
end;
i := 0;
l := Length(Fbits);
while ((i < l)) do
begin
if (Fbits[i] <> other.Fbits[i]) then
begin
Result := false;
exit
end;
Inc(i)
end;
Result := true;
end;
procedure TBitMatrix.flip(x, y: Integer);
var
offset, s: Integer;
begin
s := TMathUtils.Asr(x, 5);
offset := ((y * FrowSize) + s);
Fbits[offset] := (Fbits[offset] xor (1 shl x))
end;
function TBitMatrix.getBottomRightOnBit: TArray<Integer>;
var
bitsOffset, x, y, theBits, bit: Integer;
begin
bitsOffset := Length(Fbits) - 1;
while ((bitsOffset >= 0) and (Self.Fbits[bitsOffset] = 0)) do
begin
dec(bitsOffset)
end;
if (bitsOffset < 0) then
begin
Result := nil;
exit
end;
y := (bitsOffset div FrowSize);
x := ((bitsOffset mod FrowSize) shl 5);
theBits := Fbits[bitsOffset];
bit := $1F;
while ((TMathUtils.Asr(theBits, bit) = 0)) do
begin
dec(bit);
end;
Inc(x, bit);
Result := TArray<Integer>.Create(x, y);
end;
function TBitMatrix.getEnclosingRectangle: TArray<Integer>;
var
bit, left, top, right, bottom, y, x32, theBits, widthTmp, heightTmp: Integer;
begin
left := Self.Fwidth;
top := Self.Fheight;
right := -1;
bottom := -1;
y := 0;
while ((y < Self.Fheight)) do
begin
x32 := 0;
while ((x32 < Self.FrowSize)) do
begin
theBits := Self.Fbits[((y * Self.FrowSize) + x32)];
if (theBits <> 0) then
begin
if (y < top) then
top := y;
if (y > bottom) then
bottom := y;
if ((x32 * $20) < left) then
begin
bit := 0;
while (((theBits shl ($1F - bit)) = 0)) do
begin
Inc(bit)
end;
if (((x32 * $20) + bit) < left) then
left := ((x32 * $20) + bit)
end;
if (((x32 * $20) + $1F) > right) then
begin
bit := $1F;
while ((TMathUtils.Asr(theBits, bit) = 0)) do
begin
dec(bit)
end;
if (((x32 * $20) + bit) > right) then
right := ((x32 * $20) + bit)
end
end;
Inc(x32)
end;
Inc(y)
end;
widthTmp := (right - left);
heightTmp := (bottom - top);
if ((widthTmp < 0) or (heightTmp < 0)) then
begin
Result := nil;
exit;
end;
Result := TArray<Integer>.Create(left, top, widthTmp, heightTmp);
end;
function TBitMatrix.GetHashCode: Integer;
var
bit, hash: Integer;
begin
hash := Self.Fwidth;
hash := (($1F * hash) + Self.Fwidth);
hash := (($1F * hash) + Self.Fheight);
hash := (($1F * hash) + Self.FrowSize);
for bit in Self.Fbits do
begin
hash := (($1F * hash) + bit); // .GetHashCode
end;
Result := hash;
end;
function TBitMatrix.getRow(const y: Integer; row: IBitArray): IBitArray;
var
x, offset: Integer;
begin
if ((row = nil) or (row.Size < Self.Fwidth)) then
row := TBitArrayHelpers.CreateBitArray(Self.Fwidth)
else
row.clear;
offset := (y * Self.FrowSize);
x := 0;
while ((x < Self.FrowSize)) do
begin
row.setBulk((x shl 5), Self.Fbits[(offset + x)]);
Inc(x)
end;
Result := row;
end;
function TBitMatrix.getTopLeftOnBit: TArray<Integer>;
var
bitsOffset, x, y, theBits, bit: Integer;
begin
bitsOffset := 0;
while (((bitsOffset < Length(Fbits)) and (Fbits[bitsOffset] = 0))) do
begin
Inc(bitsOffset)
end;
if (bitsOffset = Length(Fbits)) then
begin
Result := nil;
exit
end;
y := (bitsOffset div Self.FrowSize);
x := ((bitsOffset mod Self.FrowSize) shl 5);
theBits := Self.Fbits[bitsOffset];
bit := 0;
while (((theBits shl ($1F - bit)) = 0)) do
begin
Inc(bit)
end;
Inc(x, bit);
Result := TArray<Integer>.Create(x, y);
end;
procedure TBitMatrix.Rotate180;
var
i, width, height: Integer;
topRow, bottomRow: IBitArray;
begin
width := Self.Fwidth;
height := Self.Fheight;
topRow := TBitArrayHelpers.CreateBitArray(width);
bottomRow := TBitArrayHelpers.CreateBitArray(width);
i := 0;
while ((i < ((height + 1) div 2))) do
begin
topRow := Self.getRow(i, topRow);
bottomRow := Self.getRow(((height - 1) - i), bottomRow);
topRow.reverse;
bottomRow.reverse;
Self.setRow(i, bottomRow);
Self.setRow(((height - 1) - i), topRow);
Inc(i)
end;
end;
procedure TBitMatrix.setRegion(left, top, width, height: Integer);
var
x, y, offset, right, bottom: Integer;
begin
if ((top < 0) or (left < 0)) then
raise EArgumentException.Create('Left and top must be non-negative');
if ((height < 1) or (width < 1)) then
raise EArgumentException.Create('Height and width must be at least 1');
right := (left + width);
bottom := (top + height);
if ((bottom > Self.Fheight) or (right > Self.Fwidth)) then
raise EArgumentException.Create('The region must fit inside the matrix');
y := top;
while ((y < bottom)) do
begin
offset := (y * Self.FrowSize);
x := left;
while ((x < right)) do
begin
Fbits[(offset + TMathUtils.Asr(x, 5))] :=
(Fbits[(offset + TMathUtils.Asr(x, 5))] or (1 shl x));
Inc(x)
end;
Inc(y)
end;
end;
procedure TBitMatrix.setRow(y: Integer; row: IBitArray);
begin
Fbits := System.Copy(row.bits, (y * FrowSize), FrowSize);
end;
function TBitMatrix.ToBitmap(format: TBarcodeFormat; content: string): TBitmap;
begin
raise ENotImplemented.Create('Converting to bitmap is not implemented yet!');
end;
function TBitMatrix.ToString: string;
var
r, c: Integer;
begin
for r := 0 to Fheight - 1 do
begin
for c := 0 to Fwidth - 1 do
begin
if Matrix[r, c] then
Result := Result + '*'
else
Result := Result + '.';
end;
Result := Result + #13#10;
end;
end;
function TBitMatrix.ToBitmap: TBitmap;
begin
Result := ToBitmap(TBarcodeFormat.CODE_128, '')
end;
end.
@@ -0,0 +1,63 @@
{
* 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.Common.DetectorResult;
interface
uses
System.SysUtils,
ZXing.Common.BitMatrix,
ZXing.ResultPoint;
type
/// <summary> <p>Encapsulates the result of detecting a barcode in an image. This includes the raw
/// matrix of black/white pixels corresponding to the barcode, and possibly points of interest
/// in the image, like the location of finder patterns or corners of the barcode in the image.</p>
/// </summary>
TDetectorResult = class
private
Fbits: TBitmatrix;
Fpoints: TArray<IResultPoint>;
public
constructor Create(const bits: TBitmatrix; const points: TArray<IResultPoint>);
destructor Destroy; override;
property bits: TBitmatrix read Fbits;
property points: TArray<IResultPoint> read Fpoints;
end;
implementation
constructor TDetectorResult.Create(const bits: TBitmatrix;
const points: TArray<IResultPoint>);
begin
Fbits := bits;
Fpoints := points;
end;
destructor TDetectorResult.Destroy;
begin
FPoints := nil;
FBits.Free;
inherited;
end;
end.
@@ -0,0 +1,159 @@
{
* 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.
* Implemented by K. Gossens for Delphi
}
unit ZXing.Common.GridSampler;
interface
uses
System.SysUtils,
ZXing.Common.Bitmatrix,
ZXing.Common.PerspectiveTransform;
type
TGridSampler = class abstract
private
class var FgridSampler: TGridSampler;
//class constructor Create;
protected
constructor Create;
class function checkAndNudgePoints(image: TBitMatrix; points: TArray<Single>): boolean; static;
public
function sampleGrid(image: TBitMatrix; dimensionX: Integer;
dimensionY: Integer; transform: TPerspectiveTransform): TBitMatrix; overload; virtual;
function sampleGrid(image: TBitMatrix; dimensionX: Integer;
dimensionY: Integer; p1ToX: Single; p1ToY: Single; p2ToX: Single;
p2ToY: Single; p3ToX: Single; p3ToY: Single; p4ToX: Single; p4ToY: Single;
p1FromX: Single; p1FromY: Single; p2FromX: Single; p2FromY: Single;
p3FromX: Single; p3FromY: Single; p4FromX: Single; p4FromY: Single): TBitMatrix; overload; virtual; abstract;
procedure setGridSampler(newGridSampler: TGridSampler); //static;
class property Instance: TGridSampler read FgridSampler;
end;
implementation
{ TGridSampler }
constructor TGridSampler.Create;
begin
inherited Create;
end;
class function TGridSampler.checkAndNudgePoints(image: TBitMatrix;
points: TArray<Single>): boolean;
var
width,
height : Integer;
nudged : Boolean;
offset, maxOffset : Integer;
x, y : Single;
begin
width := image.Width;
height := image.Height;
nudged := true;
offset := 0;
maxOffset := Length(points) - 1; // points.length must be even
while ((offset < maxOffset) and nudged) do
begin
x := points[offset];
y := points[(offset + 1)];
if (((x < -1) or (x > width)) or ((y < -1) or (y > height))) then
begin
Result := false;
exit;
end;
nudged := false;
if (x = -1) then
begin
points[offset] := 0;
nudged := true;
end
else
if (x = width) then
begin
points[offset] := (width - 1);
nudged := true;
end;
if (y = -1) then
begin
points[(offset + 1)] := 0;
nudged := true;
end
else
if (y = height) then
begin
points[(offset + 1)] := (height - 1);
nudged := true;
end;
Inc(offset, 2)
end;
nudged := true;
offset := (Length(points) - 2);
while (((offset >= 0) and nudged)) do
begin
x := points[offset];
y := points[(offset + 1)];
if (((x < -1) or (x > width)) or ((y < -1) or (y > height))) then
begin
Result := false;
exit;
end;
nudged := false;
if (x = -1) then
begin
points[offset] := 0;
nudged := true;
end else
if (x = width) then
begin
points[offset] := (width - 1);
nudged := true;
end;
if (y = -1) then
begin
points[(offset + 1)] := 0;
nudged := true
end else
if (y = height) then
begin
points[(offset + 1)] := (height - 1);
nudged := true;
end;
dec(offset, 2)
end;
Result := true;
end;
function TGridSampler.sampleGrid(image: TBitMatrix; dimensionX: Integer;
dimensionY: Integer; transform: TPerspectiveTransform): TBitMatrix;
begin
raise ENotSupportedException.Create('Not yet supported');
end;
procedure TGridSampler.setGridSampler(newGridSampler: TGridSampler);
begin
if (newGridSampler = nil)
then
raise EArgumentException.Create('Missing arguments');
FgridSampler := newGridSampler;
end;
end.
@@ -0,0 +1,248 @@
{
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Original Author: Sean Owen
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.Common.PerspectiveTransform;
interface
uses
System.SysUtils;
type
/// <summary> <p>This class implements a perspective transform in two dimensions. Given four source and four
/// destination points, it will compute the transformation implied between them. The code is based
/// directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.</p>
/// </summary>
TPerspectiveTransform = class sealed
private
a11: Single;
a12: Single;
a13: Single;
a21: Single;
a22: Single;
a23: Single;
a31: Single;
a32: Single;
a33: Single;
function buildAdjoint(): TPerspectiveTransform;
function times(const other: TPerspectiveTransform): TPerspectiveTransform;
public
constructor Create(const a11, a21, a31, a12, a22, a32, a13,
a23, a33: Single);
class function quadrilateralToQuadrilateral(const x0, y0, x1, y1, x2, y2,
x3, y3, x0p, y0p, x1p, y1p, x2p, y2p,
x3p, y3p: Single): TPerspectiveTransform; static;
class function quadrilateralToSquare(x0: Single; y0: Single; x1: Single;
y1: Single; x2: Single; y2: Single; x3: Single; y3: Single)
: TPerspectiveTransform; static;
class function squareToQuadrilateral(const x0, y0, x1, y1, x2, y2,
x3, y3: Single): TPerspectiveTransform; static;
procedure transformPoints(var points: TArray<Single>); overload;
/// <summary>Convenience method, not optimized for performance. </summary>
procedure transformPoints(const xValues, yValues: TArray<Single>); overload;
end;
implementation
constructor TPerspectiveTransform.Create(const a11, a21, a31, a12, a22, a32,
a13, a23, a33: Single);
begin
Self.a11 := a11;
Self.a12 := a12;
Self.a13 := a13;
Self.a21 := a21;
Self.a22 := a22;
Self.a23 := a23;
Self.a31 := a31;
Self.a32 := a32;
Self.a33 := a33;
end;
class function TPerspectiveTransform.quadrilateralToQuadrilateral(const x0, y0,
x1, y1, x2, y2, x3, y3, x0p, y0p, x1p, y1p, x2p, y2p,
x3p, y3p: Single): TPerspectiveTransform;
var
qToS,
SToQ : TPerspectiveTransform;
begin
qToS := nil;
SToQ := nil;
try
qToS := TPerspectiveTransform.quadrilateralToSquare(x0, y0, x1, y1, x2,
y2, x3, y3);
SToQ := TPerspectiveTransform.squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p,
y2p, x3p, y3p);
Result := SToQ.times(qToS);
finally
//if (SToQ <> nil)
//then
SToQ.Free;
//if (qToS <> nil)
//then
qToS.Free;
end;
end;
class function TPerspectiveTransform.quadrilateralToSquare(x0: Single;
y0: Single; x1: Single; y1: Single; x2: Single; y2: Single; x3: Single;
y3: Single): TPerspectiveTransform;
var
SToQ: TPerspectiveTransform;
begin
SToQ := nil;
// Here, the adjoint serves as the inverse:
try
SToQ := TPerspectiveTransform.squareToQuadrilateral(x0, y0, x1, y1, x2,
y2, x3, y3);
Result := SToQ.buildAdjoint;
finally
//if (SToQ <> nil)
//then
SToQ.Free;
end;
end;
class function TPerspectiveTransform.squareToQuadrilateral(const x0, y0,
x1, y1, x2, y2, x3, y3: Single): TPerspectiveTransform;
var
dx1, dx2,
dx3, dy1,
dy2, dy3,
a13, a23,
denominator: Single;
begin
dx3 := (((x0 - x1) + x2) - x3);
dy3 := (((y0 - y1) + y2) - y3);
if ((dx3 = 0) and (dy3 = 0)) then
begin
Result := TPerspectiveTransform.Create((x1 - x0), (x2 - x1), x0, (y1 - y0),
(y2 - y1), y0, 0, 0, 1);
end else
begin
dx1 := (x1 - x2);
dx2 := (x3 - x2);
dy1 := (y1 - y2);
dy2 := (y3 - y2);
denominator := ((dx1 * dy2) - (dx2 * dy1));
if denominator = 0 then
begin
a13 := 0;
a23 := 0;
end
else
begin
a13 := (((dx3 * dy2) - (dx2 * dy3)) / denominator);
a23 := (((dx1 * dy3) - (dx3 * dy1)) / denominator);
end;
Result := TPerspectiveTransform.Create(((x1 - x0) + (a13 * x1)),
((x3 - x0) + (a23 * x3)), x0, ((y1 - y0) + (a13 * y1)),
((y3 - y0) + (a23 * y3)), y0, a13, a23, 1);
end;
end;
procedure TPerspectiveTransform.transformPoints(var points: TArray<Single>);
var
max, i: Integer;
x, y,
denominator: Single;
begin
a11 := Self.a11;
a12 := Self.a12;
a13 := Self.a13;
a21 := Self.a21;
a22 := Self.a22;
a23 := Self.a23;
a31 := Self.a31;
a32 := Self.a32;
a33 := Self.a33;
i := 0;
max := Length(points) - 1;
while ((i < max)) do
begin
x := points[i];
y := points[(i + 1)];
denominator := (((a13 * x) + (a23 * y)) + a33);
points[i] := ((((a11 * x) + (a21 * y)) + a31) / denominator);
points[(i + 1)] := ((((a12 * x) + (a22 * y)) + a32) / denominator);
Inc(i, 2);
end
end;
procedure TPerspectiveTransform.transformPoints(const xValues,
yValues: TArray<Single>);
var
n, i: Integer;
x, y, denominator: Single;
begin
n := Length(xValues);
for i := 0 to Pred(n) do
begin
x := xValues[i];
y := yValues[i];
denominator := (((self.a13 * x) + (self.a23 * y)) + self.a33);
xValues[i] := ((((self.a11 * x) + (self.a21 * y)) + self.a31) /
denominator);
yValues[i] := ((((self.a12 * x) + (self.a22 * y)) + self.a32) /
denominator);
end
end;
function TPerspectiveTransform.buildAdjoint(): TPerspectiveTransform;
begin
// Adjoint is the transpose of the cofactor matrix:
Result := TPerspectiveTransform.Create(a22 * a33 - a23 * a32,
a23 * a31 - a21 * a33,
a21 * a32 - a22 * a31,
a13 * a32 - a12 * a33,
a11 * a33 - a13 * a31,
a12 * a31 - a11 * a32,
a12 * a23 - a13 * a22,
a13 * a21 - a11 * a23,
a11 * a22 - a12 * a21);
end;
function TPerspectiveTransform.times(
const other: TPerspectiveTransform): TPerspectiveTransform;
begin
Result := TPerspectiveTransform.Create(a11 * other.a11 + a21 * other.a12 + a31 * other.a13,
a11 * other.a21 + a21 * other.a22 + a31 * other.a23,
a11 * other.a31 + a21 * other.a32 + a31 * other.a33,
a12 * other.a11 + a22 * other.a12 + a32 * other.a13,
a12 * other.a21 + a22 * other.a22 + a32 * other.a23,
a12 * other.a31 + a22 * other.a32 + a32 * other.a33,
a13 * other.a11 + a23 * other.a12 + a33 * other.a13,
a13 * other.a21 + a23 * other.a22 + a33 * other.a23,
a13 * other.a31 + a23 * other.a32 + a33 * other.a33);
end;
end.
@@ -0,0 +1,127 @@
{
* 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.
* Implemented by E. Spelt for Delphi
* Restructured by K. Gossens
}
unit ZXing.DecodeHintType;
interface
type
TDecodeHintType = (
/// <summary>
/// Unspecified, application-specific hint. Maps to an unspecified <see cref="System.Object" />.
/// </summary>
OTHER,
/// <summary>
/// Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
/// use <see cref="bool" /> = true.
/// </summary>
PURE_BARCODE,
/// <summary>
/// Image is known to be of one of a few possible formats.
/// Maps to a <see cref="System.Collections.ICollection" /> of <see cref="BarcodeFormat" />s.
/// </summary>
POSSIBLE_FORMATS,
/// <summary>
/// Spend more time to try to find a barcode; optimize for accuracy, not speed.
/// Doesn't matter what it maps to; use <see cref="bool" /> = true.
/// </summary>
TRY_HARDER,
/// <summary>
/// Specifies what character encoding to use when decoding, where applicable (type String)
/// </summary>
CHARACTER_SET,
/// <summary>
/// Allowed lengths of encoded data -- reject anything else. Maps to an int[].
/// </summary>
ALLOWED_LENGTHS,
/// <summary>
/// Assume Code 39 codes employ a check digit. Maps to <see cref="bool" />.
/// </summary>
ASSUME_CODE_39_CHECK_DIGIT,
/// <summary>
/// The caller needs to be notified via callback when a possible <see cref="TResultPoint" />
/// is found. Maps to a <see cref="TResultPointCallback" />.
/// </summary>
NEED_RESULT_POINT_CALLBACK,
/// <summary>
/// Assume MSI codes employ a check digit. Maps to <see cref="bool" />.
/// </summary>
ASSUME_MSI_CHECK_DIGIT,
/// <summary>
/// if Code39 could be detected try to use extended mode for full ASCII character set
/// Maps to <see cref="bool" />.
/// </summary>
USE_CODE_39_EXTENDED_MODE,
/// <summary>
/// Don't fail if a Code39 is detected but can't be decoded in extended mode.
/// Return the raw Code39 result instead. Maps to <see cref="bool" />.
/// </summary>
RELAXED_CODE_39_EXTENDED_MODE,
/// <summary>
/// 1D readers supporting rotation with TRY_HARDER enabled.
/// But BarcodeReader class can do auto-rotating for 1D and 2D codes.
/// Enabling that option prevents 1D readers doing double rotation.
/// BarcodeReader enables that option automatically if "global" auto-rotation is enabled.
/// Maps to <see cref="bool" />.
/// </summary>
TRY_HARDER_WITHOUT_ROTATION,
/// <summary>
/// Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
/// For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;
/// use <see cref="bool" />.
/// </summary>
ASSUME_GS1,
/// <summary>
/// If true, return the start and end digits in a Codabar barcode instead of stripping them. They
/// are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them
/// to not be. Doesn't matter what it maps to; use <see cref="bool" />.
/// </summary>
RETURN_CODABAR_START_END,
/// <summary>
/// Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this.
/// Maps to an <see cref="Array.int" /> of the allowed extension lengths, for example [2], [5], or [2, 5].
/// If it is optional to have an extension, do not set this hint. If this is set,
/// and a UPC or EAN barcode is found but an extension is not, then no result will be returned
/// at all.
/// </summary>
ALLOWED_EAN_EXTENSIONS,
/// <summary>
/// Allowes for inversion of an image.
/// Add the to invert the image
/// </summary>
ENABLE_INVERSION
);
implementation
end.
@@ -0,0 +1,90 @@
unit ZXing.DecoderResult;
{
* 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.
* Implemented by E. Spelt for Delphi
}
interface
uses SysUtils,
Generics.Collections,
ZXIng.ByteSegments;
type
TDecoderResult = class
private
function GetStructuredAppend: boolean;
public
ByteSegments: IByteSegments;
ECLevel: string;
Erasures: Integer;
ErrorsCorrected: Integer;
Other: TObject;
RawBytes: TArray<Byte>;
StructuredAppendParity: Integer;
StructuredAppendSequenceNumber: Integer;
Text: string;
constructor Create(RawBytes: TArray<Byte>; const Text: string;
ByteSegments: IByteSegments; ECLevel: string); overload;
constructor Create(RawBytes: TArray<Byte>; const Text: string;
ByteSegments: IByteSegments; ECLevel: string; saSequence: Integer;
saParity: Integer); overload;
destructor Destroy; override;
property StructuredAppend: boolean read GetStructuredAppend;
end;
implementation
{ TDecoderResult }
constructor TDecoderResult.Create(RawBytes: TArray<Byte>; const Text: String;
ByteSegments: IByteSegments; ECLevel: String);
begin
Self.Create(RawBytes, Text, ByteSegments, ECLevel, -1, -1);
end;
constructor TDecoderResult.Create(RawBytes: TArray<Byte>; const Text: string;
ByteSegments: IByteSegments; ECLevel: string;
saSequence, saParity: Integer);
begin
if ((RawBytes = nil) and (Text = ''))
then
raise EArgumentException.Create('Text or rawbytes cannot be nil or empty');
Self.RawBytes := RawBytes;
Self.Text := Text;
Self.ByteSegments := ByteSegments;
Self.ECLevel := ECLevel;
Self.StructuredAppendParity := saParity;
Self.StructuredAppendSequenceNumber := saSequence
end;
destructor TDecoderResult.Destroy;
begin
RawBytes := nil;
FreeAndNil(Other);
inherited;
end;
function TDecoderResult.GetStructuredAppend: boolean;
begin
result := (StructuredAppendParity >= 0) and
(StructuredAppendSequenceNumber >= 0);
end;
end.
@@ -0,0 +1,266 @@
{
* 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.DefaultGridSampler;
interface
uses
System.SysUtils,
System.Math,
ZXing.Common.BitMatrix,
ZXing.Common.PerspectiveTransform,
ZXing.Common.Detector.MathUtils;
type
// TODO(KG) Add GridSampler basis class
TDefaultGridSampler = class //(TGridSampler)
private
class var GridSampler: TDefaultGridSampler;
class function get_Instance(): TDefaultGridSampler; static;
protected
/// <summary> <p>Checks a set of points that have been transformed to sample points on an image against
/// the image's dimensions to see if the point are even within the image.</p>
///
/// <p>This method will actually "nudge" the endpoints back onto the image if they are found to be
/// barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
/// patterns in an image where the QR Code runs all the way to the image border.</p>
///
/// <p>For efficiency, the method will check points from either end of the line until one is found
/// to be within the image. Because the set of points are assumed to be linear, this is valid.</p>
///
/// </summary>
/// <param name="image">image into which the points should map
/// </param>
/// <param name="points">actual points in x1,y1,...,xn,yn form
/// </param>
class function checkAndNudgePoints(const image: TBitMatrix;
const points: TArray<Single>): boolean; static;
public
class function sampleGrid(const image: TBitMatrix;
const dimensionX, dimensionY: Integer;
const p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY,
p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX,
p4FromY: Single): TBitMatrix; overload; static;
class function sampleGrid(const image: TBitMatrix;
const dimensionX, dimensionY: Integer;
const transform: TPerspectiveTransform): TBitMatrix; overload; static;
class procedure setGridSampler(newGridSampler: TDefaultGridSampler); static;
// Properties
class property Instance: TDefaultGridSampler read get_Instance;
end;
implementation
{ TDefaultGridSampler }
class function TDefaultGridSampler.sampleGrid(const image: TBitMatrix;
const dimensionX, dimensionY: Integer; const p1ToX, p1ToY, p2ToX, p2ToY,
p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX,
p3FromY, p4FromX, p4FromY: Single): TBitMatrix;
var
transform: TPerspectiveTransform;
begin
transform := TPerspectiveTransform.quadrilateralToQuadrilateral(p1ToX, p1ToY,
p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX,
p2FromY, p3FromX, p3FromY, p4FromX, p4FromY);
try
Result := sampleGrid(image, dimensionX, dimensionY, transform);
finally
transform.free;
end;
end;
class function TDefaultGridSampler.sampleGrid(const image: TBitMatrix;
const dimensionX, dimensionY: Integer;
const transform: TPerspectiveTransform) : TBitMatrix;
var
bits: TBitMatrix;
points: TArray<Single>;
y, x, max: Integer;
iValue: Single;
begin
Result := nil;
if ((dimensionX <= 0) or
(dimensionY <= 0))
then
exit;
bits := TBitMatrix.Create(dimensionX, dimensionY);
points := TArray<Single>.Create();
SetLength(points, dimensionX shl 1);
try
for y := 0 to Pred(dimensionY) do
begin
max := Length(points) - 1;
iValue := (y + 0.5);
x := 0;
while ((x < max)) do
begin
points[x] := TMathUtils.Asr(x, 1) + 0.5;
points[(x + 1)] := iValue;
Inc(x, 2);
end;
transform.transformPoints(points);
// Quick check to see if points transformed to something inside the image;
// sufficient to check the endpoints
if (not checkAndNudgePoints(image, points)) then
begin
bits.Free;
exit;
end;
try
x := 0;
while (x < max) do
begin
bits[(TMathUtils.Asr(x, 1)), y] := image[Floor(points[x]), Floor(points[x + 1])];
Inc(x, 2);
end
except
// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
// transform gets "twisted" such that it maps a straight line of points to a set of points
// whose endpoints are in bounds, but others are not. There is probably some mathematical
// way to detect this about the transformation that I don't know yet.
// This results in an ugly runtime exception despite our clever checks above -- can't have
// that. We could check each point's coordinates but that feels duplicative. We settle for
// catching and wrapping ArrayIndexOutOfBoundsException.
exit;
end;
end;
finally
points := nil;
end;
Result := bits;
end;
class function TDefaultGridSampler.checkAndNudgePoints(const image: TBitMatrix;
const points: TArray<Single>): Boolean;
var
offset,
x, y,
width, height : Integer;
nudged : Boolean;
begin
width := image.width;
height := image.height;
// Check and nudge points from start until we see some that are OK:
nudged := true;
offset := 0;
while ((offset < (Length(points) - 1)) and nudged) do
begin
x := Floor(points[offset]);
y := Floor(points[(offset + 1)]);
if ((((x < -1) or (x > width)) or (y < -1)) or (y > height)) then
begin
Result := false;
exit
end;
nudged := false;
if (x = -1) then
begin
points[offset] := 0;
nudged := true
end
else if (x = width) then
begin
points[offset] := (width - 1);
nudged := true
end;
if (y = -1) then
begin
points[(offset + 1)] := 0;
nudged := true
end
else if (y = height) then
begin
points[(offset + 1)] := (height - 1);
nudged := true
end;
inc(offset, 2)
end;
// Check and nudge points from end:
nudged := true;
offset := (Length(points) - 2);
while ((offset >= 0) and nudged) do
begin
x := Floor(points[offset]);
y := Floor(points[(offset + 1)]);
if ((((x < -1) or (x > width)) or (y < -1)) or (y > height)) then
begin
Result := false;
exit
end;
nudged := false;
if (x = -1) then
begin
points[offset] := 0;
nudged := true
end
else if (x = width) then
begin
points[offset] := (width - 1);
nudged := true
end;
if (y = -1) then
begin
points[(offset + 1)] := 0;
nudged := true
end
else if (y = height) then
begin
points[(offset + 1)] := (height - 1);
nudged := true
end;
Dec(offset, 2);
end;
Result := true;
end;
class function TDefaultGridSampler.get_Instance: TDefaultGridSampler;
begin
GridSampler := TDefaultGridSampler.Create();
Result := GridSampler;
end;
class procedure TDefaultGridSampler.setGridSampler(newGridSampler
: TDefaultGridSampler);
begin
if (newGridSampler = nil) then
begin
raise EArgumentException.Create('Arguments in error');
end;
GridSampler := newGridSampler;
end;
end.
@@ -0,0 +1,43 @@
{
* 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.
* Implemented by E. Spelt for Delphi
* Restructured by K. Gossens
}
unit ZXing.EncodeHintType;
interface
type
TEncodeHintType = (AZTEC_LAYERS = 15,
CHARACTER_SET = 4,
CODE128_FORCE_CODESET_B = 13,
DATA_MATRIX_DEFAULT_ENCODATION = 14,
DATA_MATRIX_SHAPE = 10,
DISABLE_ECI = 9,
ERROR_CORRECTION = 3,
HEIGHT = 1,
MARGIN = 5,
MAX_SIZE = 12,
MIN_SIZE = 11,
PDF417_COMPACTION = 7,
PDF417_DIMENSIONS = 8,
PURE_BARCODE = 2,
WIDTH = 0);
implementation
end.
@@ -0,0 +1,62 @@
unit ZXing.Helpers;
{
* 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.
* Implemented by E. Spelt for Delphi
}
interface
type
TArray = class
class function Clone(original: TArray<Integer>): TArray<Integer>; static;
class function CopyInSameArray(const Input: TArray<Integer>;
StartIndex: Integer; Len: Integer): TArray<Integer>; static;
end;
implementation
class function TArray.Clone(original: TArray<Integer>): TArray<Integer>;
var
i: Integer;
l: SmallInt;
begin
l := Length(original);
Result := TArray<Integer>.Create();
SetLength(Result, l);
for i := 0 to l - 1 do
begin
Result[i] := original[i];
end;
end;
class function TArray.CopyInSameArray(const Input: TArray<Integer>;
StartIndex: Integer; Len: Integer): TArray<Integer>;
var
i, y: Integer;
begin
Result := TArray.Clone(Input);
y := 0;
for i := StartIndex to (StartIndex + Len -1) do
begin
Result[y] := Input[i];
inc(y);
end;
end;
end.
@@ -0,0 +1,350 @@
{
* 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 Authors: Sean Owen and dswitkin@google.com (Daniel Switkin)
* Ported from ZXING Java Source: www.Redivivus.in (suraj.supekar@redivivus.in)
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.MultiFormatReader;
interface
uses
System.SysUtils,
System.Rtti,
System.Generics.Collections,
System.RegularExpressions,
ZXing.ReadResult,
ZXing.Reader,
ZXing.DecodeHintType,
ZXing.BinaryBitmap,
ZXing.BarcodeFormat,
ZXing.ResultPoint,
// 1D Barcodes
ZXing.OneD.OneDReader,
ZXing.OneD.Code128Reader,
ZXing.OneD.Code93Reader,
ZXing.OneD.ITFReader,
ZXing.OneD.EAN13Reader,
ZXing.OneD.EAN8Reader,
ZXing.OneD.UPCAReader,
ZXing.OneD.UPCEReader,
ZXing.OneD.Code39Reader,
// 2D Codes
ZXing.QrCode.QRCodeReader,
ZXing.Datamatrix.DataMatrixReader;
/// <summary>
/// MultiFormatReader is a convenience class and the main entry point into the library for most uses.
/// By default it attempts to decode all barcode formats that the library supports. Optionally, you
/// can provide a hints object to request different behavior, for example only decoding QR codes.
/// </summary>
/// <author>Sean Owen</author>
/// <author>dswitkin@google.com (Daniel Switkin)</author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source</author>
type
TMultiFormatReader = class(TInterfacedObject, IReader)
private
FHints: TDictionary<TDecodeHintType, TObject>;
readers: TList<IReader>;
function DecodeInternal(image: TBinaryBitmap): TReadResult;
procedure Set_Hints(const Value: TDictionary<TDecodeHintType, TObject>);
function Get_Hints: TDictionary<TDecodeHintType, TObject>;
/// <summary> This version of decode honors the intent of Reader.decode(BinaryBitmap) in that it
/// passes null as a hint to the decoders. However, that makes it inefficient to call repeatedly.
/// Use setHints() followed by decodeWithState() for continuous scan applications.
///
/// </summary>
/// <param name="image">The pixel data to decode
/// </param>
/// <returns> The contents of the image
/// </returns>
/// <throws> ReaderException Any errors which occurred </throws>
public
function decode(const image: TBinaryBitmap): TReadResult; overload;
function decode(const image: TBinaryBitmap; WithHints: Boolean)
: TReadResult; overload;
/// <summary> Decode an image using the hints provided. Does not honor existing state.
///
/// </summary>
/// <param name="image">The pixel data to decode
/// </param>
/// <param name="hints">The hints to use, clearing the previous state.
/// </param>
/// <returns> The contents of the image
/// </returns>
/// <throws> ReaderException Any errors which occurred </throws>
function decode(const image: TBinaryBitmap;
hints: TDictionary<TDecodeHintType, TObject>): TReadResult; overload;
/// <summary> Decode an image using the state set up by calling setHints() previously. Continuous scan
/// clients will get a <b>large</b> speed increase by using this instead of decode().
///
/// </summary>
/// <param name="image">The pixel data to decode
/// </param>
/// <returns> The contents of the image
/// </returns>
/// <throws> ReaderException Any errors which occurred </throws>
function DecodeWithState(image: TBinaryBitmap): TReadResult;
destructor Destroy; override;
/// <summary> This method adds state to the MultiFormatReader. By setting the hints once, subsequent calls
/// to decodeWithState(image) can reuse the same set of readers without reallocating memory. This
/// is important for performance in continuous scan clients.
///
/// </summary>
/// <param name="hints">The set of hints to use for subsequent calls to decode(image)
/// </param>
property hints: TDictionary<TDecodeHintType, TObject> read Get_Hints
write Set_Hints;
procedure Reset;
procedure FreeReaders();
end;
implementation
function TMultiFormatReader.decode(const image: TBinaryBitmap): TReadResult;
begin
hints := nil;
result := DecodeInternal(image)
end;
function TMultiFormatReader.decode(const image: TBinaryBitmap;
WithHints: Boolean): TReadResult;
begin
result := DecodeInternal(image)
end;
function TMultiFormatReader.decode(const image: TBinaryBitmap;
hints: TDictionary<TDecodeHintType, TObject>): TReadResult;
begin
FHints := hints;
result := DecodeInternal(image)
end;
function TMultiFormatReader.DecodeWithState(image: TBinaryBitmap): TReadResult;
begin
// Make sure to set up the default state so we don't crash
if readers = nil then
begin
hints := nil
end;
result := DecodeInternal(image);
end;
destructor TMultiFormatReader.Destroy;
begin
FreeReaders;
inherited;
end;
procedure TMultiFormatReader.FreeReaders;
begin
if readers <> nil then
readers.Clear();
readers.Free;
readers := nil;
end;
function TMultiFormatReader.Get_Hints: TDictionary<TDecodeHintType, TObject>;
begin
result := FHints;
end;
procedure TMultiFormatReader.Set_Hints(const Value: TDictionary<TDecodeHintType,
TObject>);
var
useCode39CheckDigit, useCode39ExtendedMode: Boolean;
formats: TList<TBarcodeFormat>;
begin
FHints := Value;
// tryHarder := (Value <> nil) and
// (Value.ContainsKey(ZXing.DecodeHintType.TRY_HARDER));
if ((Value = nil) or
(not Value.ContainsKey(ZXing.DecodeHintType.POSSIBLE_FORMATS))) then
begin
formats := nil;
end
else
begin
formats := Value[ZXing.DecodeHintType.POSSIBLE_FORMATS]
as TList<TBarcodeFormat>
end;
// add readers from the hints
readers := TList<IReader>.Create;
if formats <> nil then
begin
// 1D readers
if (formats.Contains(TBarcodeFormat.CODE_128)) then
begin
readers.Add(TCode128Reader.Create())
end;
if (formats.Contains(TBarcodeFormat.CODE_93)) then
begin
readers.Add(TCode93Reader.Create())
end;
if (formats.Contains(TBarcodeFormat.ITF)) then
begin
readers.Add(TITFReader.Create())
end;
// 2D readers
if (formats.Contains(TBarcodeFormat.QR_CODE)) then
begin
readers.Add(TQRCodeReader.Create())
end;
if formats.Contains(TBarcodeFormat.DATA_MATRIX) then
readers.Add(TDataMatrixReader.Create);
if (formats.Contains(TBarcodeFormat.EAN_13)) then
readers.Add(TEAN13Reader.Create());
if (formats.Contains(TBarcodeFormat.EAN_8)) then
readers.Add(TEAN8Reader.Create());
if (formats.Contains(TBarcodeFormat.UPC_A)) then
readers.Add(TUPCAReader.Create());
if (formats.Contains(TBarcodeFormat.UPC_E)) then
readers.Add(TUPCEReader.Create());
if (formats.Contains(TBarcodeFormat.CODE_39)) then
begin
useCode39CheckDigit := hints.ContainsKey
(TDecodeHintType.ASSUME_CODE_39_CHECK_DIGIT) and
hints.ContainsKey(TDecodeHintType.ASSUME_CODE_39_CHECK_DIGIT);
useCode39ExtendedMode := hints.ContainsKey
(TDecodeHintType.USE_CODE_39_EXTENDED_MODE) and
hints.ContainsKey(TDecodeHintType.USE_CODE_39_EXTENDED_MODE);
readers.Add(TCode39Reader.Create(useCode39CheckDigit,
useCode39ExtendedMode));
end;
end;
if (readers.Count = 0) then // must be auto, add them all
begin
// 1D readers
readers.Add(TCode128Reader.Create());
readers.Add(TUPCAReader.Create());
readers.Add(TUPCEReader.Create());
readers.Add(TEAN13Reader.Create());
readers.Add(TEAN8Reader.Create());
readers.Add(TCode93Reader.Create());
readers.Add(TITFReader.Create());
useCode39CheckDigit := hints.ContainsKey
(TDecodeHintType.ASSUME_CODE_39_CHECK_DIGIT) and hints.ContainsKey
(TDecodeHintType.ASSUME_CODE_39_CHECK_DIGIT);
useCode39ExtendedMode := hints.ContainsKey
(TDecodeHintType.USE_CODE_39_EXTENDED_MODE) and hints.ContainsKey
(TDecodeHintType.USE_CODE_39_EXTENDED_MODE);
readers.Add(TCode39Reader.Create(useCode39CheckDigit,
useCode39ExtendedMode));
// 2D readers
readers.Add(TQRCodeReader.Create());
readers.Add(TDataMatrixReader.Create)
end;
end;
procedure TMultiFormatReader.Reset;
var
Reader: IReader;
begin
if readers <> nil then
begin
for Reader in readers do
begin
Reader.Reset();
end
end
end;
function TMultiFormatReader.DecodeInternal(image: TBinaryBitmap): TReadResult;
var
rpCallBack: TResultPointCallback;
i: integer;
Reader: IReader;
begin
result := nil;
if (readers = nil) then
begin
Exit;
end;
rpCallBack := nil;
if ((FHints <> nil) and
(FHints.ContainsKey(ZXing.DecodeHintType.NEED_RESULT_POINT_CALLBACK))) then
begin
// rpCallBack := FHints[DecodeHintType.NEED_RESULT_POINT_CALLBACK]
// as TResultPointCallback(nil);
end;
for i := 0 to readers.Count - 1 do
begin
Reader := readers[i];
Reader.Reset();
result := Reader.decode(image, FHints);
if result <> nil then
begin
// found a barcode, pushing the successful reader up front
// I assume that the same type of barcode is read multiple times
// so the reordering of the readers list should speed up the next reading
// a little bit
readers.Delete(i);
readers.Insert(0, Reader);
Exit;
end;
// if rpCallBack <> nil then
// rpCallBack(nil)
end;
end;
end.
@@ -0,0 +1,344 @@
{
* 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 E. Spelt and K. Gossens
}
unit ZXing.ReadResult;
interface
uses
System.SysUtils,
Generics.Collections,
ZXing.ResultPoint,
ZXing.ResultMetadataType,
ZXing.BarcodeFormat,
ZXing.ByteSegments;
type
IMetaData = Interface
['{27AEB2C5-D775-44C6-9816-3E69025CA57C}']
end;
IStringMetadata = interface(IMetaData)
['{8073F897-30AD-49B7-AE0B-B187D3373163}']
function Value:String;
end;
IIntegerMetadata = interface(IMetaData)
['{CA60A23A-57ED-4C8E-9886-4F23B832A90C}']
function Value:Integer;
end;
IByteSegmentsMetadata = interface(IMetaData)
['{CA60A23A-57ED-4C8E-9886-4F23B832A90C}']
function Value:IByteSegments;
end;
/// <summary>
/// represents the metadata contained in a TReadResult and provides the
/// helping functions needed to create them
/// </summary>
TResultMetaData = class(TDictionary<TResultMetadataType, IMetaData>)
public
constructor Create;
class function CreateStringMetadata(const Value:string) :IStringMetaData;
class function CreateIntegerMetadata(const Value:integer) :IIntegerMetaData;
class function CreateByteSegmentsMetadata(const Value:IByteSegments) :IByteSegmentsMetadata;
end;
TReadResult = class
private
FText: string;
FTimeStamp: TDateTime;
FResultMetadata: TResultMetaData;
FRawBytes: TArray<Byte>;
FResultPoints: TArray<IResultPoint>;
FFormat: TBarcodeFormat;
procedure SetText(const AValue: String);
procedure SetMetaData( const Value: TResultMetadata);
public
/// <summary>
/// Initializes a new instance of the <see cref="TReadResult"/> class.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="rawBytes">The raw bytes.</param>
/// <param name="resultPoints">The result points.</param>
/// <param name="format">The format.</param>
constructor Create(const text: string; const rawBytes: TArray<Byte>;
const resultPoints: TArray<IResultPoint>;
const format: TBarcodeFormat); overload;
/// <summary>
/// Initializes a new instance of the <see cref="TReadResult"/> class.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="rawBytes">The raw bytes.</param>
/// <param name="resultPoints">The result points.</param>
/// <param name="format">The format.</param>
/// <param name="timestamp">The timestamp.</param>
constructor Create(const text: string; const rawBytes: TArray<Byte>;
const resultPoints: TArray<IResultPoint>; const format: TBarcodeFormat;
const timeStamp: TDateTime); overload;
destructor Destroy; override;
function ToString: String; override;
// we need a "write" procedure accessor because we need to properly deallocate
// existing instance if we overwrite it with another one
property ResultMetaData: TResultMetadata
read FResultMetadata write SetMetaData;
/// <summary>
/// Adds one metadata to the result
/// </summary>
/// <param name="type">The type.</param>
/// <param name="value">The value.</param>
procedure putMetadata(const ResultMetadataType: TResultMetadataType;
const Value: IMetaData);
/// <summary>
/// Adds a list of metadata to the result
/// </summary>
/// <param name="metadata">The metadata.</param>
procedure putAllMetaData(const metaData: TResultMetadata);
/// <summary>
/// Adds the result points.
/// </summary>
/// <param name="newPoints">The new points.</param>
procedure addResultPoints(const newPoints: TArray<IResultPoint>);
/// <returns>raw text encoded by the barcode, if applicable, otherwise <code>null</code></returns>
property text: String read FText write SetText;
/// <returns>raw bytes encoded by the barcode, if applicable, otherwise <code>null</code></returns>
property rawBytes: TArray<Byte> read FRawBytes write FRawBytes;
/// <returns>
/// points related to the barcode in the image. These are typically points
/// identifying finder patterns or the corners of the barcode. The exact meaning is
/// specific to the type of barcode that was decoded.
/// </returns>
property resultPoints: TArray<IResultPoint> read FResultPoints
write FResultPoints;
/// <returns>{@link TBarcodeFormat} representing the format of the barcode that was decoded</returns>
property BarcodeFormat: TBarcodeFormat read FFormat write FFormat;
/// <summary>
/// Gets the timestamp.
/// </summary>
property timeStamp: TDateTime read FTimeStamp;
end;
implementation
{$REGION 'IMetaData implementations'}
{ TStringMetadata }
type
TStringMetadata = class(TInterfacedObject,IMetaData,IStringMetadata)
strict private
FValue: String;
function Value: String;
private
constructor Create(const AValue: String);
end;
function TStringMetadata.Value: String;
begin
result := FValue
end;
constructor TStringMetadata.Create(const AValue: string);
begin
inherited Create;
FValue := AValue;
end;
{ TIntegerMetadata }
type
TIntegerMetadata = class(TInterfacedObject,IMetaData,IIntegerMetadata)
strict private
FValue: Integer;
function Value: Integer;
private
constructor Create(const AValue: Integer);
end;
function TIntegerMetadata.Value: Integer;
begin
result := FValue
end;
constructor TIntegerMetadata.Create(const AValue: Integer);
begin
inherited Create;
FValue := AValue;
end;
{ TByteSegmentsMetadata }
type
TByteSegmentsMetadata = class(TInterfacedObject,IMetaData,IByteSegmentsMetadata)
strict private
FValue: IByteSegments;
function Value: IByteSegments;
private
constructor Create(const AValue: IByteSegments);
end;
function TByteSegmentsMetadata.Value: IByteSegments;
begin
result := FValue
end;
constructor TByteSegmentsMetadata.Create(const AValue: IByteSegments);
begin
inherited Create;
FValue := AValue;
end;
{ TResultMetadata }
constructor TResultMetaData.Create;
begin
inherited Create;
end;
class function TResultMetadata.CreateStringMetadata(const Value:string) :IStringMetaData;
begin
result := TStringMetadata.Create(Value);
end;
class function TResultMetaData.CreateByteSegmentsMetadata(
const Value: IByteSegments): IByteSegmentsMetadata;
begin
result := TByteSegmentsMetadata.Create(value);
end;
class function TResultMetadata.CreateIntegerMetadata(const Value:integer) :IIntegerMetaData;
begin
result := TIntegerMetadata.Create(Value);
end;
{$ENDREGION 'IMetaData implementations'}
{ TReadResult }
constructor TReadResult.Create(const text: String; const rawBytes: TArray<Byte>;
const resultPoints: TArray<IResultPoint>; const format: TBarcodeFormat);
begin
Self.Create(text, rawBytes, resultPoints, format, Now);
end;
constructor TReadResult.Create(const text: String; const rawBytes: TArray<Byte>;
const resultPoints: TArray<IResultPoint>; const format: TBarcodeFormat;
const timeStamp: TDateTime);
begin
if ((text = '') and (rawBytes = nil)) then
raise EArgumentException.Create('Text and bytes are null.');
SetText(text);
FRawBytes := rawBytes;
FResultPoints := resultPoints;
FResultMetadata := nil;
FFormat := format;
FResultMetadata := nil;
FTimeStamp := timeStamp;
end;
destructor TReadResult.Destroy;
var i:Integer;
begin
for I := Low(FResultPoints) to High(FResultPoints) do
begin
FResultPoints[i] := nil;
end;
FResultPoints := nil;
FRawBytes := nil;
if FResultMetadata <> nil then
begin
FResultMetadata.Clear;
FreeAndNil(FResultMetadata);
end;
inherited;
end;
// Added (KG): UTF-8 compatiblity
procedure TReadResult.SetMetaData(const Value: TResultMetadata);
begin
if value = FResultMetadata then
exit; // no change
if FResultMetadata<>nil then
FResultMetadata.Free; // we need to free existing object instance, for old-gen compilers
FResultMetadata := Value;
end;
procedure TReadResult.SetText(const AValue: String);
begin
FText := AValue;
end;
procedure TReadResult.putMetadata(const ResultMetadataType: TResultMetadataType;
const Value: IMetaData);
begin
if (FResultMetadata = nil) then
FResultMetadata := TResultMetadata.Create();
FResultMetadata.AddOrSetValue(ResultMetadataType, Value);
end;
procedure TReadResult.putAllMetaData(const metaData: TResultMetadata);
var
key: TResultMetadataType;
begin
if (metaData <> nil) then
begin
if (FResultMetadata = nil) then
FResultMetadata := TResultMetadata.Create();
for key in metaData.Keys do
FResultMetadata.AddOrSetValue(key, metaData[key]);
end;
end;
procedure TReadResult.addResultPoints(const newPoints: TArray<IResultPoint>);
begin
FResultPoints := FResultPoints + newPoints;
end;
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
function TReadResult.ToString(): String;
begin
if (text = '') then
Result := '[' + IntToStr(Length(rawBytes)) + ' bytes]'
else
Result := text;
end;
end.
+71
View File
@@ -0,0 +1,71 @@
{
* 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 Authors: Sean Owen and dswitkin@google.com (Daniel Switkin)
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.Reader;
interface
uses
System.SysUtils,
System.Generics.Collections,
ZXing.BinaryBitmap,
ZXing.ReadResult,
ZXing.DecodeHintType;
type
/// <summary>
/// Implementations of this interface can decode an image of a barcode in some format into
/// the String it encodes. For example, <see cref="ZXing.QrCode.QRCodeReader" /> can
/// decode a QR code. The decoder may optionally receive hints from the caller which may help
/// it decode more quickly or accurately.
///
/// See <see cref="MultiFormatReader" />, which attempts to determine what barcode
/// format is present within the image as well, and then decodes it accordingly.
/// </summary>
IReader = interface
/// <summary>
/// Locates and decodes a barcode in some format within an image.
/// </summary>
/// <param name="image">image of barcode to decode</param>
/// <returns>String which the barcode encodes</returns>
function decode(const image: TBinaryBitmap): TReadResult; overload;
/// <summary> Locates and decodes a barcode in some format within an image. This method also accepts
/// hints, each possibly associated to some data, which may help the implementation decode.
/// </summary>
/// <param name="image">image of barcode to decode</param>
/// <param name="hints">passed as a <see cref="IDictionary{TKey, TValue}" /> from <see cref="DecodeHintType" />
/// to arbitrary data. The
/// meaning of the data depends upon the hint type. The implementation may or may not do
/// anything with these hints.
/// </param>
/// <returns>String which the barcode encodes</returns>
function decode(const image: TBinaryBitmap;
hints: TDictionary<TDecodeHintType, TObject>): TReadResult; overload;
/// <summary>
/// Resets any internal state the implementation has after a decode, to prepare it
/// for reuse.
/// </summary>
procedure Reset();
end;
implementation
end.
@@ -0,0 +1,108 @@
{
* 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.
* Implemented by E. Spelt for Delphi
}
unit ZXing.ResultMetadataType;
interface
type
/// <summary>
/// Represents some type of metadata about the result of the decoding that the decoder
/// wishes to communicate back to the caller.
/// </summary>
/// <author>Sean Owen</author>
TResultMetadataType = (
/// <summary>
/// Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
/// </summary>
OTHER,
/// <summary>
/// Denotes the likely approximate orientation of the barcode in the image. This value
/// is given as degrees rotated clockwise from the normal, upright orientation.
/// For example a 1D barcode which was found by reading top-to-bottom would be
/// said to have orientation "90". This key maps to an {@link Integer} whose
/// value is in the range [0,360).
/// </summary>
ORIENTATION,
/// <summary>
/// <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
/// which is sometimes used to encode binary data. While {@link Result} makes available
/// the complete raw bytes in the barcode for these formats, it does not offer the bytes
/// from the byte segments alone.</p>
/// <p>This maps to a {@link java.util.List} of byte arrays corresponding to the
/// raw bytes in the byte segments in the barcode, in order.</p>
/// </summary>
BYTE_SEGMENTS,
/// <summary>
/// Error correction level used, if applicable. The value type depends on the
/// format, but is typically a String.
/// </summary>
ERROR_CORRECTION_LEVEL,
/// <summary>
/// For some periodicals, indicates the issue number as an {@link Integer}.
/// </summary>
ISSUE_NUMBER,
/// <summary>
/// For some products, indicates the suggested retail price in the barcode as a
/// formatted {@link String}.
/// </summary>
SUGGESTED_PRICE,
/// <summary>
/// For some products, the possible country of manufacture as a {@link String} denoting the
/// ISO country code. Some map to multiple possible countries, like "US/CA".
/// </summary>
POSSIBLE_COUNTRY,
/// <summary>
/// For some products, the extension text
/// </summary>
UPC_EAN_EXTENSION,
/// <summary>
/// If the code format supports structured append and
/// the current scanned code is part of one then the
/// sequence number is given with it.
/// </summary>
STRUCTURED_APPEND_SEQUENCE,
/// <summary>
/// If the code format supports structured append and
/// the current scanned code is part of one then the
/// parity is given with it.
/// </summary>
STRUCTURED_APPEND_PARITY,
/// <summary>
/// PDF417-specific metadata
/// </summary>
PDF417_EXTRA_METADATA,
/// <summary>
/// Aztec-specific metadata
/// </summary>
AZTEC_EXTRA_METADATA);
implementation
end.
@@ -0,0 +1,206 @@
{
* 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: Sean Owen
* Delphi Implementation by E. Spelt and K. Gossens
}
unit ZXing.ResultPoint;
interface
uses
System.SysUtils,
ZXing.Common.Detector.MathUtils;
type
/// <summary>
/// To mimic "garbage collection" (or ARC) with old-gen compilers we have to use
/// interfaces. IResultPoint is the interface that maps TResultPoint which is now
/// implemented as a TInterfacedObject descendant which supports automatic deallocation
/// based on reference counting of interface variables.
/// See ZXing.ResultPointImplementation to see the actual implementation of this interface.
/// since we are using automatic reference counting, we don't need the Clone method any more.
/// </summary>
IResultPoint = interface
['{1E60EDAB-0387-440C-9D8E-4E0CC772C820}']
// property accessors, for interfaces, MUST be functions or procedures (interfaces cannot contain data)
procedure SetX(const AValue: Single);
procedure SetY(const AValue: Single);
function GetX: Single;
function GetY: Single;
function Equals(other: TObject): Boolean;
function GetHashCode(): Integer;
function ToString(): String;
property x: Single read GetX write SetX;
property y: Single read GetY write SetY;
end;
/// <summary>
/// in order to implement TResultPoint as an interface all static methods
/// have been moved in this static class.
/// this class contains also the CreateResultPoint method that must be used
/// in place of the actual class constructor
/// </summary>
TResultPointHelpers = class
protected
/// <summary>
/// Returns the z component of the cross product between vectors BC and BA.
/// </summary>
class function crossProductZ(const pointA, pointB, pointC: IResultPoint): Single; static;
public
/// <summary>
/// Initializes a new instance of <see cref="TResultPoint"/> and returns
/// its <see cref="IResultPoint"/> interface
/// </summary>
class function CreateResultPoint:IResultPoint; overload;
/// <summary>
/// Initializes a new instance of <see cref="TResultPoint"/> and returns
/// its <see cref="IResultPoint"/> interface
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
class function CreateResultPoint(const pX, pY: Single):IResultPoint; overload;
/// <summary>
/// Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC and
/// BC is less than AC and the angle between BC and BA is less than 180 degrees.
/// </summary>
/// <param name="patterns">array of three <see cref="IResultPoint" /> to order</param>
class procedure orderBestPatterns(const patterns : TArray<IResultPoint>); static;
/// <summary>
/// calculates the distance between two points
/// </summary>
/// <param name="pattern1">first pattern</param>
/// <param name="pattern2">second pattern</param>
/// <returns>
/// distance between two points
/// </returns>
class function distance(const pattern1, pattern2: IResultPoint) : Single; static;
end;
/// <summary> Callback which is invoked when a possible result point (significant
/// point in the barcode image such as a corner) is found.
///
/// </summary>
/// <seealso cref="TDecodeHintType.NEED_RESULT_POINT_CALLBACK">
/// </seealso>
TResultPointCallback = procedure(const point: IResultPoint) of object;
TResultPointEventObject = class(TObject)
private
FResultPointCallback: TResultPointCallback;
public
constructor Create(const AEvent: TResultPointCallback);
property Event: TResultPointCallback read FResultPointCallback;
end;
implementation
uses ZXing.ResultPointImplementation;
class function TResultPointHelpers.CreateResultPoint:IResultPoint;
begin
result := ZXing.ResultPointImplementation.NewResultPoint;
end;
class function TResultPointHelpers.CreateResultPoint(const pX, pY: Single):IResultPoint;
begin
result := ZXing.ResultPointImplementation.NewResultPoint(px,py);
end;
class function TResultPointHelpers.crossProductZ(const pointA, pointB,
pointC: IResultPoint): Single;
var
bX, bY: Single;
begin
bX := pointB.x;
bY := pointB.y;
Result := ((pointC.x - bX) * (pointA.y - bY)) -
((pointC.y - bY) * (pointA.x - bX));
end;
class function TResultPointHelpers.distance(const pattern1,
pattern2: IResultPoint): Single;
begin
Result := TMathUtils.distance(pattern1.x, pattern1.y, pattern2.x, pattern2.y);
end;
class procedure TResultPointHelpers.orderBestPatterns(const patterns
: TArray<IResultPoint>);
var
zeroOneDistance, oneTwoDistance, zeroTwoDistance: Single;
pointA, pointB, pointC, temp: IResultPoint;
begin
// Find distances between pattern centers
zeroOneDistance := distance(patterns[0], patterns[1]);
oneTwoDistance := distance(patterns[1], patterns[2]);
zeroTwoDistance := distance(patterns[0], patterns[2]);
// Assume one closest to other two is B; A and C will just be guesses at first
if ((oneTwoDistance >= zeroOneDistance) and
(oneTwoDistance >= zeroTwoDistance)) then
begin
pointB := patterns[0];
pointA := patterns[1];
pointC := patterns[2];
end
else if ((zeroTwoDistance >= oneTwoDistance) and
(zeroTwoDistance >= zeroOneDistance)) then
begin
pointB := patterns[1];
pointA := patterns[0];
pointC := patterns[2];
end
else
begin
pointB := patterns[2];
pointA := patterns[0];
pointC := patterns[1];
end;
// Use cross product to figure out whether A and C are correct or flipped.
// This asks whether BC x BA has a positive z component, which is the arrangement
// we want for A, B, C. If it's negative, then we've got it flipped around and
// should swap A and C.
if (crossProductZ(pointA, pointB, pointC) < 0) then
begin
temp := pointA;
pointA := pointC;
pointC := temp;
end;
patterns[0] := pointA;
patterns[1] := pointB;
patterns[2] := pointC;
end;
{ TResultPointEventObject }
constructor TResultPointEventObject.Create(const AEvent: TResultPointCallback);
begin
Self.FResultPointCallback := AEvent;
end;
end.
@@ -0,0 +1,168 @@
unit ZXing.ResultPointImplementation;
interface
uses system.SysUtils,
ZXing.ResultPoint;
type
/// <summary>
/// Encapsulates a point of interest in an image containing a barcode. Typically, this
/// would be the location of a finder pattern or the corner of the barcode, for example.
/// this class is meant to be used only indirectly, via the IResultPoint reference-counted interface
// it implements. It is exposed in the interface section just to allow the declaration of its derived classes
/// </summary>
TResultPoint = class(TInterfacedObject,IResultPoint)
private type
TSingleArray = array [0 .. Pred(SizeOf(Single))] of Byte;
var
Fx, Fy: Single;
bytesX, bytesY: TSingleArray;
FToString: String;
procedure SetX(const AValue: Single);
procedure SetY(const AValue: Single);
function GetX: Single;
function GetY: Single;
public
/// <summary>
/// Initializes a new instance of the <see cref="TResultPoint"/> class.
/// </summary>
constructor Create; overload;
/// <summary>
/// Initializes a new instance of the <see cref="TResultPoint"/> class.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
constructor Create(const pX, pY: Single); overload;
destructor Destroy; override;
/// <summary>
/// Determines whether the specified <see cref="System.TObject"/> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="System.TObject"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.TObject"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
function Equals(other: TObject): Boolean; override;
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
function GetHashCode(): Integer; override;
/// <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;
property x: Single read GetX write SetX;
property y: Single read GetY write SetY;
end;
// these are the helping functions that actually create a new instance.
// The actual constructor cannot be called outside of this unit
function NewResultPoint:IResultPoint; overload;
function NewResultPoint(const pX, pY: Single):IResultPoint; overload;
implementation
function NewResultPoint:IResultPoint;
begin
result := TResultPoint.Create;
end;
function NewResultPoint(const pX, pY: Single):IResultPoint;
begin
result := TResultPoint.Create(px,py);
end;
{ TResultPoint }
constructor TResultPoint.Create;
begin
inherited;
end;
constructor TResultPoint.Create(const pX, pY: Single);
begin
inherited Create;
Fx := pX;
Fy := pY;
bytesX := TSingleArray(pX);
bytesY := TSingleArray(pY);
end;
destructor TResultPoint.Destroy;
var
n: Single;
begin
n := 0;
bytesX := TSingleArray(n);
bytesY := TSingleArray(n);
inherited;
end;
procedure TResultPoint.SetX(const AValue: Single);
begin
if (AValue <> Fx) then
begin
Fx := AValue;
bytesX := TSingleArray(Fx);
end;
end;
procedure TResultPoint.SetY(const AValue: Single);
begin
if (AValue <> Fy) then
begin
Fy := AValue;
bytesY := TSingleArray(Fy);
end;
end;
function TResultPoint.GetX: Single;
begin
result := Fx;
end;
function TResultPoint.GetY: Single;
begin
result := Fy;
end;
function TResultPoint.Equals(other: TObject): Boolean;
var
otherPoint: TResultPoint;
begin
otherPoint := other as TResultPoint;
if (otherPoint = nil) then
Result := false
else
Result := ((otherPoint.x = Fx) and (otherPoint.y = Fy));
end;
function TResultPoint.GetHashCode: Integer;
begin
Result := 31 * ((bytesX[0] shl 24) + (bytesX[1] shl 16) + (bytesX[2] shl 8) +
bytesX[3]) + (bytesY[0] shl 24) + (bytesY[1] shl 16) + (bytesY[2] shl 8) +
bytesY[3];
end;
function TResultPoint.ToString: String;
begin
if (FToString = '') then
FToString := Format('(%g),(%g)', [Fx, Fy]);
Result := FToString;
end;
end.
@@ -0,0 +1,229 @@
unit ZXing.StringUtils;
{
* 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.
* Implemented by E. Spelt for Delphi
}
interface
uses SysUtils, Generics.Collections, ZXing.DecodeHintType;
type
TStringUtils = class abstract
const
UTF8 = 'UTF-8';
EUC_JP = 'EUC-JP';
ISO88591 = 'ISO-8859-1';
class procedure ClassInit;
class var ASSUME_SHIFT_JIS: boolean;
class var PLATFORM_DEFAULT_ENCODING: string;
public
class var GB2312: string;
class var SHIFT_JIS: string;
class function guessEncoding(bytes: TArray<Byte>;
hints: TDictionary<TDecodeHintType, TObject>): string; static;
end;
implementation
class procedure TStringUtils.ClassInit;
begin
PLATFORM_DEFAULT_ENCODING := 'ISO-8859-1'; // 'ISO 8859-15';
SHIFT_JIS := 'SJIS';
GB2312 := 'GB2312';
ASSUME_SHIFT_JIS := (string.Compare(TStringUtils.SHIFT_JIS,
TStringUtils.PLATFORM_DEFAULT_ENCODING, true) = 0) or
(string.Compare('EUC-JP', TStringUtils.PLATFORM_DEFAULT_ENCODING,
true) = 0);
end;
class function TStringUtils.guessEncoding(bytes: TArray<Byte>;
hints: TDictionary<TDecodeHintType, TObject>): string;
var
characterSet: string;
utf8BytesLeft, utf2BytesChars, utf3BytesChars, utf4BytesChars, sjisBytesLeft,
sjisKatakanaChars, sjisCurKatakanaWordLength, sjisCurDoubleBytesWordLength,
sjisMaxKatakanaWordLength, sjisMaxDoubleBytesWordLength, isoHighOther, len,
i, value: Integer;
utf8bom, canBeISO88591, canBeShiftJIS, canBeUTF8: boolean;
begin
if ((hints <> nil) and hints.ContainsKey(ZXing.DecodeHintType.CHARACTER_SET)) then
begin
characterSet := string(hints[ZXing.DecodeHintType.CHARACTER_SET]);
if (characterSet <> '') then
begin
Result := characterSet;
exit
end
end;
len := length(bytes);
canBeISO88591 := true;
canBeShiftJIS := true;
canBeUTF8 := true;
utf8BytesLeft := 0;
utf2BytesChars := 0;
utf3BytesChars := 0;
utf4BytesChars := 0;
sjisBytesLeft := 0;
sjisKatakanaChars := 0;
sjisCurKatakanaWordLength := 0;
sjisCurDoubleBytesWordLength := 0;
sjisMaxKatakanaWordLength := 0;
sjisMaxDoubleBytesWordLength := 0;
isoHighOther := 0;
utf8bom := ((((length(bytes) > 3) and (bytes[0] = $EF)) and (bytes[1] = $BB))
and (bytes[2] = $BF));
i := 0;
while (((i < len) and ((canBeISO88591 or canBeShiftJIS) or canBeUTF8))) do
begin
value := (bytes[i] and $FF);
if (canBeUTF8) then
if (utf8BytesLeft > 0) then
if ((value and $80) = 0) then
canBeUTF8 := false
else
dec(utf8BytesLeft)
else if ((value and $80) <> 0) then
if ((value and $40) = 0) then
canBeUTF8 := false
else
begin
inc(utf8BytesLeft);
if ((value and $20) = 0) then
inc(utf2BytesChars)
else
begin
inc(utf8BytesLeft);
if ((value and $10) = 0) then
inc(utf3BytesChars)
else
begin
inc(utf8BytesLeft);
if ((value and 8) = 0) then
inc(utf4BytesChars)
else
canBeUTF8 := false
end
end
end;
if (canBeISO88591) then
if ((value > $7F) and (value < 160)) then
canBeISO88591 := false
else if ((value > $9F) and (((value < $C0) or (value = $D7)) or
(value = $F7))) then
inc(isoHighOther);
if (canBeShiftJIS) then
if (sjisBytesLeft > 0) then
if (((value < $40) or (value = $7F)) or (value > $FC)) then
canBeShiftJIS := false
else
dec(sjisBytesLeft)
else if (((value = $80) or (value = 160)) or (value > $EF)) then
canBeShiftJIS := false
else if ((value > 160) and (value < $E0)) then
begin
inc(sjisKatakanaChars);
sjisCurDoubleBytesWordLength := 0;
inc(sjisCurKatakanaWordLength);
if (sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength) then
sjisMaxKatakanaWordLength := sjisCurKatakanaWordLength
end
else if (value > $7F) then
begin
inc(sjisBytesLeft);
sjisCurKatakanaWordLength := 0;
inc(sjisCurDoubleBytesWordLength);
if (sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength) then
sjisMaxDoubleBytesWordLength := sjisCurDoubleBytesWordLength
end
else
begin
sjisCurKatakanaWordLength := 0;
sjisCurDoubleBytesWordLength := 0
end;
inc(i)
end;
if (canBeUTF8 and (utf8BytesLeft > 0)) then
canBeUTF8 := false;
if (canBeShiftJIS and (sjisBytesLeft > 0)) then
canBeShiftJIS := false;
if (canBeUTF8 and (utf8bom or (((utf2BytesChars + utf3BytesChars) +
utf4BytesChars) > 0))) then
begin
Result := 'UTF-8';
exit
end;
if (canBeShiftJIS and ((TStringUtils.ASSUME_SHIFT_JIS or
(sjisMaxKatakanaWordLength >= 3)) or (sjisMaxDoubleBytesWordLength >= 3)))
then
begin
Result := TStringUtils.SHIFT_JIS;
exit
end;
if (canBeISO88591 and canBeShiftJIS) then
begin
if (((sjisMaxKatakanaWordLength = 2) and (sjisKatakanaChars = 2)) or
((isoHighOther * 10) >= len)) then
Result := TStringUtils.SHIFT_JIS
else
Result := 'ISO-8859-1';
exit
end;
if (canBeISO88591) then
begin
Result := 'ISO-8859-1';
exit
end;
if (canBeShiftJIS) then
begin
Result := TStringUtils.SHIFT_JIS;
exit
end;
if (canBeUTF8) then
begin
Result := 'UTF-8';
exit
end;
Result := TStringUtils.PLATFORM_DEFAULT_ENCODING;
end;
Initialization
TStringUtils.ClassInit;
end.