Wednesday, October 29, 2008

Linux: Install HP LaserJet 1020 on CUPS

First, I thought all printers configure on CUPS as local raw printer should works for windows workstation as long as I use correct printer driver to render the printing image.  The CUPS will just re-route the raw printing image received to the printer.

I have follow this rule to configure two other printers: Panasonic KX-1121 dot matrix printer and Samsung ML-1450 Laser Jet Printer.  Both works flawlessly for years.

I receive a HP LaserJet 1020 printer last night and try to configure it for CUPS.  The printer has only one USB port.  I plug the printer to the Linux machine's USB port and CUPS detect the printer well.  I then install the latest HP LaserJet 1020 printer downloaded from HP web site and configure the printer as usual.  I send a test page to printer.  CUPS receive the printing image and report print job completed successfully but the printer doesn't print anything.  The printer doesn't feed the paper.

I thought it was printer's problem.  To re-confirm the problem again, I attach the printer to my Windows Vista machine directly.  It print perfectly.

I found some interesting things with HP LaserJet 1020 after Google for solutions.

It is a Host-Based Printer

As defined by HP:

The host-based software uses the computer's resources to process print commands and rasterizing data, taking advantage of the computer's memory and processing power. Host-Based printing is a cost-effective printing technology that enables printers to utilize the processing power and memory resources of the PC (or the Host). In comparison, PDL-based printers use the processor and memory resources of the printer.

It utilize the power and resources of PC to render the printing images.  It print much faster than those PDL printer since newer computers process at much faster speeds than most PDL printers processors.

The disadvantages of Host-Based Printers is it no longer accept ASCII text directly from computer as all print images are generated by host operating system's print engine.

This however, is a good news as it reduce the manufacturing cost of the printer.  It also doesn't seems to be a problem for my RAW printer for CUPS service.

It always requires firmware download else it won't print

The printer is one of the cost-reduced HP printers that requires a firmware download before it will operate.  It means every time you switch on the printer, you need to send the firmware image to printer first before you start feeding the printing images.  The HP LJ1020 doesn't has a flash ROM to persist the firmware permanently.  This is the main reason why the printer works well when attach to windows vista machine but not Linux machine.  The HP LJ1010 printer drivers for windows OS take care of the firmware uploading well.

You may only get the N series of HP 1000 printers that has flash ROM equipped for firmware.  For example, HP LaserJet 1020n printer.

Solution: foo2zjs

I only need foo2zjs to upload the firmware to printer when the HP LJ1020 first attach to my Linux box.

  1. Logon to root account.
  2. Download foo2zjs tarball:

    $ wget -O foo2zjs.tar.gz http://foo2zjs.rkkda.com/foo2zjs.tar.gz
  3. Untar it:

    $ tar zxf foo2zjs.tar.gz
  4. Configure foo2zjs:

    $ cd foo2zjs
    $ make

  5. Download HP LaserJet 1020 firmware file:

    $ ./getweb 1020
  6. Install driver, foomatic XML files, and extra files:

    $ make install
  7. Configure hotplug

    $ make install-hotplug
  8. Configure CUPS:

    $ make cups
  9. You may need to switch off and on the HP LJ 1020 printer to activate hot plug upload the firmware.
  10. To check if firmware uploaded:

    $ usb_printerid /dev/usb/lp1
    GET_DEVICE_ID string:
    MFG:Hewlett-Packard;MDL:HP LaserJet 1020;CMD:ACL;CLS:PRINTER;DES:HP LaserJet 1020;FWVER:20050309;

    If you see a string "FWVER:xxxxxxx". it means the firmware has uploaded successfully.

You may now start feeding the raw printing images from your windows workstation to HP Laser Jet 1020.

Saturday, October 11, 2008

Delphi 2009: Array is dead. Long live Array

I start learn the concept of array in high school mathematic subject.  The array has dimensions, 1D, 2D and so on.  The first programming language BASIC I learned also has array.  The concept of mathematic array and programming array match perfectly.  However, there is always a memory restriction using array in 8-bits and 16-bits world.

I start ignore array after I learn object oriented programming and design patterns.  There is always ready classes like TList or TCollection for me to use in OO world.  The OOP concept has poisoned me for years that I should always coding in OO way.  The TList class can do more than array and I almost forget that Object Pascal still has array.

I migrate my Delphi 2007 code to Delphi 2009 when it was launched.  In the migration stage, the most headache part is unicode conversion.  Due to several reasons below:

  1. Some 3rd party components aren't ready for Delphi 2009 yet and some in beta stage.  Even the component makers claim they already ready but it is still new and I don't have confident yet.
  2. My current application persistent storage (database or resource) is in ANSI format.  I need some buffer duration before I port to Unicode.

I will still stay in Delphi 2007 for a while before I am ready to release application compiled in Delphi 2009.

At this stage, I will revise all my code that aren't compatible with Delphi 2009 and amend it to compatible for both Delphi 2007 and Delphi 2009.

I have some classes perform Base16 and Base64 encoding.  These classes using string to as internal storage.  It become a problem in Delphi 2009 as it use WideChar (2 bytes) and the effect is avalanche as other part of source use the encoding classes.  I revise the code and use TBytes (array of byte) as internal storage.

A new problem raise, the lack of solid array knowledge has slow down my day to day coding practice.  My knowledge of array is still stay in high school.  I re-study the array construct in Delphi documentation to strengthen my understanding about it.  Some I already know but some don't.  Below are the result of my study.

Static Array

Static array has fixed memory allocated at compiled time.  For example,

var A, B: array[1..10] of integer;

define A and B as byte array of 10 element.

To initialize a static array:

var C: array[1..3] of integer = (1, 2, 3);

  • Length(A) return 10
  • SizeOf(A) return 40
  • Low(A) return 1
  • High(A) return 10
  • FillChar(A, Length(A), 77) or
    FillChar(A[1], Length(A), 77)
    will fill all elements with value 77
  • Move(A, B, Length(A)) or
    Move(A[1], B[1], Length(A))
    will copy all elements from A to B

Dynamic Array

As it name implied, the size of dynamic array is not fixed at compile time.  It is determine at runtime.  Thus, there are some operations distinct to static array:

var A: array of integer;

A is of type pointer to an array memory storage.  Thus, when we apply any operation against dynamic array, always treat it as pointer to reduce any confusions for operation like FillChar or Move.

Use SetLength to allocate memory storage for dynamic array:

SetLength(A, 10)

A good news is the system will manage the dynamic array storage area.  there is no need to free the storage size allocated via SetLength.

To initialize a dynamic array (undocumented feature):

type
  TDynIntegerArray = array of integer;

var C: TDynByteArray;
begin
  C := TDynByteArray.Create(1, 2, 3, 4);
end;

  • Length(A) return 10
  • SizeOf(A) return 4, same as SizeOf(Pointer).  To get the array physical storage size, use Length(A) * SizeOf(Integer)
  • Low(A) return 0 (Dynamic array always starting from 0)
  • High(A) return 9
  • FillChar(A[0], Length(A), 77)
    will fill all elements with value 77 but
    FillChar(A, Length(A), 77)
    will lead to unexpected result.
  • Move(A[0], B[0], Length(A))
    will copy all elements from A to B but
    Move(A, B, Length(A))
    will lead to unexpected result.

Array assignment

Arrays are assignment-compatible only if they are of the same type. Because the Delphi language uses name-equivalence for types, the following code will not compile:

var A: array[1..10] of integer;
    B: array[1..10] of integer;
    C: array of integer;
    D: array of integer;

begin
  B := A;
  D := C;
end;

To make it works, either do this:

var A, B: array[1..10] of integer;
    C, D: array of integer;
begin
  B := A;
  D := C;

end;

or

type TIntegerArray = array[1..10] of integer;
     TDynIntegerArray = array of integer;

var A: TIntegerArray;
    B: TIntegerArray;
    C: TDynIntegerArray;
    D: TDynIntegerArray;
begin
  B := A;
  D := C;
end;

As static arrays has pre-allocated memory storage, copy-on-write is not employed on static array assignment.  In the above example, B will copy of all elements' value from A.  Changing any value in B[i] will has no effect on A.  A and B has 2 independent storage area.

Unlike static array, dynamic array reference is a pointer.  Thus, B := A will make B point to A's storage area.  The storage area for A is also storage area for B now.  Changing value in A[i] will be reflected immediately on B[i] and vice versa.  Both A and B share same storage area.  To practice copy-on-write for dynamic array, use Copy function

B := Copy(A, 0, Length(A)):

It is not need to allocate storage for B using SetLength prior to Copy.  Now A and B has two distinct storage area. Changing B[i] or A[i] do not affect each other.

Open Array Parameters

Open Array is not static or dynamic array.  It is use as parameters in procedures or functions.

Unfortunately, it has same syntax as dynamic array and always confuse us.  Open array parameter must always declares as "array of <baseType>".  If we declare a type for it, it is not an open array.

This is open array parameter:

procedure MyProc(A: array of integer);
begin
end;

This is not open array parameter:

type
  TIntegerArray = array of integer;

procedure MyProc(A: TIntegerArray);
begin
end;

Open array has several rules:

  • They are always zero-based. The first element is 0, the second element is 1, and so forth. The standard Low and High functions return 0 and Length1, respectively. The SizeOf function returns the size of the actual array passed to the routine.
  • They can be accessed by element only. Assignments to an entire open array parameter are not allowed.
  • They can be passed to other procedures and functions only as open array parameters or untyped var parameters. They cannot be passed to SetLength.
  • Instead of an array, you can pass a variable of the open array parameter's base type. It will be treated as an array of length 1.

For example,

procedure MyProc(A: array of integer);
begin
  WriteLn('SizeOf(A)=', SizeOf(A));
  WriteLn('Length(A)=', Length(A));
end;

var A: array[0..9] of integer;
begin
  MyProc(A);
end;

The output is

  • SizeOf(A)=40
  • Length(A)=10

We can pass variable to open array parameter of a routine, it will be treated as a single element array:

var i: integer;
begin
  MyProc(i);
end;

The output is

  • SizeOf(A)=4
  • Length(A)=1

Conclusion

For simple construct, using array is efficient and easy.  It consume less system resources than collection classes.

Thursday, October 09, 2008

Delphi 2009: SizeOf and Length

SizeOf and Length always confuse me.  Some time both function return same result but not always.  There are situations they have same behaviors but not always.

In Delphi 2009 documentation:

SizeOf

function SizeOf(X): Integer;

Returns the number of bytes occupied by a variable or type.

Length

function Length(S): Integer;

Returns the number of characters in a string or elements in an array.

Case 1: Apply on Static Array

For static array, the size of array is statically reserved by compiler.  Thus, SizeOf know the total number of bytes allocated for the array. 

Length will always behave as defined regardless of the element size of array, that is return the number of element in the array.

var A: array[0..9] of byte;

  • SizeOf(A) return 10
  • Length(A) return 10

var B: array[0..9] of char;

  • SizeOf(B) return 20 (in Delphi 2009, char is WideChar of 2 bytes in size)
  • Length(B) return 10

Case 2: Apply on Dynamic Array

Unlike static array, a variable reference dynamic array is of pointer type.  Thus, apply SizeOf on a dynamic array point always return 4, that is same as SizeOf(Pointer) no matter how much memory allocated to the dynamic array at runtime.

Length as always, return the number of elements in array.

var A: array of byte;  // or TBytes
begin
  SetLength(A, 10);
end;

  • SizeOf(A) return 4
  • Length(A) return 10

var C: array of char;  // or TBytes
begin
  SetLength(C, 10);
end;

  • SizeOf(C) return 4
  • Length(C) return 10
  • To get the total number of bytes allocated for a dynamic array, use Length(C) * SizeOf(Char) and it return 20.

Case 3: Apply on Open Array

Open array parameters allow arrays of different sizes to be passed to the same procedure or function. To define a routine with an open array parameter, use the syntax array of type (rather than array[X..Y] of type) in the parameter declaration. For example,

procedure MyProc(A: array of integer);
begin
  WriteLn('SizeOf(A)=', SizeOf(A));

  WriteLn('Length(A)=', Length(A));
end;

declares a procedure called MyProc that takes a integer array of any size.

We may pass either a static or dynamic array to MyProc but not limited to that.

Example 1: Dynamic array

var A: array of integer;
begin
  SetLength(A, 10);
  MyProc(A);
end;

The output is

  • SizeOf(A)=40
  • Length(A)=10

Example 2: Static array

var A: array[0..9] of integer;
begin
  MyProc(A);
end;

The output is

  • SizeOf(A)=40
  • Length(A)=10

Example 3: Static array

begin
  MyProc([0,1,2,3,4,5,6,7,8,9]);
end;

The output is

  • SizeOf(A)=40
  • Length(A)=10

Example 4: Integer variable

Instead of an array, you can pass a variable of the open array parameter's base type. It will be treated as an array of length 1.

var i: integer;
begin
  MyProc(i);
end;

The output is

  • SizeOf(A)=4
  • Length(A)=1

Example 5: Open Array or Dynamic Array

It is easy to confuse about the array syntax.  The syntax of open array parameters resembles that of dynamic array types, but they do not mean the same thing.  If you declare a type identifier for an array, it will be treated as dynamic array:

type
  TIntegerArray = array of integer;

procedure MyProc(A: TIntegerArray);
begin
  WriteLn('SizeOf(A)=', SizeOf(A));
  WriteLn('Length(A)=', Length(A));
end;

var A: TIntegerArray;
begin
  SetLength(A, 10);
  MyProc(A);
end;

The output is

  • SizeOf(A)=4
  • Length(A)=10

Reference:

  1. Open array parameters and array of const

Friday, October 03, 2008

Delphi 2009: Unicode

W1050 WideChar reduced to byte char in set expressions.  Consider using 'CharInSet' function in 'SysUtils' unit

This compiler warning is commonly encounter in Delphi 2009.  We should change all coding using characters set with CharInSet.

For example:

Delphi 2007: A in ['a', 'b']

Delphi 2009: CharInSet(A, ['a', 'b'])

However, doing such changes will make the code not compatible with Delphi 2007.  We may construct a CharInSet function for Delphi 2007:

unit D2009_to_D2007;

interface

{$if CompilerVersion <= 18.5}
function CharInSet(C: AnsiChar; const CharSet: TSysCharSet): Boolean; inline;
{$ifend}

implementation

{$if CompilerVersion <= 18.5}
function CharInSet(C: AnsiChar; const CharSet: TSysCharSet): Boolean;
begin
  Result := C in CharSet;
end;
{$ifend}

end.

The $if directive will restrict the code available for Delphi 2007 or older only.

W1057 Implicit string cast from 'ShortString' to 'string'

The following code will raise a famous W1057 compiler warning in Delphi 2009:

var s: ShortString;
    t: string;
begin
  t := s;
end;

To eliminate the warning, just cast the ShortString variable as String and it is compatible with both Delphi 2007 and 2009:

var s: ShortString;
    t: string;
begin
  t := string(s);
end;

W1057 Implicit string cast from 'AnsiString' to 'string'

The following code will raise a famous W1057 compiler warning in Delphi 2009:

var s: AnsiString;
    t: string;
begin
  t := s;
end;

To eliminate the warning, just cast the AnsiString variable as String and it is compatible with both Delphi 2007 and 2009:

var s: AnsiString;
    t: string;
begin
  t := string(s);
end;

W1058 Implicit string cast with potential data loss from 'string' to 'AnsiString'

We must take extra care for this warning.  Although using the famous cast method may solve the problem, we should only use it only if we understand what we are doing.

For example:

var s: AnsiString;
    t: string;
begin
  s := t;
end;

If we are very sure that t will contain only ansi string value, then we can perform the cast as follow safely:

var s: AnsiString;
    t: string;
begin
  s := AnsiString(t);
end;

This warning is usually happens in code invoke legacy external library (*.DLL) that supports PAnsiChar data type only.

Delphi 2009: Project Management

  1. In Delphi 2007,if we compile our application with runtime package that has dot in the package file name (eg: SQL.patch.vcl.dcp), an exception EPackageError will prompt. This issue no longer exist in Delphi 2009. (Reference: QC#48394, Package naming cause problem in runtime)
  2. Project file (.dproj) always turn to modifed state when first open. (Reference: QC#66781, https://forums.codegear.com/thread.jspa?threadID=2818&tstart=0)
  3. Dot (.) is not allow in LIB Prefix. (Reference: QC#66782, https://forums.codegear.com/thread.jspa?threadID=2819&tstart=0)
  4. Incorrect status of inherited value in Build configuration after upgrade D2007 Project file (*.dproj) (Reference: QC#66786, https://forums.codegear.com/thread.jspa?threadID=2829&tstart=0)
  5. Build Configuration:
    1. New Build configuration implement new Option Set introduced in Delphi 2009.
    2. For our development environment, we define build configuration as below:
      1. Base (..\build\optset\Base.optset)
        1. Debug (..\build\optset\Debug.optset)
        2. Profile (..\build\optset\Profile.optset)
        3. Release (..\build\optset\Release.optset)

          Capture_thumb51
  6. RC Files
    1. Remove library.rc file from Project Manager
    2. Add "..\build\rc\library.rc" into Project
    3. Remove "{$R 'rc\library.res' 'rc\library.rc'}" on first line of *.dpk files
    4. Add "{$R 'library.res'}" below "{$R *.res}"

      Capture_thumb6
    5. Build and Compile
    6. You should see "library.res" appears in Contains node of Project Manager when you re-open the project again next time.

      Capture_thumb7