How can I control margins when printing text that I have loaded
into a TRichEdit?
Printing from a RichEdit is really rather easy - all you need to do is to call the
Print method:
RichEdit1.Print(const Caption: string);
The Caption parameter shown here specifies the title that appears in the Print queue.
Therefore you would, for example, call something like:
RichEdit1.Print(MyAppName+': '+DocumentTitle);
From this call the system will carefully format and print the contents of the RichEdit
automatically over the required number of pages. That is all well and good, but it
does have an unwanted side effect - there is no control over the margins whatsoever.
It will simply use the default margins as reported by the Printer when it returns its
printable page size.
In order to be able to define the margins you need to know a number of things... The
available space on the page, the unprintable area of the page, whether to calculate the
margins as Inches or Centimetres, and so on. Much of this can be retrieved from the
Printer object, and the rest we will need to provide in our code.
The first thing to decide is how to determine the measurements. You could pass
this as a String (e.g. 'inches' or 'centimetres'), you could pass it as a number (e.g. 1
for inches, 2 for centimetres) but perhaps the easiest way is to declare your own data
type that will make the code easier to read. For this example I have defined
TRichTextMeasurements in the Interface section of the unit:
type TRichTextMeasurements = (rtmNone, rtmInches, rtmCentimetres);
Now we can determine whether to use the default margins (rtmNone) or to create our own
margins in the selected measurement.
Now all we need is the code to implement the margins if needed. This example
function accepts parameters to describe the margins required, the type of measurements to
be used for creating margins and the number of copies to be printed.
function PrintRichText(RTLeftMargin, RTRightMargin, RTTopMargin, RTBottomMargin: Extended;
RichTextMeasurement: TRichTextMeasurements; Copies: Integer): Boolean;
var
PixelsX, PixelsY, LeftSpace, TopSpace: Integer;
LeftMargin, RightMargin, TopMargin, BottomMargin: Extended;
begin
Result := False; // default return value
if RichTextMeasurement <> rtmNone then
begin
// get pixels per inch
PixelsX := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
PixelsY := GetDeviceCaps(Printer.Handle, LOGPIXELSY);
// get non-printable margins
LeftSpace := GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX);
TopSpace := GetDeviceCaps(Printer.Handle, PHYSICALOFFSETY);
LeftMargin := RTLeftMargin;
RightMargin := RTRightMargin;
TopMargin := RTTopMargin;
BottomMargin := RTBottomMargin;
// If the measurement is set in Centimetres, recalculate
if RichTextMeasurement = rtmCentimetres then
begin
LeftMargin := LeftMargin / 2.54;
RightMargin := RightMargin / 2.54;
TopMargin := TopMargin / 2.54;
BottomMargin := BottomMargin / 2.54;
end;
// Set the Margins
R.Left := Round(PixelsX * LeftMargin) - LeftSpace;
R.Right := Printer.PageWidth - Round(PixelsX * RightMargin) - LeftSpace;
R.Top := Round(PixelsY * TopMargin) - TopSpace;
R.Bottom := Printer.PageHeight - Round(PixelsY * BottomMargin) - TopSpace;
RichEdit1.PageRect := R;
Application.ProcessMessages;
end;
// Print the required number of copies
while Copies > 0 do
begin
RichEdit1.Print('MyApp: Copy '+IntToStr(Copies));
Dec(Copies);
Application.ProcessMessages;
end;
Result := True;
end;
To call the example code to print one copy with a 1 inch margin all round, you
could do something like this:
procedure TForm1.PrintButtonClick(Sender: TObject);
begin
if PrintRichText(1, 1, 1, 1, rtmInches, 1) then
ShowMessage('Printing was successful')
else
ShowMessage('Printing failed');
end;
Note that if you want to use the default margins you still have to pass a number for
each margin in the function call. What that number is does not matter as they are
all ignored, but perhaps for clarity using a zero would be best:
procedure TForm1.PrintButtonClick(Sender: TObject);
begin
if PrintRichText(0, 0, 0, 0, rtmNone, 1) then
ShowMessage('Printing was successful')
else
ShowMessage('Printing failed');
end;
This example can easily be extended to provide visual print progress feedback, or to
add margin measurements in millimetres. However, it will not replace a full
implementation as provided by commercial Word Processing products. On the other
hand, for many purposes it will more than suffice.
© Chris Bray / Vertical Software 2002
|