how to print a canvas

Viewed 497

I have a canvas which I draw to, to make slips for purchases made, I am trying to print the canvas out but am getting an error

'Project Project2.exe raised exception class EConvertError with message 'Cannot assign a TCanvasD2D to a TCanvasGdiPlus'

where am I going wrong?

var
printDialog : TPrintDialog;
myPrinter   : TPrinter;

begin



printDialog := TPrintDialog.Create(Form5);

if printDialog.Execute then
begin
  myPrinter := printer;
  with myPrinter do
  begin

    BeginDoc;

    Canvas.Assign(slipPnl.Canvas);

    EndDoc;
  end;
end;
end;
1 Answers

The error suggest that probable causes is that source canvas is DirectX accelerated and printer is usually just SW GDI rendered which might prevent direct Assign due to Rendering Context incompatibility (the objects are too foreign to eachother).

Another possibility is that Assign also changes the resolution which I expect is not allowed for printer Canvas ...

Why not CopyRect and/or StretchDraw instead?

So copy the contents of source Canvas into printer Canvas or temp Canvas instead of Assign it directly...

OK here is VCL C++ example of what I mean (adapted from mine standard printing routine I use in my apps):

void TMain::print(TCanvas *src,int xs,int ys)
    {
    // just check if any printers available first
    TPrinter *prn = Printer();
    int pxs,pys,psz,i=0;
    if (prn==NULL) i=1;
    if (prn->Printers==NULL) i=1;
    if (prn->Printers->Count<=0) i=1;
    if (i) return;

    // used print dialog component
    TPrintDialog *dlg_print=PrintDialog1;
    dlg_print->Options.Clear();
    dlg_print->Options<<poPrintToFile;
    if (!dlg_print->Execute()) return;

    // printer settings
    prn = Printer();
    pxs=prn->PageWidth;
    pys=prn->PageHeight;

    // create printer page backbuffer
    Graphics::TBitmap *bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    bmp->SetSize(pxs,pys);

    // compute scale
    float zoom,xx,yy;
    xx=pxs/xs;
    yy=pys/ys;
    zoom=xx; if (zoom>yy) zoom=yy;
    xx=xs; xx*=zoom;
    yy=ys; yy*=zoom;

    // copy Incompatible canvas into GDI Canvas (bmp)
    bmp->Canvas->CopyRect
        (
        TRect(0,0,xx,yy),
        src,
        TRect(0,0,xs,ys)
        );

    // bmp->SaveToFile("print.bmp"); // just for debug so I do not need actually print

    prn->BeginDoc();
    for (i=0;i<dlg_print->Copies;i++)
        {
        if (i) prn->NewPage();
        // render GDI Canvas (bmp) to printer page
        prn->Canvas->Draw(0,0,bmp);
        }
    prn->EndDoc();

    // release stuff
    delete bmp;
    }

using BDS2006 Turbo C++ (direct successor to BCB6 and Delphi)... just convert the C++ syntax to Pascal the rest should be the same... The Idea is to use temp bitmap (you might also directly render to printer page instead) and CopyRect instead of Assign ... However you need to pass resolution of the TCanvas as that is not accessible directly (as its usually a property of parent object).

[Edit1]

As Marco van de Voort pointed out you are using Firemonkey which I have no experience with and missed it while answering so in case the above code is not working you can workaround with different approach ...

Simply copy the Canvas content using Winapi see:

and look for bullet #2 with name WinAPI approach in order to make it work you need Handle of the Canvas which should be a accessible property of the Canvas or its parent object. If it is not you can still do this see:

On how to obtain the Handle using Winapi again.

Related