Recent

Author Topic: Sending Bitmap to ESC/POS  (Read 7312 times)

lainz

  • Hero Member
  • *****
  • Posts: 4468
    • https://lainz.github.io/
Sending Bitmap to ESC/POS
« on: December 26, 2019, 11:50:21 pm »
Hi, this is the code website
https://bitbucket.org/bernd_summerswell/delphi_escpos_bitmap/src/master/

This is the source for the class
https://bitbucket.org/bernd_summerswell/delphi_escpos_bitmap/src/master/ssESCPosPrintBitmap.pas

And this is the demo
Code: Pascal  [Select][+][-]
  1. uses
  2.   synaser,
  3.   ssESCPOSPrintBitmap;
  4.  
  5. var
  6.   LDevice : TBlockDevice;
  7.   LBuffer : string;
  8. begin
  9.   LBuffer := #27'@'; // Initialise the printer
  10.   LBuffer := LBuffer + _ESCPosPrintBitmap().RenderBitmap( 'path\to\bitmapfile' );
  11.   LBuffer := #29'V'#1 // Paper cut full
  12.  
  13.   // Send LBuffer to Printer
  14.   LDevice := TBlockSerial.Create();
  15.     try
  16.       LDevice.Config( 115200, 8, 'N', 1, False, False );
  17.       LDevice.Connect( 'COM7' );
  18.       LDevice.SendString( LBuffer  );
  19.     finally
  20.       LDevice.Free();
  21.     end;
  22. end.

Anyone can confirm this works with Lazarus fine? Unfortunatelly I don't have an ESC/POS printer...

There's a way I can "emulate" any ESC/POS printer to test this?

trev

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2020
  • Former Delphi 1-7, 10.2 user
Re: Sending Bitmap to ESC/POS
« Reply #1 on: December 27, 2019, 12:06:05 am »
There's a way I can "emulate" any ESC/POS printer to test this?

Have a look at https://github.com/receipt-print-hq/escpos-tools

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Sending Bitmap to ESC/POS
« Reply #2 on: December 27, 2019, 12:39:19 am »
Hi!

And if you don't want to emulate:

(Brand!)new China trash available for $ 18.99 at Ebay ("Free international shipping!").

https://www.ebay.com/sch/i.html?_from=R40&_nkw=ESC%2FPOS&_sacat=0&_sop=15

No, I don't get any payment from them ...

Winni
« Last Edit: December 27, 2019, 12:42:10 am by winni »

lainz

  • Hero Member
  • *****
  • Posts: 4468
    • https://lainz.github.io/
Re: Sending Bitmap to ESC/POS
« Reply #3 on: December 27, 2019, 12:49:39 am »
Hi!

And if you don't want to emulate:

(Brand!)new China trash available for $ 18.99 at Ebay ("Free international shipping!").

https://www.ebay.com/sch/i.html?_from=R40&_nkw=ESC%2FPOS&_sacat=0&_sop=15

No, I don't get any payment from them ...

Winni

Thanks. I imagined these was more expensive, but I get the emulator running on my system, so it will be cheaper I think  :)

There's a way I can "emulate" any ESC/POS printer to test this?

Have a look at https://github.com/receipt-print-hq/escpos-tools

Thanks. It works.

I need a bit more of help, how I can save properly the binary file? To send it to the php program?

Code: Pascal  [Select][+][-]
  1. LBuffer : string;
  2. begin
  3.   LBuffer := #27'@'; // Initialise the printer
  4.   LBuffer := LBuffer + _ESCPosPrintBitmap().RenderBitmap( 'path\to\bitmapfile' );
  5.   LBuffer := #29'V'#1 // Paper cut full
  6.  
  7. // Save to .bin file here---
  8.  

I'm new to binary stuff... always working with high level stuff...

Thanks.
« Last Edit: December 27, 2019, 12:54:02 am by lainz »

wp

  • Hero Member
  • *****
  • Posts: 11915
Re: Sending Bitmap to ESC/POS
« Reply #4 on: December 27, 2019, 12:57:33 am »
Assuming that you want to save the string LBuffer to file do this:
Code: Pascal  [Select][+][-]
  1. var
  2.   stream: TFileStream;
  3. ...
  4.   stream := TFileStream.Create(FILE_NAME, fmCreate);
  5.   try
  6.     stream.Write(LBuffer[1], Length(LBuffer));
  7.   finally
  8.     stream.Free;
  9.   end;

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Sending Bitmap to ESC/POS
« Reply #5 on: December 27, 2019, 01:17:16 am »
Hi!

The function TssESCPosPrintBitmap.RenderBitmap( const ABitmapFilename: string): string; converts everything into the format for the lineprinter:

Put the bmp header in the beginning.
Makes stripes of 24 pix height for a "24 needle printer" - old standard.
And converts the bitmap.data into printer format.

So there are definitly unprintable and illegal utf8-signs in the string.
So take care that this bytes are not stripped or masked by some routines.
I don't knoew if it happes with a FileStream.

If it does I would copy the string into an array of byte and save it als a file of byte.

And have a look in the Delphi code in the function TssESCPosPrintBitmap.RenderBitmap - there you can see in some lines of code how he is converting the bitmap into the line printer format.

Winni


lainz

  • Hero Member
  • *****
  • Posts: 4468
    • https://lainz.github.io/
Re: Sending Bitmap to ESC/POS
« Reply #6 on: December 27, 2019, 02:29:07 pm »
On Delphi it works changing string with rawbytestring, but the resulting image has some artifacts, anyways it works.

With the same code on Lazarus, it displays only the first say 20% of the image from left to right.

On Lazarus I get these warnings

ssESCPosPrintBitmap.pas(137,22) Warning: Symbol "ScanLine" is not portable
ssESCPosPrintBitmap.pas(183,30) Warning: lo/hi(dword/qword) returns the upper/lower word/dword
ssESCPosPrintBitmap.pas(184,30) Warning: lo/hi(dword/qword) returns the upper/lower word/dword

Of course ScanLine works only on Windows. What about Lo / Hi ? It works different from Delphi or is just a warning that I can forget?

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Sending Bitmap to ESC/POS
« Reply #7 on: December 27, 2019, 02:50:57 pm »
Hi!

The warings about Hi/Lo leads to those two lines:

Code: Pascal  [Select][+][-]
  1. Result := Result + Char( Lo( FBitmapData.Width ) );
  2. Result := Result + Char( Hi( FBitmapData.Width ) );

They only make sense if he is using 16 bit integers - smallint.

So change your integers to smallint!

Winni

lainz

  • Hero Member
  • *****
  • Posts: 4468
    • https://lainz.github.io/
Re: Sending Bitmap to ESC/POS
« Reply #8 on: December 27, 2019, 02:57:10 pm »
Hi!

The warings about Hi/Lo leads to those two lines:

Code: Pascal  [Select][+][-]
  1. Result := Result + Char( Lo( FBitmapData.Width ) );
  2. Result := Result + Char( Hi( FBitmapData.Width ) );

They only make sense if he is using 16 bit integers - smallint.

So change your integers to smallint!

Winni

Thanks, it works =)

   TBitmapData = record
     Dots      : array of Boolean;
-    Height    : Integer;
-    Width     : Integer;
+    Height    : smallint;
+    Width     : smallint;
   end;
« Last Edit: December 27, 2019, 03:02:39 pm by lainz »

lainz

  • Hero Member
  • *****
  • Posts: 4468
    • https://lainz.github.io/
Re: Sending Bitmap to ESC/POS
« Reply #9 on: December 27, 2019, 04:43:50 pm »
This is the fixed unit, using BGRABitmap to allow to load any kind of image.
Transparency by default is converted to White.

Tested only on Windows with the php tools of the second comment.

Code: Pascal  [Select][+][-]
  1. {******************************************************************************}
  2. {                                                                              }
  3. {  Unit: ssESCPosPrintBitmap.pas                                               }
  4. {  Summerswell Core                                                            }
  5. {                                                                              }
  6. {  Copyright (C) 2015 Summerswell, 2019 Lainz                                  }
  7. {                                                                              }
  8. {  Author     : bvonfintel                                                     }
  9. {  Original   : 2015/01/06 11:12:30 AM                                         }
  10. {  Modified   : 2019/12/27 12:38:00 AM                                         }
  11. {                                                                              }
  12. {******************************************************************************}
  13.  
  14. {
  15.  
  16.   This is based on the class provided by:
  17.   http://nicholas.piasecki.name/blog/2009/12/sending-a-bit-image-to-an-epson-tm-t88iii-receipt-printer-using-c-and-escpos/
  18.  
  19.   The Delphi translation provided (http://nicholas.piasecki.name/blog/wp-content/uploads/2009/12/Delphi-Version.txt.txt)
  20.   did not print to the Epson TM T70
  21.  
  22.   Updated to use BGRABitmap Library and working with Lazarus by Lainz
  23.  
  24. }
  25.  
  26. unit ssESCPosPrintBitmap;
  27.  
  28. {$MODE Delphi}
  29.  
  30. interface
  31.  
  32. type
  33.   // *** -------------------------------------------------------------------------
  34.   // *** INTERFACE: IssESCPosPrintBitmap
  35.   // *** -------------------------------------------------------------------------
  36.  
  37.   { IssESCPosPrintBitmap }
  38.  
  39.   IssESCPosPrintBitmap = interface( IInterface )
  40.     ['{3F279585-6D2E-451F-AF97-76F0E07A70DF}']
  41.     function RenderBitmap( const ABitmapFilename: string ): rawbytestring;
  42.     function RenderBitmapBase64(const base64data: string): rawbytestring;
  43.   end;
  44.  
  45.   function _ESCPosPrintBitmap(): IssESCPosPrintBitmap;
  46.  
  47. implementation
  48.  
  49. uses
  50.   LCLIntf, LCLType, {LMessages,}
  51.   Graphics,
  52.   Math,
  53.   BGRABitmap, BGRABitmapTypes, base64, Classes, SysUtils, Dialogs;
  54.  
  55. function Base64ToStream(const ABase64: String; var AStream: TMemoryStream): Boolean;
  56. var
  57.   Str: TStringStream;
  58. begin
  59.   Result := False;
  60.   if Length(Trim(ABase64)) = 0 then
  61.     Exit;
  62.   try
  63.     Str := TStringStream.Create(DecodeStringBase64(ABase64));
  64.     try
  65.       AStream.CopyFrom(Str, Str.Size);
  66.       AStream.Position := 0;
  67.     finally
  68.       Str.Free;
  69.     end;
  70.     Result := True;
  71.   except
  72.     on E: Exception do
  73.       ShowMessage(E.Message);
  74.   end;
  75. end;
  76.  
  77. function StreamToBase64(const AStream: TMemoryStream; out Base64: String): Boolean;
  78. var
  79.   Str: String = '';
  80. begin
  81.   Result := False;
  82.   if AStream.Size = 0 then
  83.     Exit;
  84.   AStream.Position := 0;
  85.   try
  86.     SetLength(Str, AStream.Size div SizeOf(Char));
  87.     AStream.ReadBuffer(Pointer(Str)^, AStream.Size div SizeOf(Char));
  88.     Base64 := EncodeStringBase64(Str);
  89.     Result := True;
  90.   except
  91.     {on E: Exception do
  92.       ShowMessage(E.Message);}
  93.   end;
  94. end;
  95.  
  96. type
  97.   // *** -------------------------------------------------------------------------
  98.   // *** RECORD:  TBitmapData
  99.   // *** -------------------------------------------------------------------------
  100.   TBitmapData = record
  101.     Dots      : array of Boolean;
  102.     Height    : smallint;
  103.     Width     : smallint;
  104.   end;
  105.  
  106.   // *** -------------------------------------------------------------------------
  107.   // *** CLASS: TssESCPosPrintBitmap
  108.   // *** -------------------------------------------------------------------------
  109.  
  110.   { TssESCPosPrintBitmap }
  111.  
  112.   TssESCPosPrintBitmap = class( TInterfacedObject, IssESCPosPrintBitmap )
  113.     private
  114.       FLumThreshold : Integer;
  115.       FBitmap       : TBGRABitmap;
  116.       FBitmapData   : TBitmapData;
  117.       procedure LoadBitmapData();
  118.     public
  119.       constructor Create();
  120.       destructor Destroy; override;
  121.  
  122.       function RenderBitmap( const ABitmapFilename: string ): rawbytestring;
  123.       function RenderBitmapBase64(const base64data: string): rawbytestring;
  124.   end;
  125.  
  126. const
  127.   C_DEFAULT_THRESHOLD = 127;
  128.  
  129. function _ESCPosPrintBitmap(): IssESCPosPrintBitmap;
  130. begin
  131.   Result := TssESCPosPrintBitmap.Create();
  132. end;
  133.  
  134.  
  135. { TssESCPosPrintBitmap }
  136.  
  137. {-------------------------------------------------------------------------------
  138.   Procedure: TssESCPosPrintBitmap.Create
  139.   Author:    bvonfintel - lainz
  140.   DateTime:  2015.01.06 - 2019.12.27
  141.   Arguments: None
  142.   Result:    None
  143. -------------------------------------------------------------------------------}
  144. constructor TssESCPosPrintBitmap.Create();
  145. begin
  146.   inherited;
  147.   FBitmap       := TBGRABitmap.Create();
  148.   FLumThreshold := C_DEFAULT_THRESHOLD;
  149. end;
  150.  
  151. {-------------------------------------------------------------------------------
  152.   Procedure: TssESCPosPrintBitmap.Destroy
  153.   Author:    bvonfintel - lainz
  154.   DateTime:  2015.01.06 - 2019.12.27
  155.   Arguments: None
  156.   Result:    None
  157. -------------------------------------------------------------------------------}
  158. destructor TssESCPosPrintBitmap.Destroy;
  159. begin
  160.   FBitmap.Free();
  161.   inherited;
  162. end;
  163.  
  164. {-------------------------------------------------------------------------------
  165.   Procedure: TssESCPosPrintBitmap.LoadBitmapData
  166.   Author:    bvonfintel - lainz
  167.   DateTime:  2015.01.06 - 2019.12.27
  168.   Arguments: None
  169.   Result:    None
  170. -------------------------------------------------------------------------------}
  171. procedure TssESCPosPrintBitmap.LoadBitmapData();
  172. var
  173.   i: integer;
  174.   p: PBGRAPixel;
  175.   LIndex : Integer;
  176.   LLum   : Integer;
  177. begin
  178.   LIndex := 0;
  179.  
  180.   FBitmapData.Height := FBitmap.Height;
  181.   FBitmapData.Width  := FBitmap.Width;
  182.   SetLength( FBitmapData.Dots, FBitmap.Width * FBitmap.Height );
  183.  
  184.   FBitmap.VerticalFlip;
  185.   FBitmap.ReplaceTransparent(BGRAWhite);
  186.  
  187.   p := FBitmap.Data;
  188.  
  189.   for i := 0 to FBitmap.NBPixels - 1 do
  190.   begin
  191.     LLum := Trunc( ( p^.red * 0.3 ) + ( p^.green  * 0.59 ) + ( p^.blue * 0.11 ) );
  192.     FBitmapData.Dots[LIndex] := ( LLum < FLumThreshold );
  193.     Inc(LIndex);
  194.     Inc(p);
  195.   end;
  196. end;
  197.  
  198. {-------------------------------------------------------------------------------
  199.   Procedure: TssESCPosPrintBitmap.RenderBitmap
  200.   Author:    bvonfintel
  201.   DateTime:  2015.01.06
  202.   Arguments: const ABitmapFilename: string
  203.   Result:    string
  204. -------------------------------------------------------------------------------}
  205. function TssESCPosPrintBitmap.RenderBitmap( const ABitmapFilename: string): rawbytestring;
  206. var
  207.   LOffset     : Integer;
  208.   LX          : Integer;
  209.   LSlice      : Byte;
  210.   LB          : Integer;
  211.   LK          : Integer;
  212.   LY          : Integer;
  213.   LI          : Integer;
  214.   LV          : Boolean;
  215.   LVI         : Integer;
  216. begin
  217.   // *** load the Bitmap from the file
  218.   FBitmap.LoadFromFile( ABitmapFilename );
  219.  
  220.   // *** Convert the bitmap to an array of B/W pixels
  221.   LoadBitmapData();
  222.  
  223.   // *** Set the line spacing to 24 dots, the height of each "stripe" of the
  224.   // *** image that we're drawing
  225.   Result := #27'3'#24;
  226.  
  227.   LOffset := 0;
  228.   while ( LOffset < FBitmapData.Height ) do begin
  229.     Result := Result + #27;
  230.     Result := Result + '*'; // Bit image mode
  231.     Result := Result + #33; // 24-dot double density
  232.     Result := Result + Char( Lo( FBitmapData.Width ) );
  233.     Result := Result + Char( Hi( FBitmapData.Width ) );
  234.  
  235.     for LX := 0 to FBitmapData.Width -1 do begin
  236.       for LK := 0 to 2 do begin
  237.         LSlice := 0;
  238.         for LB := 0 to 7 do begin
  239.           LY := ( ( ( LOffset div 8 ) + LK ) * 8 ) + LB;
  240.           LI := ( LY * FBitmapData.Width ) + LX;
  241.  
  242.           LV := False;
  243.           if ( LI < Length( FBitmapData.Dots ) ) then
  244.             LV := FBitmapData.Dots[LI];
  245.  
  246.           LVI := IfThen( LV, 1, 0 );
  247.  
  248.           LSlice := LSlice or ( LVI shl ( 7 - LB ) );
  249.         end;
  250.  
  251.         Result := Result + Chr( LSlice );
  252.       end;
  253.     end;
  254.  
  255.     LOffset := LOffset + 24;
  256.     Result := Result + sLineBreak;
  257.   end;
  258.  
  259.   // *** Restore the line spacing to the default of 30 dots
  260.   Result := Result + #27'3'#30 + sLineBreak + sLineBreak + sLineBreak;
  261. end;
  262.  
  263. function TssESCPosPrintBitmap.RenderBitmapBase64(const base64data: string
  264.   ): rawbytestring;
  265. var
  266.   LOffset     : Integer;
  267.   LX          : Integer;
  268.   LSlice      : Byte;
  269.   LB          : Integer;
  270.   LK          : Integer;
  271.   LY          : Integer;
  272.   LI          : Integer;
  273.   LV          : Boolean;
  274.   LVI         : Integer;
  275. var
  276.   Stream: TMemoryStream;
  277. begin
  278.   // *** load the Bitmap from the base64
  279.   Stream := TMemoryStream.Create;
  280.   try
  281.     if Base64ToStream(base64data.Replace('data:image/png;base64,', ''), Stream) then
  282.       FBitmap.LoadFromStream(Stream);
  283.   finally
  284.     Stream.Free;
  285.   end;
  286.  
  287.   // *** Convert the bitmap to an array of B/W pixels
  288.   LoadBitmapData();
  289.  
  290.   // *** Set the line spacing to 24 dots, the height of each "stripe" of the
  291.   // *** image that we're drawing
  292.   Result := #27'3'#24;
  293.  
  294.   LOffset := 0;
  295.   while ( LOffset < FBitmapData.Height ) do begin
  296.     Result := Result + #27;
  297.     Result := Result + '*'; // Bit image mode
  298.     Result := Result + #33; // 24-dot double density
  299.     Result := Result + Char( Lo( FBitmapData.Width ) );
  300.     Result := Result + Char( Hi( FBitmapData.Width ) );
  301.  
  302.     for LX := 0 to FBitmapData.Width -1 do begin
  303.       for LK := 0 to 2 do begin
  304.         LSlice := 0;
  305.         for LB := 0 to 7 do begin
  306.           LY := ( ( ( LOffset div 8 ) + LK ) * 8 ) + LB;
  307.           LI := ( LY * FBitmapData.Width ) + LX;
  308.  
  309.           LV := False;
  310.           if ( LI < Length( FBitmapData.Dots ) ) then
  311.             LV := FBitmapData.Dots[LI];
  312.  
  313.           LVI := IfThen( LV, 1, 0 );
  314.  
  315.           LSlice := LSlice or ( LVI shl ( 7 - LB ) );
  316.         end;
  317.  
  318.         Result := Result + Chr( LSlice );
  319.       end;
  320.     end;
  321.  
  322.     LOffset := LOffset + 24;
  323.     Result := Result + sLineBreak;
  324.   end;
  325.  
  326.   // *** Restore the line spacing to the default of 30 dots
  327.   Result := Result + #27'3'#30 + sLineBreak + sLineBreak + sLineBreak;
  328. end;
  329.  
  330. end.
           
« Last Edit: December 30, 2019, 09:25:50 pm by lainz »

 

TinyPortal © 2005-2018