Print fields alignment misaligned on report - delphi

I have the following PrintValue code that prints a line to the report (tbasedxreportlink). It prints two fields on one line in the header, the caption and m. The problem is that m is never aligned straight for multiple lines. It always prints all over the place.
How do I get it to align to the right or even print decimal aligned.
Printed Data
Caption One 4,685.33
Caption 2 4.99
Caption three 74,586.88
Caption 4 58.66
Code
procedure PrintValue(Caption, Value: string);
var
m: string;
s: string;
begin
m := FormatFloat(',0.00 ;(,0.00);0.00 ', StrToFloat(Value));
s := Format('%-24s %15s', [Caption, m]);
AReportLink.PrinterPage.PageHeader.LeftTitle.Add(s);
end;
The font used on the report is Segoe UI if it matters.
Thanks

The simplest way is using monospace (fixed-width) font, for example, Courier New or Lucida Console

I found no easy way to format the strings to get the desired effect. The main reason for that is the simplicity of using the LeftTitle, CenterTitle or RightTitle 'boxes' - they only allow simple string text to be inserted. Nothing fancy allowed not to mention the True Type Font issue.
In order to solve the problem I added a tPanel to the screen and dropped the all screen fields I needed to show up on the grid print to it. I added a tdxCustomContainerReportLink to link to that panel. I then used a tdxCompositionReportLink to print both the grid and the tdxCustomContainerReportLink (panel) as individual items when the print button was pressed by overwriting the current link code:
procedure TFrmViewAcct.dxBarBtnPrintClick(Sender: TObject);
begin
dxCmpntPrtrDetail.CurrentLink := dxCmpntPrtrDetailLink2;
inherited;
end;
Thus it prints the grid info then prints what ever is on the panel. Problem solved and you can see how this solution can be flexible.
Yes I could have easily changed to a True Type font but that is an ugly workaround as far as I am concerned especially where standardized fonts need to be observed.

Related

Custom drawing of TCustomListbox items

I'm rewriting a VCL component showing a customized TCustomListbox to Firemonkey in Delphi 10.2. The customization used an overridden DrawItem, basically adding some indentation and setting the text color depending on the item text and index.
DrawItem made it rather easy, but there seem to be nothing like that in FMX. I can override PaintChildren and draw every item myself, but then it looks differently and I have to deal with scrolling and everything myself. I'm just starting with FMX and don't have the sources yet.
Is there a DrawItem replacement in FMX? I may have missed it.
If not, how do it get the needed information? Basically, the rectangle to draw in and ideally the style used.
Problems
The solution by Hans works, but has some major problems:
Color
Setting the color doesn't work, the text is always black. I tried various possibilities including this one:
PROCEDURE TMyItem.Paint;
BEGIN
TextSettings.FontColor := TAlphaColorRec.Red;
INHERITED;
END;
Speed
Opening a box with 180 Items takes maybe two seconds. We need that many items and their count is actually the reason why we need a customized box (we provide filtering using the TEdit part of our component). A version using strings without TMyItem was faster (though probably slower than the VCL version), but using these items seems to slow it down even more (it's slower than filling an HTML list styled similarly).
Or something else? Having no sources and practically no documentation I can't tell.
I tried to cache the items for reuse, but this didn't help.
It looks like using custom items is actually faster than using strings, (timing in milliseconds):
nItems String TMyItem
200 672 12
2000 5604 267
20000 97322 18700
The speed problem seems to accumulate when the content changes multiple times. I was using FListBox.Items.Clear;, then I tried
n := FListBox.Items.Count;
FOR i := 0 TO n-1 DO FListBox.ListItems[n-1-i].Free;
and finally FListBox.Clear;, which makes most sense (and which I found last). Still, in the end it seems to need 2 ms per item.
Here is an example of how it can be done. The key is to set the Parent of the (custom) ListBoxItem to the ListBox. This will append it to its list of items. I set the parent in the constructor, so I don't have to do it (and remember it) each time I add something to a listbox.
type
tMyListBoxItem = class(TListBoxItem)
strict private
fTextLabel: TLabel;
public
constructor Create(aOwner: TComponent);
property TextLabel: TLabel read fTextLabel;
end;
implementation
constructor tMyListBoxItem.Create(aOwner: TComponent);
begin
inherited;
fTextLabel := TLabel.Create(self);
fTextLabel.Parent := self;
Assert(aOwner is TFMXObject, 'tMyListBoxItem.Create');
Parent := TFMXObject(aOwner);
end;
procedure tMyForm.FillListBox(aListBox: TListBox; aStringList: TStringList);
var
lItem: tMyListBoxItem;
i: integer;
begin
aListBox.BeginUpdate; //to avoid repainting for every item added
aListBox.Clear;
for i := 0 to aStringList.Count-1 do
begin
lItem := tMyListBoxItem.Create(aListBox);
lItem.TextLabel.Text := aStringList[i];
lItem.Margins.Left := 20;
end;
aListBox.EndUpdate;
end;
I use custom ListBoxItems in many places now because you can have ComboBoxes, EditBoxes, and all other controls in a ListboxItem. This opens for a very dynamic (list based) screen layout that easily adapts to all platforms and screen sizes.

Is it possible to change the inter-character spacing in a Delphi FMX TEdit control?

I am in the process of creating a custom component descended from a FMX TEdit control. One requirement is I need this control to be able to mimic/act like a combed field. This requires a max length and increased spacing between characters so the characters fall between the vertical lines. Please see image below for example.
The max length functionality is already part of the TEdit control but I am unable to find any information on how to increase the spacing between characters. I've looked into Delphi source code and have not come across anything that might be helpful. The font settings I came across were the typical font styles of bold/italic and font alignment of leading/center/trailing.
I also came across TFontStretch under TFontStyleExt but was not able to find out much more about it. Delphi's own website states "Embarcadero Technologies does not currently have any additional information." I'm not even sure this is related to what I'm looking for but I'm shooting from the hip on this one.
If anyone can point me in the right direction it would be much appreciated.
Thank you
I think that the best solution for your would be to use one of Monospaced fonts
If you can't find Monospaced ont that has desired character with to fit properly into your control you might want to use one of many font editing tools that you can find online to make necessary changes to your desired font.
And the best thing about using of Monospaced font is that you can use it in just about every FMX control that allows you to specify which font to use.
Do note that you will probably have to ship this custom font with your application and then dynamically register it at application start and unregister it at application close.
I used this to implement a serial key typing style:
procedure TForm4.Edit1Typing(Sender: TObject);
var
atext,tmp_str,d_str:unicodestring;
index:integer;
begin
if not(edit1.Text.Length>=30)then // 30 is the max length
begin
///////////////// take ' ' out (space between letters)
atext:=edit1.Text;
tmp_str:='';
if not(edit1.Text.Length=0) then
begin
for index := 1 to Length(aText) do
begin
if not(aText[index]=' ')then
begin
tmp_str:=tmp_str+aText[index];
end;
end;
end;
///////////////// now put the data back to the edit with the space
d_str:='';
if not(Length(tmp_str)=0) then
begin
for index := 1 to Length(tmp_str) do
begin
d_str:=d_str+tmp_str[index]+' ';
end;
end;
edit1.Text:=d_str;
edit1.CaretPosition:=length(d_str)-1;
end;
end;
Override your component Ontyping event handler, this code puts a 'space' between letters so they would be drawn within each rectangle.
Make sure that your font size would allow that.
This is the result.

What are the implications of using Canvas.TextOut?

Introduction
My question comes from a rather interesting problem I have been dealing with for the past few days. I recently asked a question regarding Writing a custom property inspector - How to handle inplace editor focus when validating values?
I have since made some nice progress with my control such as adding a divider in the middle to separate between Name and Value rows, and importantly the divider can be used to resize the two columns.
Here is where my problems started, having the inplace editor visible whilst resizing the divider caused a slight slow down on my control. So I further changed the code to only show the inplace editor if the divider is not been resized. So essentially, I used Canvas.TextOut to draw my values as strings, if a row is selected then the Inplace editor is shown above. The inplace editor becomes hidden if the divider is been resized, once the resize operation has complete the inplace editor becomes visible again.
Whilst this solved the slight slowdown issue I mentioned, I was faced with a new problem in that the text from the inplace editor (which is basically a TEdit) differed slightly to the text that I was drawing using Canvas.TextOut
Example 1
The difference is quite subtle but if you look close enough you can just see it:
fig.1 Canvas.TextOut
fig.2 DrawText
You may need to use a screen magnifier to look more closer, but with the SomeText row it is more noticeable in that the spacing between Some and Text and also between the T and e in Text is slightly different.
Example 2
A slightly better example is perhaps comparing between Canvas.TextOut and DrawText to the inplace editor (TEdit) text:
fig.3 Comparison
As you can see the difference here is much more prominent. The string True clearly shows much larger spacing between the text characters when using Canvas.TextOut, where as the DrawText and inplace editor render text exactly alike.
When I was using Canvas.TextOut I was getting all kinds of horrible text mismatches between resizing my inspector divider and showing and hiding the inplace editor. Had I not experimented and tried alternative text drawing methods I don't think I would have ever realised the difference and found a solution. It is important to know that I was using the exact same Font settings when drawing my text to the canvas as the Font I had defined for the inplace editor.
Now that I am using DrawText instead of Canvas.TextOut everything is working in unison with the inplace editor and exactly how I want it to.
Question
My question is what makes Canvas.TextOut render text so differently to DrawText? From my example and dealing with my current problem, it is clear that Canvas.TextOut does not render the text in the same way that a TEdit with the same Font settings does, but DrawText does render text seemingly the correct way.
This makes me question the use of Canvas.TextOut, if it does not render text correctly should I always look to use DrawText instead?
Test Demo
You can test this for yourself with the following code:
type
TForm1 = class(TForm)
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
private
FFont: TFont;
FRect: TRect;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FFont := TFont.Create;
FFont.Color := clNavy;
FFont.Name := 'Segoe UI';
FFont.Size := 9;
FFont.Style := [];
FRect := Rect(10, 30, 100, 100);
Canvas.Font.Assign(FFont);
Edit1.Font.Assign(FFont);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FFont.Free;
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.TextOut(10, 10, 'Canvas.TextOut: [True]');
DrawText(Canvas.Handle, PChar('DrawText: [True]'), Length('DrawText: [True]'), FRect, DT_LEFT);
end;
With the above running on a completely new VCL Project, the result I get is as follows:
fig.4 Test Demo
Again notice the spacing in the string True when using Canvas.TextOut, from my end it is clearly different to DrawText and the way that the TEdit draws its text.
The below is the same image as fig.4 but zoomed in at 400%
fig.5 Test Demo zoomed at 400%
Noticeable differences are seen between the T and e in Text and also T and r in True.
fig.6 The word 'Text' zoomed in at 400% with guidelines
You can see the kerning between the T and e is one pixel closer with DrawText than with Canvas.TextOut (which uses ExtTextOut.)
fig.7 The word True zoomed in at 700% with guidelines
You can see the kerning between the T and r is one pixel closer with DrawText and the Inplace Editor (TEdit) than with Canvas.TextOut (which uses ExtTextOut.)
I have tested several different fonts and here are my findings:
Good:
Arial, Cambria, Candara, Comic Sans MS, Consolas, Courier, Courier New,
Fixedsys, Georgia, Lucida Console, Lucida Sans Unicode, Microsoft Sans
Serif, Tahoma, Terminal and Times New Roman.
Bad:
Calibri, Corbel, Myriad Pro, Segoe UI, Trebuchet MS and Verdana.
The good fonts are the ones that appear to render text the same way as DrawText and the Inpace Editor (TEdit) controls do using Canvas.TextOut. The bad ones show that Canvas.TextOut renders text slightly different to the other methods.
There may some clue here although I am not too sure, but I am adding it anyway just in case.
Observed difference is due to using different WinAPI text rendering functions and their behavior. Specifically character kerning
In typography, kerning (less commonly mortising) is the process of
adjusting the spacing between characters in a proportional font,
usually to achieve a visually pleasing result. Kerning adjusts the
space between individual letter forms, while tracking (letter-spacing)
adjusts spacing uniformly over a range of characters.
DrawText
The DrawText function draws formatted text in the specified rectangle.
It formats the text according to the specified method (expanding tabs,
justifying characters, breaking lines, and so forth).
ExtTextOut (used by Canvas.TextOut)
ExtTextOut declaration:
BOOL ExtTextOut(
_In_ HDC hdc,
_In_ int X,
_In_ int Y,
_In_ UINT fuOptions,
_In_ const RECT *lprc,
_In_ LPCTSTR lpString,
_In_ UINT cbCount,
_In_ const INT *lpDx
);
If the lpDx parameter is NULL, the ExtTextOut function uses the
default spacing between characters. The character-cell origins and the
contents of the array pointed to by the lpDx parameter are specified
in logical units. A character-cell origin is defined as the upper-left
corner of the character cell.
Basically DrawText will automatically draw formatted text and that includes adjusting spacing between characters (kerning), while ExtTextOut will by default use default spacing between characters (no-kerning). If you want to adjust spacing between characters you will have to calculate and provide kerning array (lpDx) parameter.
Those differences are especially visible with some character combinations like T and small letters that visually fit under T, or AV where one V fits over A. Different fonts also have different default kernings and that is reason why some fonts have visually same rendering using both functions and some not. Kerning also depends on font size. For instance characters AV rendered with Arial at 9 pt will have same output with both functions, while Arial at 12 pt will result in different outputs.
First line in following image is drawn with no-kerning using ExtTextOut and second line with automatic kerning using DrawText.

Shifting the erroneous horizontal position of SubItemImages in a Delphi TListView?

In a Delphi TListView, is it possible to shift the erroneous horizontal position of SubItemImages, as they are drawn too far left? Something like this, for example (pseudo-code, which just shows the intention):
x := MyListView.Items[i].SubItemImages[2].HorizontalPosition;
MyListView.Items[i].SubItemImages[2].HorizontalPosition := x + 2;
This screenshot which shows the bug:
No. TListItem.SubItemImages is an integer, and integers don't have horizontal positions.
property SubItemImages[Index: Integer]: Integer read GetSubItemImage
write SetSubItemImage;
You can find this out by looking at the VCL source code, in this case in the ComCtrls unit. The relevant code is in TListItem.GetSubItemImage (code from XE3 shown below, but it's the same as the code in previous versions of Delphi).
function TListItem.GetSubItemImage(Index: Integer): Integer;
begin
Result := TSubItems(FSubItems).ImageIndex[Index];
end;
As far as I can see from the MSDN documentation, there's no way to change that image's location. The columns are created by sending the underlying Windows ListView control an LVCOLUMN record (structure) for each column's definition, which has no location information available to assign. It has a flag to set the image right-aligned (LVCFMT_BITMAP_ON_RIGHT), but nothing else to allow you to actually position the image to a specific location in the column.

Report Builder Spacing DBMemo Delphi

I am using a DBMemo (TPPDBMemo) component in reportbuilder within delphi. The Stretch property is true but the control doesn't always stretch itself out correctly within the region.
For example if there is lower case text, that dips downward, eg, chars like p,g,q... The bottom part of the text will get cut off if it's on the last line.
I tried adding an event to the onPrint to slightly grow the DBMemo when it prints,
procedure Tfrm.ppDBMemPrint(Sender: TObject);
begin
ppDBMem.Height := ppDBMem.Height + 10;
end;
But this didn't work.
Any thoughts on how this can be achieved. If I could simply add a border to the DBMemo that would be ideal, although I do not see that property anywhere.
Thank you!

Resources