Board index » cppbuilder » A print question for Builder 5

A print question for Builder 5


2005-12-20 12:50:19 AM
cppbuilder51
I have an ascii file that I'm loading into a RichEdit window. To print
whats in the window I'm using: RichEdit1->Print("flt_price_est.txt");
Is there a way to print only so many lines (say 25) to the first sheet
of paper, and then print the balance of the file on another sheet? I
don't need to build a print menu for the user.
Thanks
Arlie
 
 

Re:A print question for Builder 5

Arlie Winters < XXXX@XXXXX.COM >wrote:
Quote

[...] Is there a way to print only so many lines (say 25) to
the first sheet of paper, and then print the balance of the
file on another sheet?
If you're going to be working with TRichEdits, you'll want to
bookmark this link:
home.att.net/~robertdunn/Yacs.html
The first thing that comes to mind is to insert a page break
into the TRichEdit using raw RTF coding. Just position the
caret to where you want the page break to be and then insert
it by sending the control the EM_STREAMIN message. For example
(sample code by Gambit):
char buffer[10] = {0};
strcpy(buffer, "\\page");
EDITSTREAM es;
es.dwCookie = (DWORD)buffer;
es.pfnCallback = (EDITSTREAMCALLBACK)EditStreamCallback;
SendMessage(RichEdit1->Handle, EM_STREAMIN, SF_RTF | SFF_SELECTION, (LPARAM)&es );
DWORD CALLBACK EditStreamCallback(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG FAR *pcb)
{
char *buffer = (char*)dwCookie;
memcpy(pbBuff, buffer, strlen(buffer));
*pcb = strlen(buffer);
return 0;
}
If you decide that you don't want to use TRichEdit's Print
method, then you'll have to manage the printing yourself.
Managing simple printing (just getting it to the paper) is
easy enough but producing a nice looking document can be
quite challanging - especially if you haven't printed much of
anything prior.
A very simple print function might look something like:
int X = 0, Y = 0;
Printer()->BeginDoc();
int CharacterHeight = Printer->Canvas->TextHeight( "Wg" );
for( int x = 0; x < RichEdit1->Lines->Count; ++x )
{
Printer->Canvas->TextOut( X, Y * CharacterHeight, RichEdit1->Lines->Strings[x] );
}
Printer()->EndDoc();
Two huge problems exist with this simple sample. The first
is that it fails to perform any checking to see if the line
is actually small enough to fit on one printed line. If it
does not fit, the trailing letters will be truncated. The
sample also fails to check if there is enough room at the
bottom of the page to print another line. As in the first
case, if there is not enough room, it just doesn't get printed.
If you're going to be doing any printing (or working with
graphics), you need to be able to work with a TCanvas. It
has many properties and methods to make it easy to use and
it works the same irrespective of if it's a TPrinter::Canvas
or a TPanel::Canvas or a TBitmap::Canvas.
All TCanvas's have a finite number of pixels organized in a 2D
array. The upper/left pixel is always 0,0 and the width and
height of the Canvas is determined by the control that it is
associated with. For example, a TBitmap::Canvas is sized
according to the TBitmap::Width and Height. For TPrinter, you
can determined it's dimensions by reading it's PageWidth and
PageHeight properties. So, anything that print that exceeds the
Canvas's width or height will be clipped (not printed).
This, along with TCanvas::TextWidth and TPrinter::NewPage is
enough information so that you should be able to surmize how
to fix the simple sample to produce a crude but reliable print
function. You'll just have to figure out how you want to break
the lines that are too long.
Once you get the printed output, you'll notice that the
text is not centered on the paper. The biggest issue with
managing your own printing centers around the fact every
different make and model of printer positions the Canvas
on the paper differently. Once in a blue moon it's centered
on the paper but it's almost always *not* centered and the
area of the paper which does not fall within the Canvas is
unprintable.
There's also the issue of DPI which varies from printer to
printer and if you want to center your text, you have to
determine this as well as a few other settings with the
printer. Then you do some math to calculate a new working
rectangle for the TPrinter::Canvas instead using the Rect
defined as (0, 0, Printer()->PageWidth, Printer()->PageHeight).
This is the only way (without using a report builder) to get
consistant output across all ranges of printers.
The information that you need is not part of TPrinter so you
have to use the win32 API GetDeviceCaps (look in the win32.hlp
file for more details):
// Standard user defined margines in inches
double LMargine = 1.0;
double TMargine = 1.25;
double RMargine = 1.0;
double BMargine = 1.25;
// Get the printer pixels per inch
XPPI = GetDeviceCaps( Printer()->Handle, LOGPIXELSX );
YPPI = GetDeviceCaps( Printer()->Handle, LOGPIXELSY );
// Get the printers total pixels
XPixels = GetDeviceCaps( Printer()->Handle, PHYSICALWIDTH );
YPixels = GetDeviceCaps( Printer()->Handle, PHYSICALHEIGHT );
// Calculate the margines in printer pixels
TRect Margines;
Margines.left = ((double)LMargine * XPPI);
Margines.top = ((double)TMargine * YPPI);
Margines.right = ((double)RMargine * XPPI);
Margines.bottom = ((double)BMargine * YPPI);
// Determine the unprintable area of the paper
TRect Offsets;
Offsets.left = GetDeviceCaps( Printer()->Handle, PHYSICALOFFSETX );
Offsets.top = GetDeviceCaps( Printer()->Handle, PHYSICALOFFSETY );
Offsets.right = XPixels - (Printer()->PageWidth + Offsets.left);
Offsets.bottom = YPixels - (Printer()->PageHeight + Offsets.top);
// Confirm margines are within printable area
if( Offsets.left>Margines.left ) Margines.left = Offsets.left;
if( Offsets.top>Margines.top ) Margines.top = Offsets.top;
if( Offsets.right < Margines.right ) Margines.right = Offsets.right;
if( Offsets.bottom < Margines.bottom ) Margines.bottom = Offsets.bottom;
At this point, the TRect Margines represents a Rect into the
TPrinter::Canvas that is centered on the paper and takes into
account the desired print margines so you can incorporate it
into the simple sample. For example (assumes all line fit):
int X = Margines.left, Y = Margines.top;
Printer()->BeginDoc();
int CharacterHeight = Printer->Canvas->TextHeight( "Wg" );
for( int x = 0; x < RichEdit1->Lines->Count; ++x )
{
if( (Y + CharacterHeight)>Margines.bottom )
{
Printer()->NewPage();
Y = Margines.top;
}
Printer->Canvas->TextOut( X, Y, RichEdit1->Lines->Strings[x] );
Y += CharacterHeight;
}
Printer()->EndDoc();
I saved the issue of breaking a line for last because I'm not
that familure with the internals of TRichEdit. It easy enough
to know if the line will fit by using TextWidth. For example:
if( Printer->Canvas->TextWidth(RichEdit1->Lines->Strings[x])>(Margines.right - Margines.left) )
{
// it's too long so break it
}
but I have questions because I know how TMemo works with it's
Lines. Even if there is no hard return, TMemo will break the
line according to how it needs to be wrapped according to it's
display. If TRichEdit does this as well, you're going to get
a very messy looking printout because the line breaks will be
based on the screen's DPI and the control's width.
You're going to have to do some testing to determine exactly
how TRichEdit treats hard returns and how it stores lines
without them when they are wrapped on the display.
Once you have determined that the line needs a break, you'll
need a copy of the line so you can manipulate it. The crudest
approach would be to start truncating the line a character at
a time until it fits. Then you have to continue truncating
until you reach a delimiter so that you can break on a whole
word.
Now you have a portion of the Line (to print as a single line)
and you need to use that against another copy of the original
line to arive at the remainder of the Line. Since the remainder
of the Line may also need a break, this method has to be in a
loop that is repeated until the remainder also fits.
A more elegant solution is to use the win32 API DrawText.
Specifically, look at DT_CALCRECT and DT_WORDBREAK but you
won't be able to get away from having to code manually
breaking the lines. This is because a line can span 2 pages
and you can't see where Windows added the break.
However, DrawText will also modify the string (if you tell it
to by using DT_MODIFYSTRING) but it will append ellipses to
the end. The end result is a modified string that fits but
you'll have to truncate the ellipses, append from the original
until you hit a delimiter and then resume truncating until
you find where to break.
The benifit of using DrawText is that manually breaking the
line will add alot of overhead because of repeated calls to
TextWidth. Using DrawText will allow you to not have to
manually break the line unless you detect that the line
spans multiple pages. In addition, DrawText will modify the
string and get you very close without ever calling TextWidth
once.
Good luck.
~ JD
 

Re:A print question for Builder 5

Hans Galema < XXXX@XXXXX.COM >wrote:
Quote

Is your clock off one hour ? I did not see your reply when I
started to answer the post. Yet the time differs an hour.
LOL.
You actually posted a couple of seconds before I did but mine
made it first <g>! My clock is correct and I don't know how
the time is determined.
For example, you posted:
Tue, 20 Dec 2005 10:49:26 +0100
and I posted:
20 Dec 2005 01:49:31 -0700
Just the fact that the format differs leads me to believe that
it's a server (aka ISP) issue and then there's the time zone
thing ... I'm an hour different from Borland's server. Your
guess is as good as mine.
~ JD
 

{smallsort}

Re:A print question for Builder 5

Arlie Winters wrote:
Quote
I have an ascii file that I'm loading into a RichEdit window. To print
whats in the window I'm using: RichEdit1->Print("flt_price_est.txt");
That is not an accurate description. You load a file in a TRichEdit component
and then the component will display part of the file -in a window-. You can not
just print part of the file. Print() will print the whole file.
Quote
Is there a way to print only so many lines (say 25) to the first sheet
of paper, and then print the balance of the file on another sheet? I
don't need to build a print menu for the user.
Just take another TRichEdit to which you transfer the lines that you
actually want to print.
Hans.
 

Re:A print question for Builder 5

JD wrote:
Is your clock off one hour ? I did not see your reply when I started
to answer the post. Yet the time differs an hour.
Hans.
 

Re:A print question for Builder 5

JD wrote:
Quote
You actually posted a couple of seconds before I did but mine
made it first <g>!
Objection!
Quote
My clock is correct and I don't know how
the time is determined.

For example, you posted:

Tue, 20 Dec 2005 10:49:26 +0100
That would count for GMT 09:49:26.
Quote
and I posted:

20 Dec 2005 01:49:31 -0700
That would be GMT 08:49:31.
So your clock was at 01:49:31.
So Greenwich Mean Time differs one hour.
Quote
Just the fact that the format differs leads me to believe that
it's a server (aka ISP) issue and then there's the time zone
thing ... I'm an hour different from Borland's server. Your
guess is as good as mine.
Someone does not play the timezone thing well. It will be the
server of the webinterface that you use.
Hans.
 

Re:A print question for Builder 5

Hans;
Thanks for putting so much time into a response. Your last suggestion of
transferring into another TRichEdit "the lines that you actually want to print" is
most appealing. The "LoadFromFile" grabs it all...how do I grab only a certain
number of lines?
Thanks
Arlie
Hans Galema wrote:
Quote
Arlie Winters wrote:
>I have an ascii file that I'm loading into a RichEdit window. To print
>whats in the window I'm using: RichEdit1->Print("flt_price_est.txt");

That is not an accurate description. You load a file in a TRichEdit component
and then the component will display part of the file -in a window-. You can not
just print part of the file. Print() will print the whole file.

>Is there a way to print only so many lines (say 25) to the first sheet
>of paper, and then print the balance of the file on another sheet? I
>don't need to build a print menu for the user.

Just take another TRichEdit to which you transfer the lines that you
actually want to print.

Hans.
 

Re:A print question for Builder 5

Arlie Winters wrote:
Quote
Thanks for putting so much time into a response. Your last suggestion of
transferring into another TRichEdit "the lines that you actually want to print" is
most appealing. The "LoadFromFile" grabs it all...how do I grab only a certain
number of lines?
You should transfer from the TRichEdit, that loaded the file, to an empty
TRichEdit. Have a look at the ->Lines->Strings[] property and ->Lines->Count.
Loop through ->Lines->Strings[] for as many lines as you want
and add to the other with ->Lines->Add ();
You suggested that the filecontents is plain text and not rtf.
Hans.
 

Re:A print question for Builder 5

Hans Galema wrote:
Quote
You suggested that the filecontents is plain text and not rtf.
Because if that is the case you could better stick with one TRichEdit.
To print the first 25 lines ->Lines->Delete() all other lines (beginning
from the end.). After a Print(), LoadFromFile() again the file and then
delete the first 25 lines. Print().
Hans.
 

Re:A print question for Builder 5

Hans;
I understand the concept you're conveying "To print the first 25 lines
->Lines->Delete() all other lines", but I don't know the syntax to delete
those other lines. If I could trouble you once more...it would give me
something to build upon.
Thanks
Arlie
Hans Galema wrote:
Quote
Hans Galema wrote:

>You suggested that the filecontents is plain text and not rtf.

Because if that is the case you could better stick with one TRichEdit.
To print the first 25 lines ->Lines->Delete() all other lines (beginning
from the end.). After a Print(), LoadFromFile() again the file and then
delete the first 25 lines. Print().

Hans.
 

Re:A print question for Builder 5

Arlie Winters < XXXX@XXXXX.COM >wrote:
Quote

[...] but I don't know the syntax to delete those other
lines. [...] give me something to build upon.
Bring up the help for TRichEdit, look at the properties and
click on the entry for Lines. When that help page opens, take
note that Lines is of type TStrings. Click that help jump and
the help for using TStrings will come up with all of the
methods and properties for manipulating a TStrings object.
Just keep in mind that Lines is the name of the TStrings so
you would use RichEdit1->Lines->SomeTStringMethod. Also think
about what happens if you delete from a given point to the
end. For example:
for( int x = 27; x < Count; ++x ) delete string[x]
That will only delete every other string starting at index 27
to the end because as you delete one string, the remaining
strings fill in the gap. The way to do it is to start at the
end and delete backwards.
~ JD
 

Re:A print question for Builder 5

Arlie Winters wrote:
Quote
I understand the concept you're conveying "To print the first 25 lines
->Lines->Delete() all other lines", but I don't know the syntax to delete
those other lines. If I could trouble you once more...it would give me
something to build upon.
To delete the 27th line use:
RichEdit1->Lines->Delete(26);
There are RichEdit1->Lines->Count lines.
So make a loop deleting all unwanted lines starting -as said- at the end.
That's all.
Hans.
 

Re:A print question for Builder 5

I see....
for( int x = RichEdit1->Lines->Count; x>47; x--) //while x is greater than
the last line I want to print
RichEdit1->Lines->Delete(x); //remove line x (always the last line in the
file)
RichEdit1->Print(""); //print all data in the RichEdit window
This trashes everything past the 48th line, and allows me to print from [0]
thru [48].
Thanks very much
Arlie
Hans Galema wrote:
Quote
Arlie Winters wrote:

>I understand the concept you're conveying "To print the first 25 lines
>->Lines->Delete() all other lines", but I don't know the syntax to delete
>those other lines. If I could trouble you once more...it would give me
>something to build upon.

To delete the 27th line use:

RichEdit1->Lines->Delete(26);

There are RichEdit1->Lines->Count lines.

So make a loop deleting all unwanted lines starting -as said- at the end.

That's all.

Hans.
 

Re:A print question for Builder 5

Ok, I've got the idea of removing lines inside of a RichEdit window. But when I
remove lines starting from the bottom of the window, I can't get the vertical
scrollbar back up to the top. I've tried RichEdit1->SelStart = 0;
but it does not work.
Any help would be appreciated.
Arlie
Arlie Winters wrote:
Quote
I see....

for( int x = RichEdit1->Lines->Count; x>47; x--) //while x is greater than
the last line I want to print
RichEdit1->Lines->Delete(x); //remove line x (always the last line in the
file)

RichEdit1->Print(""); //print all data in the RichEdit window

This trashes everything past the 48th line, and allows me to print from [0]
thru [48].

Thanks very much
Arlie

Hans Galema wrote:

>Arlie Winters wrote:
>
>>I understand the concept you're conveying "To print the first 25 lines
>>->Lines->Delete() all other lines", but I don't know the syntax to delete
>>those other lines. If I could trouble you once more...it would give me
>>something to build upon.
>
>To delete the 27th line use:
>
>RichEdit1->Lines->Delete(26);
>
>There are RichEdit1->Lines->Count lines.
>
>So make a loop deleting all unwanted lines starting -as said- at the end.
>
>That's all.
>
>Hans.
 

Re:A print question for Builder 5

I can get the scrollbar up by clicking on it, but I'd like the page to return to the
position that it was in when the file was first opened.
Arlie Winters wrote:
Quote
Ok, I've got the idea of removing lines inside of a RichEdit window. But when I
remove lines starting from the bottom of the window, I can't get the vertical
scrollbar back up to the top. I've tried RichEdit1->SelStart = 0;
but it does not work.

Any help would be appreciated.
Arlie

Arlie Winters wrote:

>I see....
>
>for( int x = RichEdit1->Lines->Count; x>47; x--) //while x is greater than
>the last line I want to print
>RichEdit1->Lines->Delete(x); //remove line x (always the last line in the
>file)
>
>RichEdit1->Print(""); //print all data in the RichEdit window
>
>This trashes everything past the 48th line, and allows me to print from [0]
>thru [48].
>
>Thanks very much
>Arlie
>
>Hans Galema wrote:
>
>>Arlie Winters wrote:
>>
>>>I understand the concept you're conveying "To print the first 25 lines
>>>->Lines->Delete() all other lines", but I don't know the syntax to delete
>>>those other lines. If I could trouble you once more...it would give me
>>>something to build upon.
>>
>>To delete the 27th line use:
>>
>>RichEdit1->Lines->Delete(26);
>>
>>There are RichEdit1->Lines->Count lines.
>>
>>So make a loop deleting all unwanted lines starting -as said- at the end.
>>
>>That's all.
>>
>>Hans.