How to use TPrinter

I'm having problems with TPrinter
//code ....
#include <Printers.hpp>
.. . .
TBitmap* PrintScn  = new TBitmap ;
//add graphics to PrintScn
TPrinter *Printer1 = new TPrinter() ;
//PrintDialog stuff
TRect CRect(1, 1, PrintScn->Width, PrintScn->Height) ;
Printer1->Canvas->CopyRect(CRect, PrintScn->Canvas, CRect) ; //generates an 
AV
if(!Printer1->Canvas->HandleAllocated()) ShowMessage("Canvas has no Handle") 
; //is true,
//And I get the same with Printer()   ie
TRect CRect(1, 1, PrintScn->Width, PrintScn->Height) ;
Printer()->Canvas->CopyRect(CRect, PrintScn->Canvas, CRect) ; //generates an 
AV
if(!Printer()->Canvas->HandleAllocated()) ShowMessage("Canvas has no 
Handle") ; //is/true,
//end code
Is "TBitmap* PrintScn  = new TBitmap;"  ok to create a TPrinter object ?
The documentation example just calls Printer() repeatedly without a formal 
instantiation on the heap.
Does this generate just one TPrinter object ?
Why do I get an AV when CopyRect'ing PrintScn to Printer1 and to Printer() .
Is it cos the Canvas has no handle ?
Or
Is it something to do with my printer driver not supporting device dependent 
bit maps ?
John
0
John
8/11/2015 7:01:11 AM
📁 embarcadero.cppbuilder.using
📃 1848 articles.
⭐ 1 followers.

💬 7 Replies
👁️‍🗨️ 779 Views


John wrote:
> TPrinter *Printer1 = new TPrinter() ;
Do not create a new TPrinter object.  Use the Printer() function instead 
to obtain a TPrinter object that is owned by the RTL:
{code}
TPrinter *Printer1 = Printer();
{code}
> TRect CRect(1, 1, PrintScn->Width, PrintScn->Height) ;
Why 1 instead of 0?
> //generates an AV
Are you sure it is actally an AV and not some other exception?  What is the 
actual exception class type, and what does its Message actually say?
> Is "TBitmap* PrintScn  = new TBitmap;"  ok to create a TPrinter object ?
The two have nothing to do with each other.  But yes, you can create a TBitmap 
and draw it onto a TPrinter canvas.
> The documentation example just calls Printer() repeatedly without a
> formal instantiation on the heap.
There is a TPrinter object allocated on the heap.  It is allocated inside 
of Printer() the first time it is called, and then repeated calls return 
the same object.  The object is freed when the app is shutdown.
> Does this generate just one TPrinter object ?
Yes.
> Why do I get an AV when CopyRect'ing PrintScn to Printer1 and to
> Printer() .
Cant answer that without seeing the actual error you are seeing.
> Is it cos the Canvas has no handle ?
Hard to say.  What is the value of the TPrinter's PrinterIndex property when 
you get the error?
> Or Is it something to do with my printer driver not supporting device
> dependent bit maps ?
Should not be
-- 
Remy Lebeau (TeamB)
0
Remy
8/11/2015 5:37:59 PM
Remy wrote -
> Do not create a new TPrinter object.  Use the Printer() function instead
> to obtain a TPrinter object that is owned by the RTL:
>
> {code}
> TPrinter *Printer1 = Printer();
> {code}
Noted .
>> TRect CRect(1, 1, PrintScn->Width, PrintScn->Height) ;
> Remy asked  Why 1 instead of 0?
No particular reason !
> //generates an AV
>
> Are you sure it is actally an AV and not some other exception?  What is 
> the
> actual exception class type, and what does its Message actually say?
 TPrinter *Printer1 = Printer() ;
(code)
TRect CRect(1, 1, PrintScn->Width, PrintScn->Height) ;
(code)
try
{
Printer1->Canvas->CopyRect(CRect, Form1->PrintScn->Canvas, CRect) ;
 //  Printer1->Canvas->Draw(1,1, Form1->PrintScn) ;  //generates same AV
 }
catch(Exception &EPrinter)
 {
 unsigned int Error = GetLastError() ;
 msg = "Exception when copying PrintScn to the Printer canvas. Msg is \"" ; 
msg += EPrinter.Message ; msg += "\"" ;
 msg += ".  Printer->PrinterIndex = " ;  msg += Printer1->PrinterIndex ;
 if(Printer1->Canvas->HandleAllocated()) msg += ". No canvas handle" ;
 ShowMessage(msg.c_str()) ;
 }
catch(...)
 {
 ShowMessage("Caught unspecified exception") ;
 }
The above generates :-
Exception when copying PrintScn to the Printer canvas. Msg is "Printer is 
not currently printing".  Printer->PrinterIndex = 0
I have 3 printer devices.  They generate PrinterIndex'es of 0, 1 and 2.
John commented
>> The documentation example just calls Printer() repeatedly without a 
>> formal instantiation on the heap.
Remy replied
> There is a TPrinter object allocated on the heap.  It is allocated inside
> of Printer() the first time it is called, and then repeated calls return
> the same object.  The object is freed when the app is shutdown.
Pity this is not stated as clearly and simply in the Help documentation !
John
0
John
8/12/2015 1:11:30 PM
John wrote:
> catch(Exception &EPrinter)
> {
> unsigned int Error = GetLastError() ;
GetLastError() is not guaranteed to work inside of an exception handler like 
that.  It is only meaningful when called *immediately* after a failed API 
call.  Once an exception is thrown, the act of creating the exception and 
throwing it can reset the error code that GetLastError() returns.
> The above generates :-
> Exception when copying PrintScn to the Printer canvas. Msg is "Printer
> is not currently printing".
That is not an AV (AccessViolation when trying to accessing invalid memory). 
 That is a standard TPrinter error.  It means you did not call TPrinter::BeginDoc() 
to start a new print job before sending data to the printer Canvas, eg:
{code}
TPrinter *Printer1 = Printer() ;
Printer1->BeginDoc(); // <-- add this!
// use Printer1->Canvas as needed...
Printer1->EndDoc(); // <-- add this!
{code}
This is clearly stated in the TPrinter documentation.
I would even go as far as doing this:
{code}
TPrinter *Printer1 = Printer() ;
Printer1->BeginDoc(); // <-- add this!
try
{
    // use Printer1->Canvas as needed...
    Printer1->EndDoc(); // <-- add this!
}
catch (const Exception &)
{
    Printer1->Abort(); // <-- add this!
    throw;
}
{code}
-- 
Remy Lebeau (TeamB)
0
Remy
8/12/2015 4:42:50 PM
Remy
Thanx for the help.
You are quite right.  I put BeginDoc AFTER attempting to write to the 
Prrinter's Canvas.
Putting it BEFORE is the fix and now printing is working (at least to MS's 
XPS printer, can't
test with laser at this moment cos of driver problems which I'll fix in 
morning, its past midnite
here !)
I'll incorporate your suggestions & read up on GetLastError.
If you're stating the documentation clearly states BeginDoc should precede 
writing to the
Printer's Canvas I'll disagree.  The crucial sentences read  "A *print* job 
is started by a
call to BeginDoc. The application sends commands by rendering through a Text 
variable
or the printer's canvas".  -   the stars are mine
Best  John
0
John
8/13/2015 12:03:56 AM
John wrote:
> If you're stating the documentation clearly states BeginDoc should
> precede writing to the Printer's Canvas I'll disagree.
http://docwiki.embarcadero.com/RADStudio/XE8/en/Printing
{quote}
To get a list of installed and available printers, use the Printers property. 
An instance of the class TPrinter uses a TCanvas (which is identical to the 
form's TCanvas). This means that anything that can be drawn on a form can 
be printed as well. To print an image, **call the BeginDoc method followed 
by whatever canvas graphics you want to print (including text through the 
TextOut method) and send the job to the printer by calling the EndDoc method**.
{quote}
http://docwiki.embarcadero.com/Libraries/XE8/en/Vcl.Printers.TPrinter
{quote}
A print job is started by a call to Vcl.Printers.TPrinter.BeginDoc. The application 
sends commands by rendering through a Text variable or the printer's canvas. 
You can move to a new page by calling the NewPage method. The job stays open 
until the application calls EndDoc. If a problem occurs and you need to terminate 
a print job that was not sent to the printer successfully, call the Abort 
method. 
{quote}
-- 
Remy Lebeau (TeamB)
0
Remy
8/13/2015 12:32:53 AM
> 
> Best  John
You might look at the web page below. It has some very good components for C++Builder printing. They now have sample projects through XE7. This worked for me a few years ago. 
Printing Utilities:
code4sale.com/tryitbuyit/index.htm
TExcellentImagePrinter
TExcellentFormPrinter
0
Larry
8/13/2015 12:57:09 AM
Remy
Ok the DocWiki is clear. . .
"To print an image, call the BeginDoc method *followed* by whatever canvas 
graphics you want to print"
Hitherto I've just referred to the docus shipped with CB2007/XE2 . 
Moral - use DocWiki. .
Larry
I've noted your references. .
Best  John
0
John
8/13/2015 11:37:50 PM
Reply: