In-Text Formatting In Rave

Category

Rave - General

Question

How can I change text font, color, etc., "on the fly" in reports, or in specific text embedded in larger strings?

Solution

Users of ReportPrinter Pro's code-based reporting features are probably aware of how easy it is to modify text format characteristics at any point in the print output: simply call the appropriate methods of TBaseReport, and then continue outputing text. What do you do if you need to do something similiar with a RAVE report, such as printing in bold font a data variable in the middle of a normal font text expression? There is in fact a way to accomplish this and similar jobs.

Your-mileage-may-vary statement: The technique I am about to describe Nevrona Designs has left undocumented deliberately. The system on which it is based is used internally by RAVE, as part of the support for RTF display and printing. As such, it is subject to change without notice. Regard it as you might the Delphi RTTI system. Borland documents RTTI only minimally, and does not encourage its general use, because the system may need to be changed with each release of Delphi. On the other hand, it's a useful resource for the programmer and there are many tasks difficult if not impossible to perform without utilizing it. In short, read on, but use at your own risk.

If you will look in the file RPDefine.pas (located in Rave4\Source in a standard installation), you will find a large number of constants starting with the characters "RPTF". Combinations of these constants can be used to pass formatting information to RAVE printing controls. For instance, just below the RPTF constant definitions in RPDefine is a set of "RPTF Macros", combinations of RPTF constants which accomplish particular formatting actions. Thus, the combination

BoldOn = RPTFPrefix + RPTFBold + RPTFOn + RPTFSuffix;

will change the text style to bold in the current font.

This combination illustrates the basic structure of an RPTF command: (Prefix character) + (Command) + (One or more parameters) + (Termination character). Multiple RPTF commands can be combined in a single formatting block by separating them with the constant RPTFInfix. Thus, changing both style and color within a single formatting block can be accomplished with a single string, as with this command changing text to red italics in the current font:

ToRedItalics = RPTFPrefix + RPTFItalic + RPTFOn + RPTFInfix + RPTFColor + '$000000FF' + RPTFSuffix;

Two other specific RPTF commands that may be of general interest are RPTFName, which can be used to change font, and RPTFSize, which is used to change font size. The former command takes as a parameter the name of the new font, and the latter the new size in points, converted to a string:

ToCourierNew12 = RPTFPrefix + RPTFName + 'Courier New' + RPTFInfix + RPTFSize + '12' + RPTFSuffix;

More unusual formatting effects are also supported. For instance, note that there are predefined commands in RPDefine for creating superscript and subscript text!

Once you have a formatting string that will do what you want, how do you get it into a RAVE visual component? There are several straightforward ways to do this, depending on what one needs to accomplish. If the format change will be the same for every execution of the report, such as always changing the style of a data variable inside of a longer text string, the easiest thing to do is probably to pass the specific formatting strings as report parameters:

In the Delphi application:

uses
	RPDefine;
...
	RaveProject.SetParam('BoldOn', BoldOn);
	RaveProject.SetParam('BoldOff', BoldOff);

In a RAVE TRaveDataText component DataField property:

'The unit containing the main form of the application is named' & Param.BoldOn & UnitName & Param.BoldOff & '.  Its size is' & UnitSize + '.'

This same approach can be used to embed the desired formatting in fixed text (using TRaveDataText, rather than TRaveText components), avoiding the need to build the text from multiple display components.

For situations where, for instance, the desired formatting may depend on the information being formatted, you can take advantage of the formatted data arguments of the WriteXXX methods of the Connection components. For instance,

If (NetIncome < 0) then
		Connection.WriteFloatData(Format('%s%s', [ToRedItalics, FormatFloat('$#,##0.00', NetIncome)), NetIncome)
	else
		Connection.WriteFloatData('', NetIncome)

This approach is a natural for custom connections; for TRPDatasetConnections, etc., you will need to override the OnGetRow event of the connection and take over the transfer of data between Delphi/C++ Builder and RAVE yourself. If you're unfamiliar with this technique, see Tip #21, "Calculated fields in Rave" in the "Rave - General" section on the "Tips and Tricks" page. Since writing the code for an override of OnGetRow can be tedious if the data set involved has many fields, it may be preferable to define a private RAVE field, as described in the article, to handle the formatted output:

DataModule.CXNBalanceSheetGetCols(Connection: TRPCustomConnection)

begin
	With Connection do begin
		DoGetCols;
		WriteField('FmtNetIncome', dtString, 18, 'Formatted Net Income', '');
	end;
end;
...
DataModule.CXNBalanceSheetGetRow(Connection: TRPCustomConnection)
begin
	With Connection do begin
		DoGetRow;
            If (Dataset.FieldByName('NetIncome').AsFloat < 0) then
		 WriteFloatData('', Format('%s%s', [ToRedItalics, FormatFloat('$#,##0.00', Dataset.FieldByName('NetIncome').AsFloat)))
		else
		 WriteFloatData('', FormatFloat('$#,##0.00', DataSet.FieldByName('NetIncome').AsFloat)))
	end;
end;