Closed BorisU7 closed 2 years ago
Segoe UI is not monospaced. SynEdit reverts to Consolas if the font is not accepted. Which Windows version are you using?
If you want say Cascadia Code Bold as your base font, set the font as Cascadia Code and add Bold to the font style.
Windows 10 with latest updates, Scale is 125% if it is important. Segoe UI is used for the program interface, not the editor. 'Change' button in this dialog execute a TFontDialog that has a Font property. I do not modify it. Then it is applied to the preview TMemo and you can see that the font is perfectly valid. Then the same Font is applied to the SynEdit component and this operation fails.
If I choose in the TFontDialog 'Cascadia Code PL' font with Regular or Italic style - everything goes smooth. Problems arise only for Light, SemiBold etc. styles of the same font.
Just made a test with another font that has many styles: 'Source Code Pro' Behavior is the same. Regular, Italic etc. styles work, others do not.
I don't seem to have this issue. Does it matter how the font is changed? I separately set SynEdit1.Font.Name,size, style, etc to the values I get back from my font dialog's font. Maybe it matters if you do that vs setting SynEdit1.Font := MyNewFont? I hope that makes sense and I'm not just breaking in on a topic that I'm not really that knowledgeable about! I'll experiment and see if I can cause it.
I do it the simplest way.
Editor.Font.Assign(GlobalOptions.EditorFont); Editor.Gutter.Font.Assign(GlobalOptions.EditorFont);
GlobalOptions.EditorFont is what I get from TFontDialog.Font
I can't seem to get it to fail on mine no matter how I assign it, direct equals, .assign or my separate font properties method. I can set it to all of those various fonts listed along with the semilight, black, medium, etc and they do seem to work and to display properly. I wonder if it's being rejected because the font name isn't found in your Screen.Fonts list, or if the font is reporting that it's not monospaced, or if the "can it be used by direct write" is failing. I'm guessing it's the first, but I don't know why the font list might be messed up? Can you breakpoint the IsFontMonospacedAndValid function in SynEditMiscProcs to find out what's failing?
I spoke too soon! I definitely see some issues now, though I haven't been able to get IsFontMonospacedAndValid itself to fail. I think I'm barking up the wrong tree. Will report back.
Fascinating! It looks to me like IsFontMonospacedAndValid works fine, but then when the TFont gets converted to a DWFont it gets changed. I found a stack overflow answer that talks about this (I think) DWFont question
Strange, now I can't reproduce the exception in IsFontMonospacedAndValid :( May be the problem is that Delphi TFont doesn't work correctly with Weight returned from WinAPI call?
Debugged it through VCL code. WinAPI returns a LOGFONT structure. In procedure TFontDialog.UpdateFromLogFont delphi sets up the corresponding TFont object. And practically ignores the lfWeight parameter. In the case of Cascadia Code Light it is equal to 300. Default value for regular font is 400.
I'm fairly sure it all comes down to TSynTextFormat.Create and how it converts TFont to be used in DWrite. The TFont.Name is family plus the extra style/weight info that doesn't fit the old TFontStyle enum. The DWrite.CreateTextFormat takes a font family and if you pass it the TFont.Name unmodified with the extra info, then it does something... strange lol.
In
CheckOSError(TSynDWrite.DWriteFactory.CreateTextFormat(PChar(AFont.Name), nil,
DWFontWeight, DWFontStyle, DWRITE_FONT_STRETCH_NORMAL,
-AFont.Height, DefaultLocaleName, FIDW));
FIDW.SetIncrementalTabStop(TabWidth * FCharWidth);
DWFontWeight should be based on LogFont.lfWeight and not on AFont.Style.
And the first argument should be the FontFamily name which is quite fiddly to get https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritefontfamily-getfamilynames
There are two problems:
@BorisU7 is right. Vcl ignores the lfWeight of the LogFont! For example both 'Cascadia Code' and 'Cascadia Code Light' result in lfWeight 400 (Normal)
SynDWrite should
I have code that gets the correct family name and passes it, but neither DWFont.GetWeight or LogFont.lfWeight seem to work. I'm testing with Source code pro light and DWFont.GetWeight just returns DWRITE_FONT_WEIGHT_NORMAL (as does LogFont.lfWeight.) So at least now it works, it just throws away the extra weight info. I'll look more.
Can you post the code for getting correct Family Name. I can get the correct lfWeight via EnumFontFamiliesEx!
This is what I've been playing with (as you can see it's a bit rough):
DWFont.GetFontFamily(FontFam);
FontFam.GetFamilyNames(Names);
for var i := 0 to Names.GetCount - 1 do
begin
Names.GetStringLength(i, l);
SetLength(S, l);
Names.GetString(i, PChar(S), Length(S) + 1);
FamilyName := S;
end;
vars for the above:
FontFam: IDWriteFontFamily;
Names: IDWriteLocalizedStrings;
l: Cardinal;
s: String;
FamilyName: String;
Your code seams to be using the last name if they are many. Don't you need something like what is described in https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritefontfamily-getfamilynames?
SynDWrite sets the DefaultLocaleName
{
hr = pFamilyNames->FindLocaleName(localeName, &index, &exists);
}
if (SUCCEEDED(hr) && !exists) // if the above find did not find a match, retry with US English
{
hr = pFamilyNames->FindLocaleName(L"en-us", &index, &exists);
}
// If the specified locale doesn't exist, select the first on the list.
if (!exists)
index = 0;
UINT32 length = 0;
// Get the string length.
if (SUCCEEDED(hr))
{
hr = pFamilyNames->GetStringLength(index, &length);
}
I'm sure that's true! I was kind of excited just to get something that seemed to give the name I was looking for lol. I can check that out and try to make something less rough if you'd like.
What you did was helpful. I will try to combine it with what I am doing regarding the weight. Thanks!
Here's an improved version (somewhat):
DWFont.GetFontFamily(FontFam);
FontFam.GetFamilyNames(Names);
if Names.GetCount > 0 then
begin
Names.FindLocaleName(DefaultLocaleName, Index, Exists);
if not Exists then
Index := 0;
Names.GetStringLength(Index, NameLength);
SetLength(FamilyName, NameLength);
Names.GetString(Index, PChar(FamilyName), Length(FamilyName) + 1);
end
else
raise Exception.Create('Font not found.');
Does this work?
DWFont.GetFontFamily(FontFam);
FontFam.GetFamilyNames(Names);
if Names.GetCount > 0 then
begin
Names.FindLocaleName(DefaultLocaleName, Index, Exists);
if not Exists then
begin
Names.FindLocaleName('en-us', Index, Exists);
if not Exists then
Index := 0;
end;
Names.GetStringLength(Index, NameLength);
SetLength(FamilyName, NameLength);
Names.GetString(Index, PChar(FamilyName), Length(FamilyName) + 1);
end
else
raise Exception.Create('Family Name not found');
Tested with a bunch of fonts. No problems seen. Great job! Thanks!
Found a strange issue. Any font where you can choose "Medium" as the style ends up with a FontStyle of fsBold and so ends up with a DWFont.Weight of bold instead of medium which causes the CharWidth to end up too wide for the displayed font. I see this on: Fira Code Medum JetBrains Mono Medium Source Code Pro Medium If I bypass the TFontStyle check in GetCorrectFontWeight it fixes the issue. I'm not sure what the workaround would be (since it seems like a TFontDialog/TFont bug) but perhaps we could get the enumerated font weight first and only check for fsBold if that result was FW_NORMAL? I'm only half awake right now so that may be a terrible idea lol. I'm happy to look into this more or do anything else you'd like to assign to me.
I am not a big specialist in the windows internals but ... As you can see from my screenshot Memo control is perfectly fine with TFont and shows the correct result. So probably one can select this TFont into the DC context and get the complete info for it with correct lfWeight etc.
Got updated code from git. The problem seems fixed for me.
Sorry, but I found one more issue.
Here is the comparison between the same font rendered in TMemo and TSynEdit
Screenshot is made with scaling at 200% where the difference is clear.
Some further investigation showed that this is not a problem of SynEdit. Current SynEdit code properly shows ExtraLight style thinner than Light. While in TMemo ExtraLight is somehow thicker.
@BorisU7 I think SynEdit is indeed now rendering the ExtraLight weight properly. Your Memo is just showing the standard weight. As you said Vcl is ignoring the font weight.
@MShark67 Good catch with Medium. The issue stems from Vcl setting bold to true when you assign a font with weight greater than 400 (normal) needs a bit of thinking. At least on my part.
When I try to change the font in SynEdit, in some cases the font is rejected by the IsFontMonospacedAndValid procedure despite being a valid monospaced scalable font. I had this issue with a Cascadia Code font (Cascadia Code) when the font style is not one of Regular, Italic, Bold, Bold Italic. I can preview these rejected font variations in TMemo control and they look fine. Far Manager also has no problems.
The worst thing is that after the attempt to set such a font SynEdit switches to some unrelated proportional font (may be the Parent font?).