Categories
Delphi

Sending Bitmap to Epson printer using Delphi and ESC/POS

I have previously tried to use http://nicholas.piasecki.name/blog/2009/12/sending-a-bit-image-to-an-epson-tm-t88iii-receipt-printer-using-c-and-escpos/ and the attached Delphi translation to send a TBitmap directly to Epson TM T20, TM T80 and TM88 printers. Unfortunately it never worked.

Recently I needed to print dynamically created QR codes using the printers so it was time to revisit this. I worked directly off the C# sources provided and created a Delphi class to replicate this.

I’ve added this to a GIT repository on Bitbucket in case anyway needs to use it in future.

Project Home: https://bitbucket.org/bernd_summerswell/delphi_escpos_bitmap/

Example Usage:

This uses the Synaser library from Synapse to send the buffer directly to a COM Port.

uses
  synaser,
  ssESCPOSPrintBitmap;

var
  LBuffer : string;
  LDevice : TBlockSerial;
begin
  LBuffer := #27'@'; // Initialise the printer
  LBuffer := LBuffer + _ESCPosPrintBitmap().RenderBitmap( 'pathtobitmapfile' );
  LBuffer := #29'V'#1 // Paper cut full

  // Send LBuffer to Printer
  LDevice := TBlockSerial.Create();
    try
      LDevice.Config( 115200, 8, 'N', 1, False, False );
      LDevice.Connect( 'COM7' );
      LDevice.SendString( LBuffer  );
    finally
      LDevice.Free();
    end;
end.
Categories
Delphi

TDocVariant CRUD Operations

This is based on http://blog.synopse.info/post/2014/02/25/TDocVariant-custom-variant-type and the corresponding forum http://synopse.info/forum/viewtopic.php?id=1631

Create

A TDocVariant custom variant type is declared as a Variant:

var
 LDocVariant : Variant;

The TDocVariant can be created as an object or from a JSON string. You can use the _Obj/_ObjFast, _JSON/_JSONFast and _Arr/_ArrFast methods to create the TDocVariant.

LDocVariant := _ObjFast( [ 'name', 'bernd' ] );
LDocVariant := _JsonFast( '{ "Person": { "First" : { "name" : "bernd" } } }' );
LDocVariant := _ArrFast( [ _ObjFast( [ 'name', 'bernd' ] ),
                           _ObjFast( [ 'name', 'bob' ] ) ] );

Read

You can access items in the TDocVariant variable dynamically:

Writeln( LDocVariant.name );
Writeln( LDocVariant.Person.First.Name )
Writeln( LDocVariant._(0).name );

Each of these will output “bernd”.

A TDocVariant will also be cast as JSON string when used in place of a string, for example, using the examples above, calling the following on each respectively:

Writeln( LDocVariant );

will output the following:

{"name":"bernd"}
{"Person":{"First":"bernd"}}
[{"name":"bernd"},{"name":"bob"}]

For arrays you also have _Count so you can iterate through the array:

for LIndex := 0 to LDocVariant._Count - 1 do
  Writeln( LDocVariant._(LIndex).name );

Update

Updating items and properties is really easy:

LDocVariant.name := "Bernd";
LDocVariant.Person.First.name := "Bernd";
LDocVariant._(1).name := "Bob";

You can add objects, array items or values to existing items as well.

LDocVariant.age := 31;
LDocVariant.Person.Next := _ObjFast( ['name','bob'] );
LDocVariant.Person.Next.age = 43;
LDocVariant.Add( _ObjFast( [ 'name', 'ned' ] ) );

Delete

All TDocVariant variables have the Delete method to remove an item from the object or array:

LDocVariant.Delete( 'name' );
LDocVariant.Person.Delete( 'Next' );
LDocVariant.Delete( 1 );

 

Categories
Delphi

Using TDocVariant for settings

Arnaud from Synopse has just released a great new addition to the mORMot framework. TDocVariant is custom variant type which integrates effortless with JSON.

One area that this can be implemented is for the settings of an application. Usually an INI file is the easiest way to implement settings for an application, especially when it is a small utility. INI files mean that you can quickly edit the settings without needing a special editor. Unfortunately arrays, lists and collections are difficult to store in INI files without some sort of pre-determined structure.

JSON eliminates this problem, and allows a truly flexible structure. Now you can use the TDocVariant class to easily read and write the settings as well as access them in your application.

For the snomSwitcher application, the settings are stored in the following format:

{
"PhoneSettings":{
"IP":"192.168.1.100",
"Username":"admin",
"Password":"admin",
"PhoneType":"Snom 300"
},
"Users":[
{
"DisplayName":"User 1",
"Account":"101",
"Password":"101",
"Registrar":"your.voip.provider"
},
{
"DisplayName":"User 2",
"Account":"102",
"Password":"1234",
"Registrar":"your.voip.provider"
},
{
"DisplayName":"User 3",
"Account":"101",
"Password":"101",
"Registrar":"your.voip.provider"
}
]
}

We can load the the settings from file by calling:

FSettings := _JsonFast(  StringFromFile( FSettingsFilename ) );

We can then access the settings easily from anywhere in code:

Writeln( FSettings.PhoneSettings.IP );
Writeln( FSettings.Users._(1).Username );

Categories
Delphi

Running two instances of the same Delphi Service

Recently there was a need to run two instances of the same service written in Delphi on the same server. While you can manually changed the service name in the registry or via the “sc” command, the service will not start up correctly unless the service name matches the TService.Name.

To get around this you can pass the name of the service as a parameter to the executable and dynamically set the Name and DisplayName properties when the service is created.

function GetParamValue( const ASwitch: string; var AValue: string ): Boolean;
var
  LIndex : Integer;
begin
  Result := False;
  for LIndex := 1 to ParamCount do
    if ( StartsText( ASwitch, ParamStr( LIndex ) ) ) then begin
      Result := True;
      AValue := ReplaceText( ParamStr( LIndex ), ASwitch, '' );
    end;
end;

procedure TYourService.ServiceAfterInstall(Sender: TService);
var
  LRegistry : TRegistry;
begin
  LRegistry := TRegistry.Create();
  try
    LRegistry.RootKey := HKEY_LOCAL_MACHINE;
    if ( LRegistry.OpenKey( 'SYSTEMCurrentControlSetServices' + Name, False ) ) then begin
      LRegistry.WriteString( 'ImagePath', ParamStr(0) + ' /name=' + Name );
    end;
  finally
    LRegistry.Free();
  end;
end;

procedure TYourService.ServiceCreate(Sender: TObject);
var
  LNewName  : string;
begin
  if ( GetParamValue( '/name=', LNewName ) ) then begin
    Name          := LNewName;
    DisplayName   := LNewName;
  end;
end;

The AfterInstall event makes sure that the /name parameter is passed to the service each time it starts up.

This should make your Delphi Service more flexible!

Categories
Delphi

Gnu Gettext for Delphi and Woll2Woll

We use GNU Gettext for translation in our Delphi 6 application. We also use the Woll2Woll data-aware components. In our forms we add the following to the OnCreate event:

TranslateComponent( Self );

We also have set up the typical ignores but we still had a problem with the Woll2Wol TwwDBComboBox that was not being populated properly and we could not set it getting the error: “True is not a valid Boolean value”. Turns out that the resourcestrings in DBConsts for the ‘True’ and ‘False’ values had been translated but the ‘Items’ property of the TwwDBComboBox that mapped ‘Yes’ to ‘True’ wasn’t being translated because the string was ‘Yes’#9’True’.

I’ve added the following function to translate the items in the wwDBComboBox so that they map correctly:

uses
  Wwstr,
  gnugettext;

{-------------------------------------------------------------------------------
  Procedure: Translate_wwDBCustomComboBox
  Author:    bvonfintel
  DateTime:  2013.09.12
  Arguments: const AComponent: TwwDBCustomComboBox
  Result:    None
-------------------------------------------------------------------------------}
procedure Translate_wwDBCustomComboBox( const AComponent: TwwDBCustomComboBox );
var
  LIndex      : Integer;
  LTokenIndex : Integer;
  LToken1     : string;
  LToken2     : string;
begin
  for LIndex := 0 to AComponent.Items.Count - 1 do begin
    LTokenIndex := 1;
    LToken1     := _( strGetToken( AComponent.Items[LIndex], #9, LTokenIndex ) );
    LToken2     := _( strGetToken( AComponent.Items[LIndex], #9, LTokenIndex ) );

    AComponent.Items[LIndex] := LToken1 + #9 + LToken2;
  end;
end;
Categories
Delphi

TMethodList for Pre-Generics Delphi

While in the later versions of Delphi we can simply use a generic TList<TMethod> in the earlier versions of Delphi (like Delphi 6 which I am using) we need to override the base TList class to accommodate the methods and implement a TMethodList container. This method list is based on the multicast event handler outlined here: http://delphi.about.com/library/weekly/aa051005a.htm

The most important part of the class is assigning the TMethod to a pointer.

First we create a pointer to a TMethod

type
  PMethod = ^TMethod;

When adding the TMehod to the list we copy the .Data and .Code to the newly created Pointer

  Result := New( PMethod );
  Result^.Code := AItem.Code;
  Result^.Data := AItem.Data;

Later we will need to Dispose of the memory

  if ( Action = lnDeleted ) then begin
    Dispose( Ptr );
  end;

The whole class:

{******************************************************************************}
{                                                                              }
{  Unit: ssMethodList.pas                                                      }
{  Summerswell Core                                                            }
{                                                                              }
{  Copyright (C) 2013 Summerswell                                              }
{                                                                              }
{  Author     : bvonfintel                                                     }
{  Original   : 2013/09/13 11:23:02 AM                                         }
{                                                                              }
{******************************************************************************}
unit ssMethodList;

interface

uses
  Classes;

type
  // *** -------------------------------------------------------------------------
  // *** CLASS: TssMethodList
  // *** -------------------------------------------------------------------------
  TssMethodList = class( TList )
    protected
      procedure Put( AIndex: Integer; AItem: TMethod );
      function  Get( AIndex: Integer ): TMethod;
      procedure Notify(Ptr: Pointer; Action: TListNotification); override;
    public
      function  Add( AItem: TMethod ): Integer;
      property Items[Index: Integer]: TMethod read Get write Put; default;
  end;

implementation

type
  PMethod = ^TMethod;

{-------------------------------------------------------------------------------
  Procedure: CreatePMethod
  Author:    bvonfintel
  DateTime:  2013.09.13
  Arguments: AItem: TMethod
  Result:    PMethod
-------------------------------------------------------------------------------}
function CreatePMethod( AItem: TMethod ): PMethod;
begin
  Result := New( PMethod );
  Result^.Code := AItem.Code;
  Result^.Data := AItem.Data;
end;

{ TssMethodList }

{-------------------------------------------------------------------------------
  Procedure: TssMethodList.Add
  Author:    bvonfintel
  DateTime:  2013.09.13
  Arguments: AItem: TMethod
  Result:    Integer
-------------------------------------------------------------------------------}
function TssMethodList.Add(AItem: TMethod): Integer;
begin
  Result := inherited Add( CreatePMethod( AItem ) );
end;

{-------------------------------------------------------------------------------
  Procedure: TssMethodList.Get
  Author:    bvonfintel
  DateTime:  2013.09.13
  Arguments: AIndex: Integer
  Result:    TMethod
-------------------------------------------------------------------------------}
function TssMethodList.Get(AIndex: Integer): TMethod;
begin
  Result := TMethod( inherited Get( AIndex )^ );
end;

{-------------------------------------------------------------------------------
  Procedure: TssMethodList.Notify
  Author:    bvonfintel
  DateTime:  2013.09.13
  Arguments: Ptr: Pointer; Action: TListNotification
  Result:    None
-------------------------------------------------------------------------------}
procedure TssMethodList.Notify(Ptr: Pointer; Action: TListNotification);
begin
  inherited;
  if ( Action = lnDeleted ) then begin
    Dispose( Ptr );
  end;
end;

{-------------------------------------------------------------------------------
  Procedure: TssMethodList.Put
  Author:    bvonfintel
  DateTime:  2013.09.13
  Arguments: AIndex: Integer; AItem: TMethod
  Result:    None
-------------------------------------------------------------------------------}
procedure TssMethodList.Put(AIndex: Integer; AItem: TMethod);
begin
  inherited Put( AIndex, CreatePMethod( AItem ) );
end;

end.
Categories
Delphi Starter XE2

Delphi Starter XE2: Installing ZEOS

Delphi Starter XE2 does not come with many options to connect to databases. You can however install the Zeos database components, so that you can connect to most common database engines. As per the ZEOS portal you should be able to connect to:

  • ADO
  • Mysql 4.1 to 6.0,
  • Interbase 5 / 6
  • Firebird 1.0 to 2.1
  • Postgres 7 to 8
  • SQLite 2.8 to 3
Categories
Delphi Starter XE2

Delphi Starter XE2: Installing JVCL

Because the installer for JVCL uses dcc32 , you can’t install it into Delphi Starter XE2. This just means that you need to install the packages manually from the IDE.
The following is a brief outline of the steps to install the latest JVCL in Delphi Starter XE2:

Categories
Delphi Starter XE2

Delphi Starter XE2: Installing JCL

Because the installer for JCL uses dcc32 , you can’t install it into Delphi Starter XE2. This just means that you need to install the packages manually from the IDE.
The following is a brief outline of the steps to install the latest JCL in Delphi Starter XE2: