Alexandr
Alexandr

Reputation: 338

How to print images on TPrinter in real size of image?

Gretings to all!

How to print pictures in Delphi on TPrinter, in real sizes of pictures? From canvas of TImage I have good results, but if I paints on TPrinter canvas, I have BAD result, puctures is too small than real size of bitmap.

Why that happens What I'm need to do for fix bug?

UPDATE

Yes, I seen question from the hint in the 1st post. I can't use JCL/JVCL code in my project, but I got idea from it.

I create temporary TImage, and calculate dimensions of it in accordance with the factor of printer's DPI:

var
  i, iRow, iCol,        // Counter
  iBorderSize,          // Ident from left/top borders
  iImgDistance,         // Ident between images in grid
  iRows,                // Rows Count
  iColumns,             // Colun count
  iLeft, iTop: Integer; // For calc
  bmp: TBitmap;
  bStop, bRowDone, bColDone: Boolean;
  Img1: TImage;
  scale: Double;

  function CalcY: Integer;
  begin
    if (iRow = 1) then
      Result := iBorderSize
    else
      Result := iBorderSize + (iImgDistance * (iRow - 1)) +
        (bmp.Height * (iRow - 1));
  end;

  function CalcX: Integer;
  begin
    if (iCol = 1) then
      Result := iBorderSize
    else
      Result := iBorderSize + (iImgDistance * (iCol - 1)) +
        (bmp.Width * (iCol - 1));
  end;

begin
  iBorderSize := StrToInt(BorderSizeEdit.Text);
  iImgDistance := StrToInt(ImgsDistanceEdit.Text);
  iRows := StrToInt(RowsCountEdit.Text);
  iColumns := StrToInt(ColCountEdit.Text);
  iRow := 1;
  iCol := 1;
  iLeft := iBorderSize;
  iTop := iBorderSize;

  if Printer.Orientation = poPortrait then
    scale := GetDeviceCaps(Printer.Handle, LOGPIXELSX) /
      Screen.PixelsPerInch
  else
    scale := GetDeviceCaps(Printer.Handle, LOGPIXELSY) /
      Screen.PixelsPerInch;

  bmp := TBitmap.Create;
  Img1 := TImage.Create(nil);
  Img1.Height := Trunc(Printer.PageHeight / scale); //Calc canvas size
  Img1.Width := Trunc(Printer.PageWidth / scale); //Calc canvas size
  Img1.Canvas.Brush.Color := clWhite;
  Img1.Canvas.FillRect(Rect(0, 0, Img1.Width, Img1.Height));
  try
    bmp.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Source.bmp');
    for i := 1 to 18 do
    begin
      if (iRow <= iRows) then
      begin
        iTop := CalcY;
        iLeft := CalcX;
        Img1.Canvas.Draw(iLeft, iTop, bmp);
        if not((iRow = iRows) and (iCol = iColumns)) then
        begin
          if (iCol = iColumns) then
          begin
            Inc(iRow);
            iCol := 1;
          end
          else
            Inc(iCol);
        end
        else
        begin
          PrintImage(Img1, 100);
          iRow := 1;
          iCol := 1;
          Img1.Canvas.Brush.Color := clWhite;
          Img1.Canvas.FillRect(Rect(0, 0, Img1.Width, Img1.Height));
        end;
      end;
    end;
  finally
    FreeAndNil(bmp);
    FreeAndNil(Img1);
  end;
end;

And draw it on TPrinter.Canvas.

You can see results below:

You can see results below:

Results is good, but not perfect.

As you can see, in the last column, all images are drawn not to the end, some part misses off the paper and not drawn.

I think it's happens because I use the Trunc to get integer part of double when I'm calculate dimensions of TImage.Canvas in accordance with the factor of printer's DPI.

By experiments I know value 0.20. 0.20 is a part of last column images, in pixels, that not drawn. If I change code, that gets scale factor by this:

  if Printer.Orientation = poPortrait then
    scale := GetDeviceCaps(Printer.Handle, LOGPIXELSX) /
      Screen.PixelsPerInch - 0.20
  else
    scale := GetDeviceCaps(Printer.Handle, LOGPIXELSY) /
      Screen.PixelsPerInch - 0.20;

I have that, what I need:

I have that, what I need:

I think the value 0.20 isn't a constant and it will change on every PC. How to calculate this value? What need to solve this problem?

Upvotes: 4

Views: 14434

Answers (3)

MERLIN
MERLIN

Reputation: 436

Delphi Basics link was also helpful : http://www.delphibasics.co.uk/RTL.asp?Name=printer&ExpandCode1=Yes

on form : drag n drop TPrintDialog from your Tool Palette

and manually add this to the uses clause under [Implementation]

uses printers;  // Unit containing the printer command

With that and this post I was able to print directly to any printer at the size I wanted for images or text. There is no need to call the bitmap or assign the TPrinter once you have added the unit above. Just draw directly to the canvas in your PC printer queue.

procedure TForm1.cmdPrintCircleClick(Sender: TObject);
 var
   xx, yy, mySize : integer;
   //printer1 : TPrinter;
 begin
  // create image directly on Printer Canvas and print it
  //Ellipse( X-(Width div 2), Y-(Height div 2), X+(Width div 2), Y+(Height div 2));
  if PrintDialog1.Execute then
   try
    with Printer do
     begin
      if Printer.Orientation = poPortrait then
       begin
         // represents 1/2 US-inch relative to Portrait page size 8.5 x 11
         mySize := Trunc(PageWidth / 8.5 / 2);
      end
      else
       begin
         // represents 1/2 US-inch relative to Landscape page size 11 x 8.5
         mySize := Trunc(PageHeight / 8.5 / 2);
      end;

      xx := Trunc(PageWidth / 2);
      yy := Trunc(PageHeight / 2);

      // Start printing
      BeginDoc;

      // Write out the ellipse  // create one-inch black circle
      Canvas.Brush.Color := clBlack;
      Canvas.Ellipse(xx - mySize, yy - mySize, xx + mySize, yy + mySize);

      // Finish printing
      EndDoc;
    end;
   finally
   end;

end;

Upvotes: 1

Pieter B
Pieter B

Reputation: 1967

The issue you're running into is that there really isn't a "real size" of an image, it's all relative. The printer often has a lot higher resolution then your monitor and that's why pictures look small.

Your monitor has often a resolution of 96 dpi and normal printer has a resolution of 600 dpi which means your image prints in its real size it just looks small because a printer can put a lot more dots in the same space then a monitor can.

Upvotes: 1

Glenn1234
Glenn1234

Reputation: 2582

The basic problem here is one of scaling. More or less, figure out how much to expand the resolution of the image and then stretchdraw it to the printer canvas. Something like this gets the image stretched out to the dimension of the printer canvas.

procedure TForm1.Button2Click(Sender: TObject);
  var
    MyRect: TRect;
    scale: Double;
  begin
    if PrintDialog1.Execute then
      begin
        Printer.BeginDoc;
        scale := Printer.PageWidth / Bitmap1.Width;
        ShowMessage(FloatToStr(scale));
       { horizontal pixels, vertical pixels, bit depth 600 x 600 x 24}
        MyRect.Left := 0;
        MyRect.Top := 0;
        MyRect.Right := trunc(Bitmap1.Width * scale);
        MyRect.Bottom := trunc(Bitmap1.Height * scale);
        Printer.Canvas.StretchDraw(MyRect, Bitmap1);
        Printer.EndDoc;
      end;

Of course, you have to check "Right" and "Bottom" to make sure they don't exceed your PageWidth and PageHeight depending on the type of scaling you use (6.25 or 600/96 seems fine for simply making an image the same relative size as the screen, assuming those numbers match your printer and screen), assuming you want to keep the image to one page and not mosaic pieces of it onto multiple pages.

I don't know if this works entirely since I don't have a varied number of devices (i.e. different DPIs) to test both orientations on, but this seems to be what you want to get both DPI numbers dynamically.

if Printer.Orientation = poPortrait then
   scale := GetDeviceCaps(Printer.Handle, LOGPIXELSX) / PixelsPerInch
else
   scale := GetDeviceCaps(Printer.Handle, LOGPIXELSY) / pixelsperinch;

Then of course, you multiply like above.

Upvotes: 4

Related Questions