Controlled output of multiline text (or "block of text") is managed by the TTextBlock object (or its RTF equivalent, TRTFBlock).

In principle, a TextBlock is "opened" with a block of text (either a direct string, or from a file or stream), automatically "rendered" to fit a given area, "printed" in toto or piecemeal as required, and then "closed".

procedure OpenBlock(

BlockText: string;
ABlockLeft: Double = NA;
ABlockRight: Double = NA;
ATextLeftMargin: Double = NA;
ATextRightMargin: Double = NA);

procedure OpenBlockFromFile(

AFileName: string;
ABlockLeft: Double = NA;
ABlockRight: Double = NA;
ATextLeftMargin: Double = NA;
ATextRightMargin: Double = NA);

 

procedure OpenBlockFromStream(

AStream: TStream;
ABlockLeft: Double = NA;
ABlockRight: Double = NA;
ATextLeftMargin: Double = NA;
ATextRightMargin: Double = NA);


Example A: Using a TextBlock On-the-Fly

A block of wrapping text can be output by creating and using a TTextBlock on-the-fly. A TTextBlock can also be dropped on a form for repeated use.

Given a string constant MyNote = 'This is a block of text wrapping over multiple lines.', it could be printed like this:

The Code The Output

YPos := 135;
with TTextBlock.Create(ReportWriter) do

try

OpenBlock(MyNote, 30, 60, 0, 0);
PrintLines;

finally

Free;

end;

Notes:

a) The text starts at a vertical position of 135mm by setting the YPos property.

b) When opening the text block, the left boundary is initialised to 30mm and the right to 60mm.

These boundaries can also be set using the BlockLeft and BlockRight properties of TTextBlock.

c) When opening the text block, the left and right text margins are both set to 0mm.

These margins can also be set using the TextLeftMargin and TextRightMargin properties of TTextBlock.

d) PrintLines simply prints all lines in this case, but it can also facilitate incremental line output (by line count).

Alternatively, incremental output by height can be achieved using the PrintHeight method.

procedure PrintLines(

LineCountLimit: Integer = 0;
EndOnNewLine: Boolean = True);

procedure PrintHeight(

HeightLimit: Double;
HeightMode: THeightMode = hmStopBefore;
EndOnNewLine: Boolean = True);


Example B: Changing TextBlock Boundaries Mid-Output

TTextBlock supports incremental output - but more significantly, you can change the placement of the block, or change the font style, between output steps. This example demonstrates the principle:

The Code The Output

YPos := 135;
with TTextBlock.Create(ReportWriter) do

try

OpenBlock(MyNote, 30, 60, 0, 0);
PrintLines(2);
BlockLeft := 40;
TextStyle := tsBI;
RenderBlock;
PrintLines;

finally

Free;

end;

Notes:

a) Taking the preceding example, only the first two lines are output to start with.

b) The left block margin (BlockLeft) is now reset to 40mm.

The same result could be achieved by setting TextLeftMargin to 10mm.

c) The text style is changed for remaining text by setting the block TextStyle to tsBI.

d) RenderBlock then forces the TextBlock to re-render itself, wrapping at the new (reduced) block width and new text style.

e) A final call to PrintLines outputs the remaining block text.

This incremental output capability allows a text block to be wrapped across a page break, or wrapped around some other object.


Example C: Page Wrapping With a TextBlock

There are a variety of ways to control the output of a TextBlock across a page break. Which method you use will depend on the nature of the report you are implementing and the wrapping rules you want to apply. This example does a simple line count check to see if the block of (plain) text will fit on the current page, and starts a new page if not.

with TTextBlock.Create(ReportWriter) do

try

OpenBlock(MyNote, 30, 60, 0, 0);
if not EnoughBandLines(BlockLineCount) then

NewPage;

PrintLines;

finally

Free;

end;

If you were using a rich text block (TRTFBlock) with the potential for lines of differing height, use the BlockHeight method with ReportInterface.EnoughBandHeight instead:

if not EnoughBandHeight(BlockHeight) then

NewPage;

PrintLines;

An alternative strategy would be to print what you can on the current page, and then start a new page if there is some remaining text to print:

PrintLines(BandLinesLeft);
if not IsFinished then

NewPage;

PrintLines;

It may be convenient to print one TextBlock line at a time:

while not IsFinished do

begin

if (BandLinesLeft = 0) then

NewPage;

PrintLines(1);

end;

Remember that PrintLines ends on a new line by default, but you can prevent this behaviour by passing parameter EndOnNewLine = False to stay on the current line.


Example D: Pre-Rendering a TextBlock

Consider Example B above. Determining whether the text will fit in the available space is made more complicated because the block width and text style are altered part way through output (narrower line width with wider, bold/italic text). Thus, the final line count may be greater than that assessed at the start of block.

The solution is to "pre-render" the text block as you plan to print it, but without actually outputting anything to the actual page. For this purpose, the PrintLines and PrintHeight methods have matching methods RenderLines and RenderHeight.

procedure RenderLines(

LineCountLimit: Integer = 0;
EndOnNewLine: Boolean = True);

procedure RenderHeight(

HeightLimit: Double;
HeightMode: THeightMode = hmStopBefore;
EndOnNewLine: Boolean = True);

 

with TTextBlock.Create(ReportWriter) do

try

OpenBlock(MyNote, 30, 60, 0, 0);
RenderLines(2);
BlockLeft := 40;
TextStyle := tsBI;
RenderBlock;
RenderLines;

if not EnoughBandLines(BlockLineCount) then

NewPage;


BlockLeft := 30;
TextStyle := tsNormal;
ResetBlock;
PrintLines(2);
BlockLeft := 40;
TextStyle := tsBI;
RenderBlock;
PrintLines;

finally

Free;

end;

Notes:

a) The first part of the code is identical to the original code above except that RenderLines is called instead of PrintLines - same mechanics, but no actual output.

b) When BlockLineCount is checked, it now represents the composite result of the two output segments (returning 4 lines instead of the 3 seen in Example A).

c) The blocks left boundary and text style are restored to the starting state.

d) ResetBlock restores the text position to the start, as it was immediately after the block was opened.

e) Actual text output now proceeds just as it did in Example B.