procedure TDlgGrid.ButtonPrintClick(Sender: TObject);
var iField: integer;
F: TextFile;
bm: TBookmark;
BlobStream: TBlobStream;
Buffer: Array[0..255] of Char;
BytesRead: Byte;
begin
AssignPrn( F );
Rewrite(F);
OffsetWindowOrg(Printer.Canvas.Handle, -200, -200);
Writeln(F, caption);
Writeln(F);
With DBGridSource.datasource.dataset do try
screen.cursor := crHourGlass;
bm := GetBookmark;
disableControls;
first;
while not eof do begin
for iField := 0 to FieldCount - 1 do begin
if Fields[iField].DataType <> ftMemo then
Writeln(F, Copy( Fields[iField].text )
else
try
Writeln(F);
BlobStream :=
TBlobStream.Create(TMemoField(Fields[iField]), bmRead);
repeat
BytesRead := BlobStream.Read(Buffer, 255);
Buffer[BytesRead] := Chr(0);
write( F, buffer );
until ( BytesRead = 0 );
finally
BlobStream.free;
Writeln(F);
end;
end;
Writeln(F);
next;
end; {not eof}
finally
GotoBookmark(bm);
FreeBookmark(bm);
enableControls;
screen.cursor := crDefault;
end; {with datasource}
CloseFile(F);
end;
René,
the problem is that the printer DC is reset (by windows) to its default
state after each NewPage. You have to redo the OffsetWindowOrg for each new
page but using AssignPrn the page breaks are out of your control, you have
no way to detect them. Do not use AssignPrn, do the text output yourself
using methods of the Printer.Canvas. This gives you complete control over
printing positions, margins etc..
Here is a little example:
{+------------------------------------------------------------
| Procedure PrintMemo
|
| Parameters:
| aMemo: memo to print
| centerVertical: true if output should be centered vertically
| centerHorizontal: true if output should be centered horizontally
| Call method:
| static
| Description:
| Prints the contents of a memo on the currently selected
| printer, using the memos Font. The output can be centered
| vertically and/or horizontally. Default margins will be used.
| If the text does not fit onto one page the centerVertical
| parameter is ignored.
| Error Conditions:
| none
| Note:
| requires Printers in the Uses clause.
|Created: 27.10.97 13:16:46 by P. Below
+------------------------------------------------------------}
Procedure PrintMemo( aMemo: TMemo;
centerVertical, centerHorizontal: Boolean );
Var
topMargin, bottomMargin, leftMargin, rightMargin: Integer;
x,y: Integer;
maxLineLength: Integer;
linecounter: Integer;
lineheight : Integer;
Begin
{ Set the default margins to 1 inch for top, bottom, 0.75 inch
for left and right margins. GetDeviceCaps is used to get the
printers resolution (dots per inch). The margin variables
contain the position of the margin on paper, in dots, not the
distance from the border of the printing region! }
topMargin := GetDeviceCaps( Printer.Handle, LOGPIXELSY );
bottomMargin := Printer.PageHeight - topMargin;
leftMargin := GetDeviceCaps( Printer.handle, LOGPIXELSX ) * 3 div 4;
rightMargin := Printer.PageWidth - leftMargin;
{ Start the print job, assign the memo font to the printer canvas.
Note that we have to make sure the print job is closed properly,
thus the try finally block. }
Printer.BeginDoc;
try
Printer.Canvas.Font.PixelsPerInch :=
GetDeviceCaps( Printer.Canvas.Handle, LOGPIXELSY );
{ This is really only necessary for Delphi 1, due to a
buglet. }
Printer.Canvas.Font:= aMemo.Font;
{ Determine the height of a line in pixels }
lineHeight := Printer.Canvas.TextHeight('Ay');
If centerHorizontal Then Begin
{ Iterate once over all lines of the memo and determine the
length, in pixels, of the longest line. We need that to
adjust the leftMargin to center the text. }
maxLineLength := 0;
For linecounter := 0 To aMemo.Lines.Count-1 Do Begin
x:= Printer.Canvas.TextWidth( aMemo.Lines[linecounter]);
If x > maxLineLength Then
maxLineLength := x;
End; { For }
{ Adjust the leftMargin to center the text into the
available space between the current margins. }
x := leftMargin +
(rightMargin-leftMargin-maxLineLength) div 2;
If x < leftMargin Then Begin
{ Problem, longest line does not fit into the available
space! We leave the margin untouched, the line will
be truncated. }
End { If }
Else
leftMargin := x;
End; { If }
If centerVertical Then Begin
{ Calculate the vertical space needed to print all lines,
adjust the topMargin to center the text as needed. }
y := lineHeight * aMemo.Lines.Count;
If y < (bottomMargin - topMargin) Then Begin
topMargin :=
topMargin + (bottomMargin - topMargin - y) div 2;
End; { If }
{ Else
space needed is larger than the height of a page,
so ignore the center request. }
End; { If }
{ Margins have been calculated, so we are finally set up
to start printing lines. In the following code x and y
hold the starting position of the next line. }
x:= leftMargin;
y:= topMargin;
For linecounter := 0 To aMemo.Lines.Count-1 Do Begin
Printer.Canvas.TextOut( x, y, aMemo.Lines[linecounter]);
y := y + lineHeight;
If y >= bottomMargin Then Begin
{ Eject the page and start a new one, but only if
the line we have just printed is not the last line
anyway. EndDoc will eject the current page. }
If linecounter < (aMemo.Lines.Count-1) Then Begin
Printer.NewPage;
y:= topMargin;
End;
End; { If }
End; { For }
finally
Printer.EndDoc;
end;
End; { PrintMemo }
Peter Below (TeamB) 10011...@compuserve.com)
No replies in private e-mail, please, unless explicitely requested!
>> In this procedure writeln() is used to send output to a printer. The
>> problem is that the left and top margin are to small (10mm).
>> With "OffsetWindowOrg(Printer.Canvas.Handle, -200, -200)" the margins
>> of the first page are wide enough (20mm), but on following pages
>> margins are to small again. Is there a possibilty to set the margins
>> fixed to 20mm?
>
>René,
>
>the problem is that the printer DC is reset (by windows) to its default
>state after each NewPage. You have to redo the OffsetWindowOrg for each new
>page but using AssignPrn the page breaks are out of your control, you have
>no way to detect them. Do not use AssignPrn, do the text output yourself
>using methods of the Printer.Canvas. This gives you complete control over
>printing positions, margins etc..
The reason I used AssignPrn has something to do with this piece of the
routine:
try
Writeln(F);
BlobStream:=TBlobStream.Create(TMemoField(Fields[iField]),bmRead);
repeat
BytesRead := BlobStream.Read(Buffer, 255);
Buffer[BytesRead] := Chr(0);
write( F, buffer );
until ( BytesRead = 0 );
finally
BlobStream.free;
Writeln(F);
end;
When textout() is used you have to retrieve one line from the
memofield at a time and I don't know how to do this. The problems are
of cause that not all lines have the same lenght and lines can be
longer than the width of a page.
(I the routine I currently use DBMemo.lines[x] is used, but I want to
rewrite the routine without this component)
Any suggestions how to solve this problem?
--snip--
> When textout() is used you have to retrieve one line from the
> memofield at a time and I don't know how to do this.
Var
sl: TStringlist;
sl:= TStringlist.Create;
sl.assign( thememofield );
If the text contained in the field needs to be wordwrapped to a certain width
you can use the DrawText or DrawTextEx API functions to do that for you. Pass
Printer.Canvas.Handle as first parameter. Lets see, i should have an example
for that as well...
Var
S: String;
R: TRect;
Begin
With Printer Do Begin
BeginDoc;
try
Canvas.Font := memo1.Font;
{ Set up output rect, using margins of 0.75 inch top and left,
0.5 inch right and bottom }
R.Left := GetDeviceCaps( Handle, LOGPIXELSX ) div 4 * 3 -
GetDeviceCaps( Handle, PHYSICALOFFSETX );
R.Top := GetDeviceCaps( Handle, LOGPIXELSY ) div 4 * 3 -
GetDeviceCaps( Handle, PHYSICALOFFSETY );
R.Right:= GetDeviceCaps( Handle, PHYSICALWIDTH ) -
GetDeviceCaps( Handle, LOGPIXELSX ) div 2 -
GetDeviceCaps( Handle, PHYSICALOFFSETX );
R.Bottom:=GetDeviceCaps( Handle, PHYSICALHEIGHT ) -
GetDeviceCaps( Handle, LOGPIXELSY ) div 2 -
GetDeviceCaps( Handle, PHYSICALOFFSETY );
// The following statement makes sure the font is selected
// into the device context of the canvas.
Canvas.TextOut( 0,0, ' ');
S:= memo1.Text;
DrawText( Canvas.Handle,
PChar(S),
Length(S),
R,
DT_WORDBREAK or DT_LEFT or DT_NOPREFIX );
finally
EndDoc;
end;
End;
End;
This assumes the text will fit onto a single page. Otherwise one should
use DrawTextEx, which returns information on how much of the text did
fit into the passed output rectangle.
Using DrawText has the benefit of having the text reformatted
automatically to the width of the output rectangle. Hard linebreaks will
be honored, however.