You can send a copy of a form as a bitmap to a printer by simply calling the print procedure. One way to do this might be to place a button a form and add a simple procedure:
procedure TConsignmentDetails.Button2Click(Sender: TObject);
begin
print;
end;
This could be useful for debugging during the development of a program.
Some controls have their own Print method - the Memo does not have a Print Method but the RichEdit control does. We can thus write something like this to print the contents of a RichEdit control:
for i:= 0 to richedit1.Lines.Count do
richedit1.Print(richedit1.lines[i]);
Where a Print method is not available you must use the facilities of the Printers unit. The first step is to include the Printers unit in the list of included units in the uses clause:
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, unit1, unit2, Printers;
This provides a Printer object to include in your code.
The second step in printing is to use the printer dialogue box to select and set up a printer. This is done by including a print dialogue option from the Dialogues component set. This is often done with an if statement because the dialogue box includes OK and Cancel buttons which return true and false. If the user clicks OK the printer settings are accepted, if Cancel is selected the printer settings are ignored and the subsequent printing code is ignored:
if PrintDialog1.Execute then
with Printer do
begin
BeginDoc;
for i:=0 to Memo1.Lines.Count do
Canvas.TextOut(200,200 +
(i *
Canvas.TextHeight(Memo1.Lines.Strings[i])),Memo1.Lines.Strings[i]);
Refresh;
EndDoc;
end;
This routine executes the PrintDialog dialogue box and runs the code that follows if the user clicks the OK button. With a combobox:
if PrintDialog1.Execute then
with Printer do
begin
BeginDoc;
for i:=1 to Combobox1.Items.Count -1 do
Canvas.TextOut(200,200 +
(i *
Canvas.TextHeight(Combobox1.Items[i])), Combobox1.Items[i]);
Refresh;
EndDoc;
end;
This will produce a single page of output only. If the output extends over more than one output page we need something more complex:
procedure TForm2.Button1Click(Sender: TObject);
const
MaxLines=59; //60 lines including 0,
RichEdit indexes lines from 0
var
LineCount, TextLine, CurrentLine:integer;
j,k:integer;
page, FirstPage, LastPage : integer;
begin
for j:=1 to 100 do
RichEdit1.Lines.Add(inttostr(j));
//set up sample output
FirstPage:=1;
LastPage:=RichEdit1.Lines.Count div MaxLines + 1;
//calculate number of pages
page:=FirstPage;
CurrentLine:=0;
if PrintDialog1.Execute then
begin
Printer.BeginDoc;
for k:= FirstPage to LastPage do
//loop through pages
begin
TextLine:=0; //initialise line number for
output to page
for LineCount := CurrentLine to (CurrentLine + MaxLines) do
//loop from current line to last line on page
begin
Canvas.TextOut(200,200 + TextLine *
Printer.Canvas.TextHeight(RichEdit1.Lines[TextLine]), RichEdit1.Lines[LineCount]);
inc(TextLine);
//TextLine is used to step through the lines on a page
end;
inc(page); //next page
CurrentLine := CurrentLine + MaxLines + 1;
//set first line for next page
if page <= FirstPage then Printer.NewPage;
//add new page
end;
Printer.EndDoc;
end;
end;
This code uses two nested loops to go through the pages in a document and then the lines on each page. If the number of lines per page is 60 and the number of lines in 200 then 4 pages will be required (200 div 60=3, add 1 for the remaining 20 lines). The loop through the lines on a page needs to start from the current point reached in the document. Each page, however, needs to be printed from 0 to the number of lines. This requires extra calculation inside the loop. The object name 'Printer' has been left in this code to show the associated methods, these could be replaced with 'With Printer do', as in the earlier code. For different print scenarios the number of lines and the height of the text could be changed.
The code illustrates a number of features of the Printer object:
This creates a global instance of a TPrinter object, which is used to print to a text file or the printer canvas.
BeginDoc starts a print job; the job is not sent until the EndDoc procedure is called.
This returns the state of the printer canvas to its default state, without resetting the font, brush or pen properties. Without this procedure the printer may produce unpredictable results.
A printer object has a canvas, which is the surface on which print commands are run. The code above creates a rectangle 200 pixels from the edges of the page and then starts printing at a point 200 pixels from the top left corner. This code prints the contents of a memo by cycling through its lines and printing each one at a distance of i * Canvas.TextHeight down the page. The print location is set to (200, 200+the line counter * the height of the line).
A canvas has font properties, which can be set as follows:
with Printer.Canvas do
begin
font.Name:='Arial';
font.Size:=12;
font.style:=[fsBold, fsItalic, fsUnderline, fsStrikeout];
//deliberately uses the 4 styles available
font.color:='clRed';
end;
The code below is almost identical but prints the contents of a string grid:
if PrintDialog1.Execute then
with Printer do
begin
BeginDoc;
for i:=0 to 15 do
begin
line:=stringgrid1.cells[0,i] + ' £' + stringgrid1.cells[1,i];
Canvas.TextOut(200,200 + (i * Canvas.TextHeight(Stringgrid1.cells[0,i])),line);
end;
Refresh;
EndDoc;
end;
This shows that similar principles apply to a range of cases and that code can be quickly adapted to new situations.
Each section in a print dialogue box can be accessed separately:
PrintDialog1.Options := [poPageNums, poSelection];
PrintDialog1.FromPage := 1;
PrintDialog1.MinPage := 1;
PrintDialog1.ToPage := PageControl1.PageCount;
PrintDialog1.MaxPage := PageControl1.PageCount;
This creates a new page in the printer output stream.
These printing routines will print whatever has been entered in a Memo or RichEdit control. Rather than typing the data into these controls we could send lines of text to them and then print them; we could obtain the lines of text from a file and we would thus have a reporting tool capable of displaying and printing the contents of a file. For example:
procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
begin
memo1.lines.Add(format('%-30s',['Geoffrey Thompson'])+
format('%20s',['West Ham United']));
if PrintDialog1.Execute then
with Printer do
begin
canvas.Font.Size:=20;
canvas.Font.name:='Courier New';
//fixed pitch font to keep characters in strict columns
BeginDoc;
for i:=0 to Memo1.Lines.Count do
Canvas.TextOut(200,200 +
(i *
Canvas.TextHeight(Memo1.Lines.Strings[i])),Memo1.Lines.Strings[i]);
Refresh;
EndDoc;
end;
We could direct output from a text file to the memo and then call this print routine (minus the memo1.lines.add line):
procedure TForm1.Open1Click(Sender: TObject);
var F:textfile;
S:string;
begin
if OpenDialog1.execute then
begin
AssignFile(F, OpenDialog1.FileName);
Reset(F);
While not eof (F) do
begin
Readln(F, S);
Memo1.Lines.Add(S);
end;
CloseFile(F);
end;
end;
This File/Open routine has been placed in a menu.
Similarly we could take output from a structured file, format it appropriately and output it to a memo before printing it.
procedure TForm1.Open2Click(Sender: TObject);
var
memostring:string;
begin
memo1.Font.Name:='Courier New'; //fixed
pitch font to keep characters in strict columns
assignfile(personfile,'persons.dat');
reset(personfile);
while not eof (personfile) do
begin
read(personfile,person);
memostring:=format('%-20s',[person.name]) +
format('%-20s',[person.address]) + format('%10s',[datetostr(person.dateofbirth)]);
memo1.Lines.Add(memostring);
end;
end;
This File/Open routine has been placed in a menu, either as a replacement for the text file routine above or as a new option below it.
An alternative approach to printing is to use the AssignPrn procedure, which directs text output to the printer. For example:
declaration: FPrn:textfile; //(can also use System.Text)
AssignPrn(FPrn);
Rewrite(FPrn);
writeln(FPrn,'Daily Management Statistics');
writeln(FPrn);
write(FPrn,'Date: ');
writeln(FPrn, cons_record.day,'/',cons_record.month,'/',cons_record.year);
write(FPrn,'Number of Parcels: ');
writeln(FPrn, num_parcels);
writeln(FPrn, total_weight_string);
writeln(FPrn, total_revenue_string);
System.CloseFile(MyFile);
Simply sending text to an output stream, whether to a text file or a canvas, may not produce the desired results, the output will have to be formatted. An application like a spreadsheet or a table in a word processor or HTML editor has formatting options available such as alignment and number of decimal places. In the examples above the text to be output was already formatted in a memo or string grid but you could also use the formatting functions when sending data to a printer stream.
In the following example the Assign(FPrn) option is used:
if PrintDialog1.Execute then
begin
AssignPrn(FPrn);
Rewrite(FPrn);
try
with Printer.Canvas do
begin
font.Name:='Courier New';
font.Size:=12;
font.style:=[];
end;
total_weight_string:=Format('Total weight: %fkg',[total_weight]);
total_revenue_string:=Format('Total cost: £%f',[total_revenue]);
writeln(FPrn,'Daily Management Statistics');
writeln(FPrn);
write(FPrn,'Date: ');
writeln(FPrn, cons_record.day,'/',cons_record.month,'/',cons_record.year);
write(FPrn,'Number of Parcels: ');
writeln(FPrn, num_parcels);
writeln(FPrn, total_weight_string);
writeln(FPrn, total_revenue_string);
finally
closefile(FPrn);
end;