View Issue Details

IDProjectCategoryView StatusLast Update
0032580FPCPackagespublic2017-10-20 18:58
ReporterOndrej PokornyAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version3.1.1Product Build 
Target Version3.2.0Fixed in Version3.1.1 
Summary0032580: [Patch] TImageQRCodeGenerator improvements
DescriptionFirst of all thank you for the QR generator!

However I found the TImageQRCodeGenerator strange in some cases:
1.) Origin is a property. This is very uncommon. Usually Origin/Destination is a parameter in the Draw method (see e.g. in TFPCustomCanvas.Draw).
2.) Border is not a property but a parameter in SaveToFile method. This is the opposite case as (1). You may want to draw (= execute the Draw method) the QR object with a border and not to save it to a file. It's not possible with current implementation.
3.) There is no SaveToStream() to generate the image for in-memory use.
4.) There is no exception or any other way to determine that SaveToFile wasn't executed because of unsupported target image file. Try to use "qrcode.xyz" as target file in createqrcode demo.
5.) TCreateQRApplication.WriteQRCode is not used.

The attached patch fixes the above points and changes the demo and fcl-pdf.
TagsNo tags attached.
Fixed in Revision37492
FPCOldBugId
FPCTarget
Attached Files
  • qr-1.patch (6,307 bytes)
    Index: packages/fcl-image/examples/createqrcode.pp
    ===================================================================
    --- packages/fcl-image/examples/createqrcode.pp	(revision 37489)
    +++ packages/fcl-image/examples/createqrcode.pp	(working copy)
    @@ -4,7 +4,7 @@
     {$H+}
     
     uses
    -  Classes, SysUtils, CustApp, fpimage, fpqrcodegen,  fpimgqrcode,
    +  Classes, SysUtils, CustApp, fpimage, fpqrcodegen, fpimgqrcode,
       fpwritepng,fpwritebmp,fpwritexpm, FPWriteJPEG, FPWritePCX,
       FPWritePNM, FPWriteTIFF;
     
    @@ -15,11 +15,8 @@
       TCreateQRApplication = class(TCustomApplication)
       Private
         FText : UTF8String;
    -    FBorder : Integer;
         Foutput : String;
    -    FPixelSize : Integer;
         FGenerator : TImageQRCodeGenerator;
    -    procedure WriteQRCode(QRCode: TQRBuffer);
       protected
     
         function ParseOptions : Boolean;
    @@ -47,7 +44,7 @@
         end;
       FText:=GetOptionValue('t','text');
       FGenerator.PixelSize:=StrToIntDef(GetOptionValue('p','pixel-size'),4);
    -  FBorder:=StrToIntDef(GetOptionValue('b','border'),0);
    +  FGenerator.Border:=StrToIntDef(GetOptionValue('b','border'),0);
       FOutput:=GetOptionValue('o','output');
       if Foutput='' then
         Foutput:='qrcode.png';
    @@ -82,45 +79,10 @@
       if not ParseOptions then
          exit;
       FGenerator.Generate(FText);
    -  FGenerator.SaveToFile(Foutput,FBorder);
    +  FGenerator.SaveToFile(Foutput);
       Terminate;
     end;
     
    -procedure TCreateQRApplication.WriteQRCode(QRCode: TQRBuffer);
    -
    -Var
    -  Img : TFPCustomImage;
    -  D,S,X,Y : Word;
    -
    -
    -begin
    -  S:=QRGetSize(QRCode);
    -  if S=0 then exit;
    -  D:=FPixelSize*S;
    -
    -  Img:=TFPCompactImgGray8Bit.Create(D+FBorder*2,D+FBorder*2);
    -  try
    -    For X:=0 to D+(FBorder*2)-1 do
    -      For Y:=1 to FBorder do
    -        begin
    -        Img[X,Y-1]:=colWhite;
    -        Img[X,D+(FBorder*2)-Y]:=colWhite;
    -        end;
    -    For Y:=FBorder to D+FBorder-1 do
    -      For X:=1 to FBorder do
    -        begin
    -        Img[X-1,Y]:=colWhite;
    -        Img[D+(FBorder*2)-X,Y]:=colWhite;
    -        end;
    -
    -    DrawQRCode(Img,QRCode,Point(FBorder,FBorder),FPixelSize);
    -    Img.SaveToFile(Foutput);
    -  finally
    -    Img.Free;
    -  end;
    -end;
    -
    -
     constructor TCreateQRApplication.Create(TheOwner: TComponent);
     begin
       inherited Create(TheOwner);
    Index: packages/fcl-image/src/fpimgqrcode.pp
    ===================================================================
    --- packages/fcl-image/src/fpimgqrcode.pp	(revision 37489)
    +++ packages/fcl-image/src/fpimgqrcode.pp	(working copy)
    @@ -27,15 +27,16 @@
     
       TImageQRCodeGenerator = Class(TQRCodeGenerator)
       private
    -    FOrigin: TPoint;
         FPixelSize: Integer;
    +    FBorder: Integer;
       Public
         Constructor Create; override;
         Procedure Draw(Img : TFPCustomImage);
    -    // overrides Origin.
    -    Procedure SaveToFile(const AFileName : String; aBorder : Integer = 0);
    +    Procedure Draw(Img : TFPCustomImage; DestX, DestY: Integer);
    +    Function SaveToStream(const AStream : TStream; AWriter: TFPCustomImageWriter): Boolean;
    +    Function SaveToFile(const AFileName : String): Boolean;
         Property PixelSize : Integer Read FPixelSize Write FPixelSize default 2;
    -    Property Origin : TPoint Read FOrigin Write FOrigin;
    +    Property Border : Integer Read FBorder Write FBorder default 0;
       end;
     
     Procedure DrawQRCode(Img : TFPCustomImage; QRCode : TQRBuffer; aOrigin: TPoint; PixelSize : Byte = 1);
    @@ -89,38 +90,80 @@
     
     procedure TImageQRCodeGenerator.Draw(Img: TFPCustomImage);
     begin
    -  DrawQRCode(Img,Bytes,FOrigin,PixelSize);
    +  Draw(Img, 0, 0);
     end;
     
    -procedure TImageQRCodeGenerator.SaveToFile(const AFileName: String; aBorder: Integer);
    +procedure TImageQRCodeGenerator.Draw(Img: TFPCustomImage; DestX,
    +  DestY: Integer);
    +var
    +  X,Y : Integer;
    +  S,D : Integer;
    +begin
    +  S:=Size;
    +  D:=PixelSize*S;
    +  if Border>0 then
    +    begin
    +    For X:=0 to D+(Border*2)-1 do
    +      For Y:=1 to Border do
    +        begin
    +        Img[DestX+X,DestY+Y-1]:=colWhite;
    +        Img[DestX+X,DestY+D+(Border*2)-Y]:=colWhite;
    +        end;
    +    For Y:=Border to D+Border-1 do
    +      For X:=1 to Border do
    +        begin
    +        Img[DestX+X-1,DestY+Y]:=colWhite;
    +        Img[DestX+D+(Border*2)-X,DestY+Y]:=colWhite;
    +        end;
    +    end;
    +  DrawQRCode(Img,Bytes,Point(DestX+Border,DestY+Border),PixelSize);
    +end;
     
    +function TImageQRCodeGenerator.SaveToFile(const AFileName: String): Boolean;
     
    +
     Var
    +  WriterClass : TFPCustomImageWriterClass;
    +  Writer : TFPCustomImageWriter;
    +  Stream : TFileStream;
    +
    +
    +begin
    +  Result := Size>0;
    +  if not Result then exit;
    +  WriterClass := TFPCustomImage.FindWriterFromFileName(AFileName);
    +  if Assigned(WriterClass) then
    +  begin
    +    Writer := nil;
    +    Stream := nil;
    +    try
    +      Writer := WriterClass.Create;
    +      Stream := TFileStream.Create(AFileName, fmCreate);
    +      SaveToStream(Stream, Writer);
    +    finally
    +      Stream.Free;
    +      Writer.Free;
    +    end;
    +  end else
    +    FPImageException.CreateFmt(ErrorText[StrCantDetermineType], [AFileName]);
    +end;
    +
    +function TImageQRCodeGenerator.SaveToStream(const AStream: TStream;
    +  AWriter: TFPCustomImageWriter): Boolean;
    +Var
       Img : TFPCustomImage;
    -  D,S,X,Y : Word;
    +  D,S : Word;
     
     
     begin
       S:=Size;
    -  if S=0 then exit;
    +  Result := S>0;
    +  if not Result then exit;
       D:=PixelSize*S;
    -  Img:=TFPCompactImgGray8Bit.Create(D+aBorder*2,D+aBorder*2);
    +  Img:=TFPCompactImgGray8Bit.Create(D+Border*2,D+Border*2);
       try
    -    For X:=0 to D+(aBorder*2)-1 do
    -      For Y:=1 to aBorder do
    -        begin
    -        Img[X,Y-1]:=colWhite;
    -        Img[X,D+(aBorder*2)-Y]:=colWhite;
    -        end;
    -    For Y:=aBorder to D+aBorder-1 do
    -      For X:=1 to aBorder do
    -        begin
    -        Img[X-1,Y]:=colWhite;
    -        Img[D+(aBorder*2)-X,Y]:=colWhite;
    -        end;
    -    Origin:=Point(aBorder,aBorder);
         Draw(Img);
    -    Img.SaveToFile(aFileName);
    +    Img.SaveToStream(AStream, AWriter);
       finally
         Img.Free;
       end;
    Index: packages/fcl-report/src/fpreportqrcode.pp
    ===================================================================
    --- packages/fcl-report/src/fpreportqrcode.pp	(revision 37489)
    +++ packages/fcl-report/src/fpreportqrcode.pp	(working copy)
    @@ -189,8 +189,7 @@
          if DD>0 then
            PY:=DD div 2;
          end; 
    -    D.Origin:=Point(PX,PY);
    -    D.Draw(aImage);
    +    D.Draw(aImage, PX, PY);
       finally
         D.Free;
       end;
    
    qr-1.patch (6,307 bytes)

Activities

Ondrej Pokorny

2017-10-19 15:18

developer  

qr-1.patch (6,307 bytes)
Index: packages/fcl-image/examples/createqrcode.pp
===================================================================
--- packages/fcl-image/examples/createqrcode.pp	(revision 37489)
+++ packages/fcl-image/examples/createqrcode.pp	(working copy)
@@ -4,7 +4,7 @@
 {$H+}
 
 uses
-  Classes, SysUtils, CustApp, fpimage, fpqrcodegen,  fpimgqrcode,
+  Classes, SysUtils, CustApp, fpimage, fpqrcodegen, fpimgqrcode,
   fpwritepng,fpwritebmp,fpwritexpm, FPWriteJPEG, FPWritePCX,
   FPWritePNM, FPWriteTIFF;
 
@@ -15,11 +15,8 @@
   TCreateQRApplication = class(TCustomApplication)
   Private
     FText : UTF8String;
-    FBorder : Integer;
     Foutput : String;
-    FPixelSize : Integer;
     FGenerator : TImageQRCodeGenerator;
-    procedure WriteQRCode(QRCode: TQRBuffer);
   protected
 
     function ParseOptions : Boolean;
@@ -47,7 +44,7 @@
     end;
   FText:=GetOptionValue('t','text');
   FGenerator.PixelSize:=StrToIntDef(GetOptionValue('p','pixel-size'),4);
-  FBorder:=StrToIntDef(GetOptionValue('b','border'),0);
+  FGenerator.Border:=StrToIntDef(GetOptionValue('b','border'),0);
   FOutput:=GetOptionValue('o','output');
   if Foutput='' then
     Foutput:='qrcode.png';
@@ -82,45 +79,10 @@
   if not ParseOptions then
      exit;
   FGenerator.Generate(FText);
-  FGenerator.SaveToFile(Foutput,FBorder);
+  FGenerator.SaveToFile(Foutput);
   Terminate;
 end;
 
-procedure TCreateQRApplication.WriteQRCode(QRCode: TQRBuffer);
-
-Var
-  Img : TFPCustomImage;
-  D,S,X,Y : Word;
-
-
-begin
-  S:=QRGetSize(QRCode);
-  if S=0 then exit;
-  D:=FPixelSize*S;
-
-  Img:=TFPCompactImgGray8Bit.Create(D+FBorder*2,D+FBorder*2);
-  try
-    For X:=0 to D+(FBorder*2)-1 do
-      For Y:=1 to FBorder do
-        begin
-        Img[X,Y-1]:=colWhite;
-        Img[X,D+(FBorder*2)-Y]:=colWhite;
-        end;
-    For Y:=FBorder to D+FBorder-1 do
-      For X:=1 to FBorder do
-        begin
-        Img[X-1,Y]:=colWhite;
-        Img[D+(FBorder*2)-X,Y]:=colWhite;
-        end;
-
-    DrawQRCode(Img,QRCode,Point(FBorder,FBorder),FPixelSize);
-    Img.SaveToFile(Foutput);
-  finally
-    Img.Free;
-  end;
-end;
-
-
 constructor TCreateQRApplication.Create(TheOwner: TComponent);
 begin
   inherited Create(TheOwner);
Index: packages/fcl-image/src/fpimgqrcode.pp
===================================================================
--- packages/fcl-image/src/fpimgqrcode.pp	(revision 37489)
+++ packages/fcl-image/src/fpimgqrcode.pp	(working copy)
@@ -27,15 +27,16 @@
 
   TImageQRCodeGenerator = Class(TQRCodeGenerator)
   private
-    FOrigin: TPoint;
     FPixelSize: Integer;
+    FBorder: Integer;
   Public
     Constructor Create; override;
     Procedure Draw(Img : TFPCustomImage);
-    // overrides Origin.
-    Procedure SaveToFile(const AFileName : String; aBorder : Integer = 0);
+    Procedure Draw(Img : TFPCustomImage; DestX, DestY: Integer);
+    Function SaveToStream(const AStream : TStream; AWriter: TFPCustomImageWriter): Boolean;
+    Function SaveToFile(const AFileName : String): Boolean;
     Property PixelSize : Integer Read FPixelSize Write FPixelSize default 2;
-    Property Origin : TPoint Read FOrigin Write FOrigin;
+    Property Border : Integer Read FBorder Write FBorder default 0;
   end;
 
 Procedure DrawQRCode(Img : TFPCustomImage; QRCode : TQRBuffer; aOrigin: TPoint; PixelSize : Byte = 1);
@@ -89,38 +90,80 @@
 
 procedure TImageQRCodeGenerator.Draw(Img: TFPCustomImage);
 begin
-  DrawQRCode(Img,Bytes,FOrigin,PixelSize);
+  Draw(Img, 0, 0);
 end;
 
-procedure TImageQRCodeGenerator.SaveToFile(const AFileName: String; aBorder: Integer);
+procedure TImageQRCodeGenerator.Draw(Img: TFPCustomImage; DestX,
+  DestY: Integer);
+var
+  X,Y : Integer;
+  S,D : Integer;
+begin
+  S:=Size;
+  D:=PixelSize*S;
+  if Border>0 then
+    begin
+    For X:=0 to D+(Border*2)-1 do
+      For Y:=1 to Border do
+        begin
+        Img[DestX+X,DestY+Y-1]:=colWhite;
+        Img[DestX+X,DestY+D+(Border*2)-Y]:=colWhite;
+        end;
+    For Y:=Border to D+Border-1 do
+      For X:=1 to Border do
+        begin
+        Img[DestX+X-1,DestY+Y]:=colWhite;
+        Img[DestX+D+(Border*2)-X,DestY+Y]:=colWhite;
+        end;
+    end;
+  DrawQRCode(Img,Bytes,Point(DestX+Border,DestY+Border),PixelSize);
+end;
 
+function TImageQRCodeGenerator.SaveToFile(const AFileName: String): Boolean;
 
+
 Var
+  WriterClass : TFPCustomImageWriterClass;
+  Writer : TFPCustomImageWriter;
+  Stream : TFileStream;
+
+
+begin
+  Result := Size>0;
+  if not Result then exit;
+  WriterClass := TFPCustomImage.FindWriterFromFileName(AFileName);
+  if Assigned(WriterClass) then
+  begin
+    Writer := nil;
+    Stream := nil;
+    try
+      Writer := WriterClass.Create;
+      Stream := TFileStream.Create(AFileName, fmCreate);
+      SaveToStream(Stream, Writer);
+    finally
+      Stream.Free;
+      Writer.Free;
+    end;
+  end else
+    FPImageException.CreateFmt(ErrorText[StrCantDetermineType], [AFileName]);
+end;
+
+function TImageQRCodeGenerator.SaveToStream(const AStream: TStream;
+  AWriter: TFPCustomImageWriter): Boolean;
+Var
   Img : TFPCustomImage;
-  D,S,X,Y : Word;
+  D,S : Word;
 
 
 begin
   S:=Size;
-  if S=0 then exit;
+  Result := S>0;
+  if not Result then exit;
   D:=PixelSize*S;
-  Img:=TFPCompactImgGray8Bit.Create(D+aBorder*2,D+aBorder*2);
+  Img:=TFPCompactImgGray8Bit.Create(D+Border*2,D+Border*2);
   try
-    For X:=0 to D+(aBorder*2)-1 do
-      For Y:=1 to aBorder do
-        begin
-        Img[X,Y-1]:=colWhite;
-        Img[X,D+(aBorder*2)-Y]:=colWhite;
-        end;
-    For Y:=aBorder to D+aBorder-1 do
-      For X:=1 to aBorder do
-        begin
-        Img[X-1,Y]:=colWhite;
-        Img[D+(aBorder*2)-X,Y]:=colWhite;
-        end;
-    Origin:=Point(aBorder,aBorder);
     Draw(Img);
-    Img.SaveToFile(aFileName);
+    Img.SaveToStream(AStream, AWriter);
   finally
     Img.Free;
   end;
Index: packages/fcl-report/src/fpreportqrcode.pp
===================================================================
--- packages/fcl-report/src/fpreportqrcode.pp	(revision 37489)
+++ packages/fcl-report/src/fpreportqrcode.pp	(working copy)
@@ -189,8 +189,7 @@
      if DD>0 then
        PY:=DD div 2;
      end; 
-    D.Origin:=Point(PX,PY);
-    D.Draw(aImage);
+    D.Draw(aImage, PX, PY);
   finally
     D.Free;
   end;
qr-1.patch (6,307 bytes)

Michael Van Canneyt

2017-10-20 09:20

administrator   ~0103595

The thanks should go mostly to Nayuki, the original author of the QR code :)

I reviewed and applied your patch. Your remarks are absolutely correct.
Many thanks for the patch !

Ondrej Pokorny

2017-10-20 18:58

developer   ~0103613

Thank you for fast resolving!

Issue History

Date Modified Username Field Change
2017-10-19 15:18 Ondrej Pokorny New Issue
2017-10-19 15:18 Ondrej Pokorny File Added: qr-1.patch
2017-10-19 15:50 Michael Van Canneyt Assigned To => Michael Van Canneyt
2017-10-19 15:50 Michael Van Canneyt Status new => assigned
2017-10-20 09:20 Michael Van Canneyt Fixed in Revision => 37492
2017-10-20 09:20 Michael Van Canneyt Note Added: 0103595
2017-10-20 09:20 Michael Van Canneyt Status assigned => resolved
2017-10-20 09:20 Michael Van Canneyt Fixed in Version => 3.1.1
2017-10-20 09:20 Michael Van Canneyt Resolution open => fixed
2017-10-20 09:20 Michael Van Canneyt Target Version => 3.2.0
2017-10-20 18:58 Ondrej Pokorny Note Added: 0103613
2017-10-20 18:58 Ondrej Pokorny Status resolved => closed