OnGetFontFolder

Using FlexCel version 6.21. For some reason, I have begun getting a new error message when I try to view a pdf in my programs after creating it: 


"Project x raised exception EFlexCelPdfException with message 'Cannot find the font "Arial Narrow".   ...You might fix this by setting an OnGetFontFolder event...

The program is not in a server. Looking for example code on how to set an OnGetFontFolder event.

Hi,

First of all,I need to apologize for how long it took to answer this question. I still have your email open in my queue of mails to answer, it is just that for one reason or another it kept being delayed.

Now, about the question itself: You can use a OnGetFontFolder event, but my guess is that it won't change anything. FlexCel by default looks in c:\Windows\Fonts, soif you setup a OnGetFontFolder that redirects FlexCel to the same place, it won't change much.

But in any case, to assign the event you would do something like this:


pdf := TFlexCelPdfExport.Create(xls, true);
  try
  ...
  pdf.GetFontFolder := MyGetFontFolder;



Where MyGetFontFolder is a method in a class (it can't be a standalone method: it must be a member of an object):


private:

    procedure MyGetFontFolder(const sender: TObject; const e: TGetFontFolderEventArgs);
...



procedure TVCLTestForm.MyGetFontFolder(const sender: TObject; const e: TGetFontFolderEventArgs);
begin
  e.FontPath := 'c:\Windows\Fonts';
  e.Applied := true;
end;




That is all there is to setting the event. Now, as said, I don't believe it will solve your problem. I think that  you will need to either install Arial Narrow in the machine, or not use Arial Narrow in the files you are exporting.

Can you check if you have the following files:
ARIALN.TTF
ARIALNB.TTF
ARIALNBI.TTF
ARIALNI.TTF

In your Windows\Fonts folder?
Note that by default the Windows Explorer won't show filenames in c:\Windows\Fonts, but you can open a cmd.exe window and type:
dir c:\Windows\Fonts\Arial*.*
To find out if the files with Arial Narrow are installed.

Yes, all ttf versions of Arial Narrow are installed. However, they are located in the Arial subfolder of the C:\Windows\Fonts folder, i.e., C\Windows\Fonts\Arial\ARIALN.TTF. 


In all my computers that run fully updated versions of Win10, fonts with multiple font styles are stored in subfolders, not in the C:\Windows\Fonts parent.

I will try the OnGetFontFolder solution when I am back working on this (returning 'C\Windows\Fonts\Arial' as the font folder.

Hi,

Some thoughts:
1. I've never seen Arial Narrow installed in a subfolder. I've checked a lot of machines here, and even downloaded a microsoft prebuilt VM, and in all of them it is in c:\Windows\Fonts.
But I've seen that the Windows explorer can be misleading. Maybe this is the reason?

This is how it shows in my machine:



As you can see in (1) explorer indeed shows the path as c:\Windows\Fonts\Arial, as if it was a folder. But if you right click the font and look at the properties, it should show it is at c:\Windows\Fonts (2)
Also if you check from a command line here, there is indeed no "Arial" folder at all in c:\Windows\Fonts.
So I wonder if this could be what you saw, or do you have a physical real "Arial" folder inside Windows\Fonts?

2. But the worse thing is, that even if it is inside a subfolder, it shouldn't matter. FlexCel searches in Windows\Fonts and all subfolders too. Again, just to be completely sure I rechecked it here, and even if I put the fonts in a subfolder. FlexCel will find it. So this can't be the cause.
I also checked git, and searching in subfolders was added in February 2017.You are using an older FlexCel version (6.21), but 6.21 is from 2018, so it should already search inside subfolders. But in any case, it might make sense to recheck it is 6.21 and not something older, or try with FlexCel 7 just to be sure.

3. if you set the GetFontFolder event to be C\Windows\Fonts\Arial, then FlexCel won't find all fonts at c:\Windows\Fonts.  You can return multiple paths in the GetFontFolder event, separating them with semicolons. So you could return:
'c:\windows\fonts;c:\windows\fonts\arial' 
But again, this wouldn't make a difference, because if you just set c:\Windows\Fonts FlexCel will search in c:\Windows\Fonts\Arial anyway. And you don't need a GetFontFolder event to set the folder to be c:\Windows\Fonts since that's the default if you don't set an event.

Well, you are right about the font. GetFontFolder doesn't help. (But you already knew that.) Here's what I don't understand. I can use Arial Narrow in Excel. I can print to pdf from Excel. I can print to Foxit's pdf printer from Excel. If you have any other suggestions. If I recall from somewhere in the documentation, I can specify a fallback font, So, I guess I will research that.

Yes, the strange thing is that you can see it in Excel, and in fact that GDI+ in Excel seems to bee seeing it too. I don't really know how this could be happening, and where is Excel getting it.


But once thing we should be sure first is that Excel is actually finding it. If you print to PDF in Excel, and then press Ctrl-D in Acrobat, do you see "Arial Narrow" or maybe Excel used a fallback font too?

Yes, Excel seems to be using Arial Narrow. Here is a snapshot of the font properties in a workbook I saved to pdf from Excel:


And here is a snap of two lines of text (first, Arial Narrow; next Aria) from the pdf:


Arial Narrow is such a nice, readable reporting font. However, since I'm apparently the only one with the problem, it's probably not worth your spending much time on it. Thanks for getting back to me, though
Hi,
It is indeed a weird issue, and it hasn't been reported by anyone else. And indeed I can use arial narrow here without issues, and without setting any event or anything.

I expected that Excel would somehow find Arial Narrow, or it wouldn't be showing it. But I am really out of ideas on what could be wrong here and I would really like to find out. Is there a way I could remotely access one of your machines where I can debug the problem and see what's going on? If possible, please let me know at adrian@tmssoftware.com

Also, is this happening in more than one machine? Or only in one?

Sorry I had failed to check this on other machines before. Yes, the problem is specific to this machine (my development environment in VMWare virtual machine). It only started when I first emailed you. Everything worked fine before that.


Before contacting you, I had run System File Checker. However, it probably does not check fonts. I had also reinstalled Arial Narrow, then the entire Arial family, using files from another machine. I will try some other Windows repair options (short of a full reinstall) to see if that resolves the problem. Will let you know if I get any positive results. 

Hi,

Can you try compiling and running the program below?  It might give me more insight on what is going on. Just create a new console app and paste the code.
Let me know what the program outputs to the console, and also send me all the files it copies to c:\temp\testarial* (note that you can change c:\temp to other folder by changing the destfolder constant) 



program arialfinder;


{$APPTYPE CONSOLE}


{$R *.res}


uses
  System.SysUtils,
  IOUtils;


const
  destfolder = 'c:\temp\';
var
  font: string;
  fonts: TArray<string>;
  arialn: string;
  i: integer;
begin
  arialn := '';
  font := 'c:\Windows\Fonts\ARIALN.TTF';
  if TFile.Exists(font) then
  begin
    if arialn = '' then arialn := font;
  end else WriteLn('1. Can''t find Arial Narrow in c:\Windows\Fonts');


  font := 'c:\Windows\Fonts\Arial\ARIALN.TTF';
  if TFile.Exists(font) then
  begin
    if arialn = '' then arialn := font;
  end else WriteLn('2. Can''t find Arial Narrow in c:\Windows\Fonts\Arial');


  fonts := TDirectory.GetFiles('c:\Windows\Fonts', 'ARIALN.TTF', TSearchOption.soAllDirectories);
  if (length(fonts) = 0) then WriteLn('Can''t find ArialN at all')
  else
  begin
    i := 1;
    for font in fonts do
    begin
      if arialn = '' then arialn := font;
      WriteLn('3. Arial found in ' + font + ' (' + IntToStr(i) + '/' + IntToStr(length(fonts)) +')') ;
      TFile.Copy(font, destfolder + 'testarial' + inttoStr(i) + '.ttf');
      inc(i);
    end;
    WriteLn('Files copied to ' + destfolder);
  end;




   ReadLn;
end.



Had to make two small changes to your program:

1. Added System.Types to uses
2. //fonts: TArray<string>;
      fonts: TStringDynArray;
Result were:
1. Can't find Arial Narrow in c:\Windows\Fonts
2. Can't find Arial Narrow in c:\Windows\Fonts\Arial
Can't find ArialN at all

Yet, Windows sees them:




Thanks, I think I get what is happening here.

I was thinking in a problem finding Arial Narrow, but from what I see what happens is that you don't have the actual font "Arial Narrow" at all:  Excel and the others are using "arial", in a Narrow variant, which is not the same as a dedicated "arial narrow" font.

We actually take care of this case for linux (because linux doesn't do this kind of conversion from a font name like "arial-narrow" to "arial", "variant narrow" automatically), but it seems like we will have to add the same for Windows too.

I'll investigate more and let you know.

Well, after more digging, I don't think that it could be different font variants. But what I found, is that if you install arial narrow from Office itself (with the cloud icon), it downloads the font to:

C:\Program Files (x86)\Microsoft Office\root\vfs\Fonts\private
(on this machine)

Can you check if you have arialn.ttf there?  That might explain why Excel can find it but FlexCel cannot (as FlexCel is searching only in c:\windows\fonts)

And finally, in the second screenshot you sent, can you post what you see if you scroll down, choosing arial narrow in the "Metadata" combobox?

I see this here:



Eureka! I found them. No, you found them. The Arial Narrow family fonts are located in:


"C:\Users&lt;UserName>\AppData\Local\Microsoft\Windows\Fonts\ARIALN.TTF"

Now all I have to do is to apply the info you gave me to make GetFontFolder look in the normal location plus the above folder. You spent way more time than anyone would expect, so a big THANK YOU (all-caps intended).
Thanks to you for all the effort you put on your side. This is valuable information, and we will be making this local font folder also a default search destination for FlexCel if you don't specify an event.

I'll be also making some changes to the font folder string, so if a particular folder doesn't exist it doesn't complain. (right now, all folders you specify in GetFontFolder separated by ";" must exist, but it seems more correct to complain if not any of the folders exists). We'll add an option for that, defaulting to throwing an error if there isn't at least one valid folder in the search list.

Here's what I ended up writing as an OnGetFontFolder function:


procedure TfPPC.MyGetFontFolder(const sender: TObject; const e: TGetFontFolderEventArgs);
const
  CSIDL_LOCAL_APPDATA = $001c;
  CSIDL_FONTS = $0014;
var
  aFontFolder, aUserFontFolder: string;
begin
  aFontFolder := GetSpecialFolderPath(CSIDL_FONTS);
  aUserFontFolder :=
    IncludeTrailingPathDelimiter(GetSpecialFolderPath(CSIDL_LOCAL_APPDATA))
    + 'Microsoft\Windows\Fonts';
  if DirectoryExists(aUserFontFolder) then
    e.FontPath := aFontFolder + ';' + aUserFontFolder
  else
    e.FontPath := aFontFolder;
  e.Applied := true;
end;

Some of the above code may be overkill; not sure if I really needed to check if the User's font folder exist after retrieving the folder.

FWIW, I found the User font location in two registry keys:
[HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Fonts] and
[HKEY_USERS\S-1-5-21-4174221731-3867810187-1242189269-1001\Software\Microsoft\Windows NT\CurrentVersion\Fonts]

Thinking back to your docs, maybe a little more robust info like you provided in this post could be useful--especially for programs run on a network. Again, thanks for your help.

Hi,

Yes, right now you need to check if the folder exists before adding it. This new "local fonts folder" was added in Windows 10 version 1809 ( https://thewincentral.com/windows-10-redstone-5-version-1809-all-changes-new-features-so-far-categorized-changelog/ ) so users in Windows 7 or Windows 10 who haven't updated won't have this folder. And FlexCel up to the current version throws an exception if any of the folders in the search path doesn't exist.

For FlexCel 7.6 which should be coming next week we've changed:
1. Now the default search is in the windows font folder and the user font folder (in that order: I've verified that if a file is installed in both, Windows uses the Windows font folder). The code is very similar to your event, but now it will work by default without needing to setup an event.

2. We now by default won't raise an exception if at least one of the folders you pass in the OnGetFontFolder event doesn't exist. There will be still an option to go back to the old behavior (in case you want to make sure all folders exist), but by default we will be more forgiving. Only one of the folders you pass has to exist. 

3. We added a code example in the documentation of GetFontFolder, so now it is clearer how you would add it.

4. We've added a section in the pdf export guide that explains the new "local folder" and more or less what we discussed in this post.

Once again, thanks for your help on this :)

I'll be sure to download the new version (and get rid of the now unnecessary code in my program). A lot of work for one user. However, in searching the internet, several people have complained that some fonts disappeared after one of the Creator updates. Maybe in the future others will benefit after all your work.

FlexCel 7.6 was just released and it should support now the "user fonts" folder

Thanks!