jmc
jmc

Reputation: 610

Delphi - Loading JPEG and printing outputs as a black square?

Having some trouble with printing JPEG images via Delphi TCanvas. Anywhere between 30-50% of the time the JPEG will print out as a black square rather than as it should. Have tried changing many settings to see if there was a particular condition under which it would fail to print but as of the time of writing nothing has worked and the condition still exists - I am not able to tell when the printout may have a black JPEG or when it will print correctly.

Here is the code I am using to print the JPEG to the Canvas.

Screen.Cursor := crHourGlass;
try
  // initialize image
  //>>imgImage := TImage.Create(Self);
  imgImage := TImage.Create(Application);

  // load image from file
  imgImage.Picture.LoadFromFile(sFileNameAndPath);

  // set width and height to that of loaded image
  ////imgImage.Autosize := true;
  ////Printer.Orientation := poPortrait;
  // Header
  Printer.Canvas.Font.Height := MulDiv(GetDeviceCaps(Printer.Canvas.Handle, LOGPIXELSY), 12, 72);
  Printer.Canvas.Font.Name := 'Courier New';

  // Determine height and width of 1 printer-character
  iDeltaW := Printer.Canvas.TextWidth('X');
  iDeltaH := Printer.Canvas.TextHeight('X');

  // ------------------------------
  // Method #1 - columns and lines
  // ------------------------------
  // what column to printing from
  iFromLeftMargin := iLeft * iDeltaW;

  // what line to print from
  iFromTopOfPage := iTop * iDeltaH;

  // ------------------------------
  // Method #2 - pixels
  // ------------------------------
  iPPW := Printer.PageWidth;
  iPPH := Printer.PageHeight;
  iIPW := imgImage.Picture.Width;

  ePXW := iPPW / iInvImageWidth;   
  ePXH := iPPH / iInvImageHeight;  
  //~//iFromLeftMargin := Ceil(iLeft * pxW);
  //~//iFromTopOfPage  := Ceil(iTop  * pxH);
  iFromLeftMargin := Ceil(iLeft * ePXW);
  iFromTopOfPage  := Ceil(iTop  * ePXH);

  // Set printed bitmap width
  iPrintedImageWidth := MulDiv(iPPW, iIPW, iInvImageWidth);  
  // Set printed bitmap height to maintain aspect ratio
  iPrintedImageHeight := imgImage.Picture.Height * iPrintedImageWidth DIV 
    imgImage.Picture.Width; // maintain aspect ratio of bitmap

  Bitmap := TBitmap.Create;
  try
    Bitmap.Width  := imgImage.Picture.Width;
    Bitmap.Height := imgImage.Picture.Height;
    Bitmap.PixelFormat := pf24bit;
    Bitmap.IgnorePalette := False;
    // Convert JPEG (GIF, or whatever) to BMP
    Bitmap.Canvas.Draw(0, 0, imgImage.Picture.Graphic);

    // Print Image
    PrintBitmap(Printer.Canvas,
      Rect(iFromLeftMargin, iFromTopOfPage,
           iFromLeftMargin + iPrintedImageWidth,
           iFromTopOfPage  + iPrintedImageHeight),
           Bitmap);
  finally
    // free bitmap memory
    Bitmap.Free;
  end;
  // free image memory
  imgImage.Free;
finally
  Screen.Cursor := crDefault;
end;

If anyone had any ideas it would be much appreciated!

Regards, James

EDIT: code for the PrintBitmap method below. I have taken the advice of saving the bitmap to disk to view it as it is being generated and the it is never saved as a black square even when the printed output is a black square. I hope this indicated the problem is in the PrintBitmap code below.

procedure PrintBitmap(Canvas: TCanvas; DestRect: TRect; Bitmap: TBitmap);
var
  BitmapHeader: pBitmapInfo;
  BitmapImage: POINTER;
  HeaderSize: DWORD;
  ImageSize: DWORD;
begin
  GetDIBSizes(Bitmap.Handle, HeaderSize, ImageSize);
  GetMem(BitmapHeader, HeaderSize);
  GetMem(BitmapImage,  ImageSize);
  try
    GetDIB(Bitmap.Handle, Bitmap.Palette, BitmapHeader^, BitmapImage^);
    StretchDIBits(Canvas.Handle,
              DestRect.Left, DestRect.Top,     // Destination Origin
              DestRect.Right  - DestRect.Left, // Destination Width
              DestRect.Bottom - DestRect.Top,  // Destination Height
              0, 0,                            // Source Origin
              Bitmap.Width, Bitmap.Height,     // Source Width & Height
              BitmapImage,
              TBitmapInfo(BitmapHeader^),
              DIB_RGB_COLORS,
              SRCCOPY);
  finally
    FreeMem(BitmapHeader);
    FreeMem(BitmapImage);
  end;
end; {PrintBitmap}

Unfortunately this code was written by someone who no longer works at my company and I am only trying to fix an existing issue.

Upvotes: 2

Views: 3605

Answers (1)

Francesca
Francesca

Reputation: 21640

It's hard to tell with the snippet you posted. You should post some self sufficient working code (compilable and runnable).
There is no local var declaration for instance, no Begindoc/EndDoc.

Now, after adding the local declarations, there is a bunch of compiler warnings for Uninitialized Variables like iTop or iLeft. If those are left with random garbage, it could be that you don't draw where you think you do.

You also assume that you always have a pf24bit format which is not guaranteed at all. As you don't specify if the same JPG can print OK or fail randomly, or if it is with different JPGs that you have problems, format conversion problems cannot be excluded.

I would also suggest that you install one of the free available Image Debug Visualizers, place a break point before PrintBitmap and see if the local Bitmap is correct before printing.

Then let us see what is in the PrintBitmap procedure... Did you try to directly draw on the Printer Canvas?

Update:
The PrintBitmap code seems legit except that it does not check for any return code:

if not GetDIB(...) then
  raise [...];
if GDI_ERROR = StretchDIBits(...) then
  RaiseLastOSError;

The problem might come from the Printer/Driver.
Did you try with another printer or with another driver?
Also, make sure your printer is ready...

Upvotes: 3

Related Questions