 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Alain Guest
|
Posted: Fri Sep 29, 2006 8:11 am Post subject: Print Preview with Printer()->Canvas |
|
|
Hello All,
How to get a print preview with the "Printer()->Canvas" function ?
Thanks
Alain |
|
| Back to top |
|
 |
JD Guest
|
Posted: Sat Sep 30, 2006 3:52 pm Post subject: Re: Print Preview with Printer()->Canvas |
|
|
"Alain" <alainbastien (AT) gmail (DOT) com> wrote:
| Quote: |
How to get a print preview with the "Printer()->Canvas" function ?
|
To be clear, the TPrinter::Canvas is not a function. It is a
member of the TPrinter class.
The first thing is to keep in mind that a TCanvas is a TCanvas
no matter what HWND (Handle) it belongs to. It is nothing more
than a wrapper for the HDC (Device Context) which Windows uses
for drawing operations. Do not confuse the TCanvas::Handle
which is an HDC with other VCL object Handles which are HWND's.
Since all TCanvas's are the same, you can use the exact
same methods to print to a TBitmap's Canvas and then you
can display the TBitmap as a print preview and even print
it if you like. While it's not difficult, it's not as quite
straight forward as it sounds. The biggest issue is that the
screen and the printer have radically different DPI (Density
of Pixels per Inch) and pixel dimensions (Width and Height in
pixels).
What I do for my print previews is I size the TBitmap to match
the paper dimensions in inches. IOW, if the paper is 8.5 x 11
inches, then I get the screen's DPI, do some math and then set
the TBitmap's Width and Height accordingly so that when
displayed at 100%, on screen, it measures 8.5 x 11 inches. The
result is a WYSIWYG (What You See Is What You Get) preview.
Look in the win32.hlp file (you might have to search your hard
drive for it) and look up GetDeviceCaps. That API needs to be
called several times to gather all of the information that you
need to do this correctly.
Another issue that needs to be addressed is that no 2 printers
are the same (unless their the same make AND model). Each
printer has a white space that surrounds the outer edge of the
paper where the printer is physically unable to print and this
white space is different from printer to printer. What this
means to you the programmer is that Canvas(0,0) on one printer
might start at absolute pixel (50,10) on one printer and it
might be (40,40) on another printer. To compound the situation,
not only do printers have varying DPI, the Canvas is never
centered on the paper so if you want the printed report to
look the same no matter what printer is used, you have to do
more calls to GetDeviceCaps and more math.
// Get the printer pixels per inch
pXPPI = ::GetDeviceCaps( Printer()->Handle, LOGPIXELSX );
pYPPI = ::GetDeviceCaps( Printer()->Handle, LOGPIXELSY );
// Get the printers totals pixels
pXPixels = ::GetDeviceCaps( Printer()->Handle, PHYSICALWIDTH );
pYPixels = ::GetDeviceCaps( Printer()->Handle, PHYSICALHEIGHT );
// Determine the printable area of the paper
TRect Area;
Area.left = ::GetDeviceCaps( Printer()->Handle, PHYSICALOFFSETX );
Area.top = ::GetDeviceCaps( Printer()->Handle, PHYSICALOFFSETY );
Area.right = pXPixels - (Printer()->PageWidth + Area.left);
Area.bottom = pYPixels - (Printer()->PageHeight + Area.top);
// User defined margines in inches
LMargine = 1.0;
TMargine = 1.25;
RMargine = 1.0;
BMargine = 1.25;
// Calculate the margines in printer pixels
TRect Margines;
Margines.left = ((double)LMargine * pXPPI);
Margines.top = ((double)TMargine * pYPPI);
Margines.right = ((double)RMargine * pXPPI);
Margines.bottom = ((double)BMargine * pYPPI);
// Adjust printable Area to account for margines
if( Margines.left > Area.left ) Area.left = Margines.left;
if( Margines.top > Area.top ) Area.top = Margines.top;
if( Margines.right > Area.right ) Area.right = Margines.right;
if( Margines.bottom > Area.bottom ) Area.bottom = Margines.bottom;
// Get the Screen Pixels Per Inch
sXPPI = ::GetDeviceCaps( Canvas->Handle, LOGPIXELSX );
sYPPI = ::GetDeviceCaps( Canvas->Handle, LOGPIXELSY );
pBitmap->Width = ((double)(pXPixels - ((Area.right - Area.left) + 1)) / pXPPI) * sXPPI;
pBitmap->Height = ((double)(pYPixels - ((Area.bottom - Area.top ) + 1)) / pYPPI) * sYPPI;
Now the TBitmap is dimensionally the same as the paper - in
inches (excluding margines) - not pixels. As you print, you'll
want to ensure that your font sizes are correct. The standard
sizes are 6 lines per inch for normal print and 8 lines per
inch for condensed print. Now, I never have understood the
Font::Size/Height. The docs were confusing to me so I just
used the TCanvas::TextHeight method to set the size. For
example:
int NormalHeight = sYPPI / 6;
int CondensedHeight = sYPPI / 8;
// set it to an arbitrary number
pBitmap->Canvas->Font->Size = 50;
while( pBitmap->Canvas->TextHeight("Wg") > NormalHeight )
{
// do not try pBitmap->Canvas->Font->Size -= 1 or similar
pBitmap->Canvas->Font->Size = pBitmap->Canvas->Font->Size - 1;
}
Be aware that calls to TextHeight and (especially) TextWidth
are very slow and should be limited as much as possible. Also
note that the font provides all of the vertical spacing as
well so you only need to increment line counters (assuming
standard print).
As a note, there is a distinct advantage to printing to a
TBitmap that you later send to the printer and that is that
many printers have a limited font selection when compared to
the screen and some printers are severly lacking. This method
doesn't care what the printer has.
As for displaying the TBitmap, I use a TScrollBox with it's
Align property set to alClient and a TPaintBox inside the
TScrollBox. All you need to do is position the TPaintBox at
(0,0) and add an OnPaint event to paint the TBitmap. I presume
that you'll be changing the zoom ratio to allow the user to
zoom in and out and for that you'll need a second TBitmap that
you size according to the zoom ratio and then draw the
original TBitmap to it using StretchDraw. For example:
private: // User declarations
Graphics::TBitmap *Bitmap1;
Graphics::TBitmap *Bitmap2;
void __fastcall ChangeZoomRatio( int Percent );
public: // User declarations
__fastcall TForm1(TComponent* Owner);
__fastcall ~TForm1();
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
// set this in the IDE
ScrollBox1->Align = alClient;
// just need some initialization to prevent AV and so it looks right
PaintBox1->Width = ScrollBox1->ClientWidth;
PaintBox1->Height = ScrollBox1->ClientHeight;
Bitmap1 = new Graphics::TBitmap();
Bitmap1->PixelFormat = pf24bit;
Bitmap2 = new Graphics::TBitmap();
Bitmap2->PixelFormat = pf24bit;
Bitmap2->Width = PaintBox1->Width;
Bitmap2->Height = PaintBox1->Height;
Bitmap2->Canvas->Brush->Style = bsClear; // Reported to speed-up bitmap operations
Bitmap2->Canvas->Brush->Color = clWhite;
Bitmap2->Canvas->FillRect( Rect(0,0,Bitmap2->Width,Bitmap2->Height) );
}
//-------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
delete Bitmap1;
delete Bitmap2;
}
//-------------------------------------------------------------
void __fastcall TForm1::PaintBox1Paint(TObject *Sender)
{
TRect R = Rect( 0, 0, Bitmap2->Width, Bitmap2->Height );
PaintBox1->Canvas->CopyRect( R, Bitmap2->Canvas, R );
}
//-------------------------------------------------------------
void __fastcall TForm1::ChangeZoomRatio( int Percent )
{
// disable painting redrawing Bitmap2
::SendMessage( ScrollBox1->Handle, WM_SETREDRAW, FALSE, 0 );
Bitmap2->Width = (Bitmap1->Width * Percent) / 100;
Bitmap2->Height = (Bitmap1->Height * Percent) / 100;
Bitmap2->Canvas->StretchDraw( Rect(0,0,Bitmap1->Width,Bitmap1->Height), Bitmap1 );
// need to set scrollbars before you size the paint box
ScrollBox1->HorzScrollBar->Position = 0;
ScrollBox1->VertScrollBar->Position = 0;
// size the paintbox
PaintBox1->SetBounds( 0, 0, Bitmap2->Width, Bitmap2->Height );
// add code here to set scroll positions if desired
// enable painting
::SendMessage( ScrollBox1->Handle, WM_SETREDRAW, TRUE, 0 );
// cause the new image to be painted
ScrollBox1->Refresh();
}
//-------------------------------------------------------------
As for printing the TBitmap, not all printer support print a
Device Dependent Bitmap (DDB) so you'll need to convert the
TBitmap to a Device Independent Bitmap (DIB).
Printer()->BeginDoc();
StretchBltBitmap( Printer()->Canvas, Area.left, Area.top, (Area.right - Area.left) + 1, (Area.bottom - Area.top) + 1, Bitmap1 );
Printer()->EndDoc();
//-------------------------------------------------------------
void __fastcall TForm1::StretchBltBitmap( TCanvas *pCanvas, int iX, int iY, int iWidth, int iHeight, Graphics::TBitmap *pBitmap )
{
char* Buffer = NULL;
unsigned int HeaderSize = 0, ImageSize = 0;
GetDIBSizes( pBitmap->Handle, HeaderSize, ImageSize );
try
{
Buffer = new char[ HeaderSize + ImageSize ];
}
catch( Exception& E )
{
MessageDlg( "Error allocating memory for DIB : " + E.Message, mtError, TMsgDlgButtons() << mbOK, 0 );
return;
}
BITMAPINFO* pBmi = (BITMAPINFO*) Buffer;
unsigned char* pImage = (unsigned char*) Buffer + HeaderSize;
if( GetDIB( pBitmap->Handle, pBitmap->Palette, pBmi, pImage ) )
{
StretchDIBits( pCanvas->Handle, iX, iY, iWidth, iHeight, 0, 0, pBitmap->Width, pBitmap->Height, pImage, pBmi, DIB_RGB_COLORS, SRCCOPY );
}
else
{
MessageDlg( "Error copying the DIB.", mtError, TMsgDlgButtons() << mbOK, 0 );
}
delete [] Buffer;
}
//-------------------------------------------------------------
~ JD |
|
| Back to top |
|
 |
Daryl Guest
|
Posted: Tue Oct 17, 2006 3:13 pm Post subject: Re: Print Preview with Printer()->Canvas |
|
|
JD
I tried the code example and have a question: Should you call the method
ChangeZoomRatio( int Percent ) with Percent set to 100% to get the correct
representation of the page on the screen? I ask this because when I ran the
code the white area was only about one 12th or less of the screen.
I read an article in a Builder 5 Developer guide that TMetafile is better
than TBitmap, have you tried the example with TMetafile substituted for
TBitmap?
Either way can you please let me have a little more info on how this example
can be used to print out a block of text? How do you manage more than on
page for example?
thanks for the link to the example.
daryl |
|
| Back to top |
|
 |
JD Guest
|
Posted: Wed Oct 18, 2006 8:11 am Post subject: Re: Print Preview with Printer()->Canvas |
|
|
"Daryl" <devaccount (AT) hotmail (DOT) com> wrote:
| Quote: |
[...] Should you call the method ChangeZoomRatio( int Percent )
with Percent set to 100% to get the correct representation
of the page on the screen?
|
Setting it to 100% would result in an image that is
dimentionally the same as if it were printed.
| Quote: | [...] when I ran the code the white area was only about one
12th or less of the screen.
|
1/10th perhaps?
I didn't test that code. I simplified a test case for work
that was thrown together on a weekend (meaning that it was
ugly code). It sounds to me like it's a simple decimal point
issue.
| Quote: | I read an article in a Builder 5 Developer guide that
TMetafile is better than TBitmap, have you tried the example
with TMetafile substituted for TBitmap?
|
No and I don't know that it is. It may be 'better' (always a
subjective term) for displaying but ultimately, it would have
to be converted to a DIB to print it so what's the point?
| Quote: | Either way can you please let me have a little more info on
how this example can be used to print out a block of text?
|
The sample is intended to demonstrate how to setup a print-
preview. It was never intended to demonstrate how to actually
print anything (except a TBitmap).
As for printing text, you should understand that no matter
what method is used, the text is drawn onto an HDC. When that
HDC is the TPrinter's Canvas, up until the moment that you
cause the printer to actually print the page, any pixel in
that HDC can be changed. So ... printing is one thing, drawing
the text is another.
The 2 usual methods to draw text are to use TCanvas's TextOut
method or to use the win32 API DrawText and each have their own
advantages. For example, TextOut will work with rotated fonts
and DrawText will not. OTOH, DrawText is much more flexible
(look in the win32.hlp file for details).
| Quote: | How do you manage more than on page for example?
|
You build it as the user requests (remember that the sample is
for a print-preview) just as you would if you were printing a
multiple page report. The difference being that you would use
the TPrinter::Canvas instead of the TBitmap::Canvas and you
would call TPrinter::NewPage between pages.
The sample included printing the TBitmap because we found that
some users wanted fonts that their printer didn't support. In
these cases, we draw the text using the desired font onto a
bitmap and print that (it's a simple test to see if the
printer supports the selected fonts).
| Quote: | thanks for the link to the example.
|
NP
~ JD |
|
| Back to top |
|
 |
george Guest
|
Posted: Thu Oct 19, 2006 5:19 pm Post subject: Re: Print Preview with Printer()->Canvas |
|
|
An alternative approach to Print Preview is to put your text to
be printed into a RichEdit and then use->Print from there. You
have to do some maths to ensure you have the same lines per
page and columns per line in the RE and the Printer, but I
don't think as much as you seem to need otherwise. The
facilities may not be as sophisticated, but it serves all my
purposes.
George
"JD" <nospam (AT) nospam (DOT) com> wrote:
| Quote: |
"Daryl" <devaccount (AT) hotmail (DOT) com> wrote:
Either way can you please let me have a little more info on
how this example can be used to print out a block of text?
The sample is intended to demonstrate how to setup a print-
preview. It was never intended to demonstrate how to actually
print anything (except a TBitmap).
As for printing text, you should understand that no matter
what method is used, the text is drawn onto an HDC. When that
HDC is the TPrinter's Canvas, up until the moment that you
cause the printer to actually print the page, any pixel in
that HDC can be changed. So ... printing is one thing, drawing
the text is another.
The 2 usual methods to draw text are to use TCanvas's TextOut
method or to use the win32 API DrawText and each have their own
advantages. For example, TextOut will work with rotated fonts
and DrawText will not. OTOH, DrawText is much more flexible
(look in the win32.hlp file for details).
How do you manage more than on page for example?
|
|
|
| Back to top |
|
 |
JD Guest
|
Posted: Fri Oct 20, 2006 2:00 am Post subject: Re: Print Preview with Printer()->Canvas |
|
|
"george" <zombacity (AT) apl (DOT) com> wrote:
Please trim your posts.
| Quote: | An alternative approach to Print Preview is to put your text
to be printed into a RichEdit
|
Ever try to add a bitmap to a TRichEdit? How about column
spanning or zooming in and out or user defined margins? They
can all be done but not easily with a TRichEdit but the bottom
line is that the print-preview still isn't WYSIWYG.
~ JD |
|
| Back to top |
|
 |
Alain Guest
|
Posted: Fri Oct 20, 2006 8:11 am Post subject: Re: Print Preview with Printer()->Canvas |
|
|
Not that easy.
I am going to use this to print barcodes on a A4 size paper with 3.5X2.5 cm
detachable stickers.
In order to avoid wastage, need to have preview with if possible dotted
lines showing
the position of the stickers relative to the printing to check any overlaps. |
|
| Back to top |
|
 |
george Guest
|
Posted: Sun Oct 22, 2006 12:52 am Post subject: Re: Print Preview with Printer()->Canvas |
|
|
OK, but does the enquirer want all that stuff? I can do
margins if I want and I am only talking about text. I can see
how it will fit in the printed page, which is what is important
to me.
George
"JD" <nospam (AT) nospam (DOT) com> wrote:
| Quote: |
Ever try to add a bitmap to a TRichEdit? How about column
spanning or zooming in and out or user defined margins? They
can all be done but not easily with a TRichEdit but the bottom
line is that the print-preview still isn't WYSIWYG.
~ JD
|
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|