Search code examples
delphiqr-codezxing

QR Codes encoded by DelphiZXingQRCode not decodable with ErrorCorrectionLevel > Low


I am using this Delphi-Unit. I am forced to use this old Delphi implementation, so don't ask about that: https://github.com/foxitsoftware/DelphiZXingQRCode

The QRcodes it produces are decoded by any decoders just fine, as long as I keep the error correction level at "Low". If I increase the error correction level, the generated codes can not be decoded by any decoder I tried so far. But I am forced (by a standard) to use an error correction level of Medium, no more no less.

However it is not clear how to increase the error correction level (ecl from now on). I assume it to be hardcoded in the file DelphiZXingQRCode on line 3491: Level.FBits := 1. I found some information on hexnumbers representing ecls, but I can't find it now. But I tried those hexnumbers as bits and the ecl-bits on the QRcode changed accordingly. So I assumed the hexnumbers to be correct (1=Low, 0=Medium, 2=High, 3=Quartile).

Here is an example of a QRcode with Level.FBits := 2, meaning I wanted the ecl to be "High". The content ist "Hello world". The cross image in the middle is part of a standard [sic] I have to implement, so don't ask about that. enter image description here

Has anyone any idea how to fix this? I tried...well...I tried to understand the code but it's too much. I just can't fix it. If I can't get it fixed by someone else, I will have to...find another solution. Which will be a problem.


Solution

  • Solved. See code below. Method GenerateQRCode() now requires a parameter for the ErrorCorrectionLevel: Integers 0-3. Seems to work. I had to remove some unchanged lines, because the file is too large for StackOverflow. Merge yourself.

        unit DelphiZXingQRCode;
    
        // ZXing QRCode port to Delphi, by Debenu Pty Ltd
        // www.debenu.com
    
        // Original copyright notice
        (*
         * Copyright 2008 ZXing authors
         *
         * Licensed under the Apache License, Version 2.0 (the "License");
         * you may not use this file except in compliance with the License.
         * You may obtain a copy of the License at
         *
         *      http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing, software
         * distributed under the License is distributed on an "AS IS" BASIS,
         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
         * See the License for the specific language governing permissions and
         * limitations under the License.
         *)
    
        interface
    
        type
          TQRCodeEncoding = (qrAuto, qrNumeric, qrAlphanumeric, qrISO88591, qrUTF8NoBOM, qrUTF8BOM);
          T2DBooleanArray = array of array of Boolean;
    
          TDelphiZXingQRCode = class
          protected
            FData: WideString;
            FRows: Integer;
            FColumns: Integer;
            FEncoding: TQRCodeEncoding;
            FQuietZone: Integer;
            FElements: T2DBooleanArray;
            FErrorCorrectionLevel: integer;
            procedure SetEncoding(NewEncoding: TQRCodeEncoding);
            procedure SetData(const NewData: WideString);
            procedure SetQuietZone(NewQuietZone: Integer);
            procedure SetErrorCorrectionLevel(value: integer);
            function GetIsBlack(Row, Column: Integer): Boolean;
            procedure Update;
          public
            constructor Create;
            property Data: WideString read FData write SetData;
            property Encoding: TQRCodeEncoding read FEncoding write SetEncoding;
            property ErrorCorrectionLevel: integer read fErrorCorrectionLevel write SetErrorCorrectionLevel;
            property QuietZone: Integer read FQuietZone write SetQuietZone;
            property Rows: Integer read FRows;
            property Columns: Integer read FColumns;
            property IsBlack[Row, Column: Integer]: Boolean read GetIsBlack;
          end;
    
        implementation
    
        uses
          SysUtils,
          contnrs, Math, Classes;
    
        type
          TByteArray = array of Byte;
          T2DByteArray = array of array of Byte;
          TIntegerArray = array of Integer;
    
        // File too large for Stackoverflow: Deleted unchanged lines.
    
        { TErrorCorrectionLevel }
    
        procedure TErrorCorrectionLevel.Assign(Source: TErrorCorrectionLevel);
        begin
          Self.fOrdinal := Source.FOrdinal;
        end;
    
        constructor TErrorCorrectionLevel.Create(ordinalValue: integer);
        begin
    
          fOrdinal:=0;
          if (ordinalValue >= 0) and (ordinalValue <=3) then
            fOrdinal:=ordinalValue;
        end;
    
        function TErrorCorrectionLevel.GetBits: integer;
        begin
          if fOrdinal = 0 then  // level L
            result:=1
          else
          if fOrdinal = 1 then  // level M
            result:=0
          else
          if fOrdinal = 2 then  // level Q
            result:=3
          else
          if fOrdinal = 3 then  // level H
            result:=2
          else
            result:=1;
        end;
    
    
        // File too large for Stackoverflow: Deleted unchanged lines.
    
        procedure TDelphiZXingQRCode.SetErrorCorrectionLevel(value: integer);
        begin
          if (value < 0) or (value > 3) then
            raise Exception.Create('invalid error correction value. must be in range 0..3.');
    
          if value <> fErrorCorrectionLevel then
          begin
            FErrorCorrectionLevel:=value;
            Update;
          end;
        end;
    
        procedure TDelphiZXingQRCode.SetQuietZone(NewQuietZone: Integer);
        begin
          if ((FQuietZone <> NewQuietZone) and (NewQuietZone >= 0) and (NewQuietZone <= 100)) then
          begin
            FQuietZone := NewQuietZone;
            Update;
          end;
        end;
    
        procedure TDelphiZXingQRCode.Update;
        begin
          FElements := GenerateQRCode(FData, Ord(FEncoding), FErrorCorrectionLevel);
          FRows := Length(FElements) + FQuietZone * 2;
          FColumns := FRows;
        end;
        end.