Link to home
Start Free TrialLog in
Avatar of jonwcroft
jonwcroft

asked on

Printing bitmaps in Delphi 4

I can't seem to be able to print bitmaps reliably in Delphi 4. My code is as follows:

procedure TForm1.BitBtn1Click(Sender: TObject);
var
  i : integer;
  bm : tbitmap;
begin
  printer.BeginDoc;

  for i := 1 to 20 do
  begin
    bm := tbitmap.create;
    try
      bm.handletype := bmDIB;
      bm.pixelformat := pf32bit;
      bm.width := 1000;
      bm.height := 100;
      bm.canvas.font.color := clBlack;
      bm.canvas.font.size := 40;
      bm.canvas.font.name := 'ARIAL';
      bm.canvas.textout(0, 0, 'Using tbitmap ' +inttostr(i));

      printer.canvas.textout(1000, 100 + 100*i, 'Using textout ' + inttoStr(i));
      printer.Canvas.Draw(100,100 + 100 * i,bm);
    finally
      bm.free;
    end;
  end;

  printer.EndDoc;
end;

The printout should have two columns, the first with text printed via a tbitmap, the second with text printed directly. However, bitmaps in the first column seem to randomly go missing. I've tried using an HP Laserjet 6L and an IBM 4039 LaserPrinter Plus both  with the same result. Using Acrobat 2.11 writer causes a GPF.

Is there something wierd going on here, or am I being totally stupid?

Any help would be greatly appreciated.

Regards,

Jon.
Avatar of RBertora
RBertora
Flag of United Kingdom of Great Britain and Northern Ireland image

it sounds like something is screwing up there, why don't you toss in a
 except on excetption do
 begin
   showmessage('haha!');
  end;
 end;
 
forget the finally freeing for now... if haha is displayed then use the trace to see which line is bombing out...
 
if this is the case then write another comment and tell us which one...

Avatar of jonwcroft
jonwcroft

ASKER

RBertora:

Thank-you for your comment.

The crash is a white box "An error has occurred in your application" job. Certainly not trapped by "try except" & "on except do"

The line that causes the crash is the "printer.canvas.draw(100, 100 + 100 * i, bm)" - I know this because the program does not crash when the line is removed. Don't forget that the program only crashes when printing to Acrobat - bitmaps dissappear when printing to normal printers.

This is only a simple little program that you can cut and paste into a new delphi 3/4 application - why don't you try it?

Regards,

Jon.

I could not even run this in Run mode. I had to step through it. The first time, it showed 2-6 and 12-20. The second time, I got 2-20. Some process is not quite finished after the Create. I added a Application.Processmessages; line after the create and all worked fine.

CalvinDay:

Thank-you for your answer.

I tried your suggestion and got it to work only by adding "processmessages" before and after the printer.canvas.draw line.

I want to reopen this question because I'm sure that there must be a better answer than just chucking in processmessages everywhere (particularly as this will routine will become part of a very large multithreading application) and I'm not totally convinced that it will work with a large veriety of bitmaps.

Obviously the VCL does not work completely propperly when printing DIBS: do Borland fix bugs like these and supply patches?

If no-one comes up with a better answer within a few days then I will give you the points.

Good point. I have seen this happen when the setting the pallete. It seems to trigger a flurry of messages.
Epsylon:

Thank-you for your comment.

In fact I have tried something simillar using StretchDIBits with some success.

But what is the point of setting TBITMAP.HANDLETYPE to bmDIB if not so that you can print reliably? I have checked the VCL source and can not find any trace of StretchDIBits.

Lets face it: this is a dirty great big omission in the VCL: bmDIBs have not been implemented propperly in the TBITMAP.DRAW method.

I shall award the points to CALVINDAY unless someone can explain the point of HANDLETYPE=bmDIB, or can deny or confirm that this is a VCL omission/bug.
Quoted from the Delphi help:

TBitmap.HandleType "Indicates whether the bitmap is a DDB, Device Dependent Bitmap, or a DIB, Device Independent Bitmap."


bmDDB:

This means that the actual data stored in the bitmap is in a format that is asociated with a certain device. Some devices can handle it, others not.

bmDIB:

This means that the actual data stored in the bitmap is universal. Whatever device wants to use the bitmap should convert it for its own use. TPrinter provides a canvas for that purpose.


Did you try TCanvas.StretchDraw?
See TBitmap.PixelFormat := pfDevice too....

Epsylon.
Epsylon:

Thank-you for your comment. Yes, I have read the same section of help several times myself.

A note for Borland:
Why should I care how bitmaps are stored? I just want them to work.

I understood that the practical upshot of using bmDIBs are that they print correctly, and that bmDDBs are faster.

I would have thought that the tbitmap should be able to detect an incompatible canvas and convert itself to a DIB should it need to. Whatever they should always work.

This not my question. My problem is that even bmDIBs, randomly don't work! Also when trying StretchDraw, or whatever pixelformat.

CalvinDay:
Repost your orriginal answer tomorrow, and I'll give you the points.
Is this question closed? I mean: has a solution already been found?

Alx
ASKER CERTIFIED SOLUTION
Avatar of CalvinDay
CalvinDay

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
simonet,
The question is still really not answered. I just gave jonwcroft a hack (application.processmessages) to keep his problem from happening. I don't think we really know why it happens.
Give this one a shot. It uses GetDIBSizes and GetDIB:

procedure PrintBitmap(BitmapOut : TBitmap);
var
  Info: PBitmapInfo;
  InfoSize: Integer;
  Image: Pointer;
  ImageSize: DWORD;
  YPos   : integer;
begin
  inherited;
  with BitmapOut do
  begin
    { Call GetDIBSizes to get the Info header and image size }
    GetDIBSizes(Handle, InfoSize, ImageSize);
    { Allocate memory for the info header }
    Info := AllocMem(InfoSize);
    try
      { Allocate memory for the Image }
      Image := AllocMem(ImageSize);
      try
        { Retrieve the bitmap bits in device-independent format, the
          palette  adn the Info header }
        GetDIB(Handle, Palette, Info^, Image^);
        with Info^.bmiHeader do begin
        { Call StretchDIBits to print the bitmap }
          Printer.title := 'Resistências dos Materiais - Diagramas';
          Printer.BeginDoc;
          Printer.title := 'Resistências dos Materiais - Diagramas';
          YPos := PrintHeader;
          try
            StretchDIBits(Printer.Canvas.Handle, 0, YPos, Printer.PageWidth,
              Printer.PageHeight - YPos, 0, 0, biWidth, biHeight, Image, Info^,
              DIB_RGB_COLORS, SRCCOPY);
          finally
            Printer.EndDoc;
          end;
        end;
      finally
        FreeMem(Image, ImageSize); // Free the memory allocated
      end;
    finally
      FreeMem(Info, InfoSize);
    end;
  end;
end;


Cheers,

Epsylon.
Epsylon,

The reason I asked if this question had already been answered is because I was planning on posting that code you just posted.

That's code I wrote for an application I developed years ago and had just used it to answer another similar question. You didn't even bother to remove the sentences that are in Portuguese and make up the header on the printed bitmap.

I relly resent you posting this code.

Alex
You are right, Alex, I should have at least have mentioned your name. But as you say this question is CalvinDay's and I was just collecting stuff on this subject to get it all solved. I'm sorry about it. Hope you are not to mad at me.
Jon, please treat that comment as if it came from Alex.
Thanks for your help guys.

In the end I used the StretchDIBits thing, and got it to work reliably.

As far as HANDLETYPE = bmDIB is concerned, I shall lodge a bug report with Borland.... (I wonder if they'll take any notice).

Oh bugger, I guess I'll have to help loads of other people before I can post another tricky one..
You can't use points you get by answering questions for asking your own questions.
eh?

So what is the point of the points system? Can you cash your points in for money or something?


Nope!