.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Check is font file missing

10 REPLIES 10
SOLVED
Reply
Message 1 of 11
tom.zhengGNS4G
894 Views, 10 Replies

Check is font file missing

Hi! I'm trying to find a way to check if any font file is missing. In AutoCAD, there's a little yellow icon displayed if this font file is missing:

圖片1.png

 

I wrote a command to test, I can access FileName and BigFontFileName property of TextStyleTableRecord:

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Application;

[CommandMethod("TT")]
public void TT()
{
    try
    {
        Document doc = AcadApplication.DocumentManager.MdiActiveDocument;
        Database database = doc.Database;
        Editor editor = doc.Editor;
        using (var tr = database.TransactionManager.StartTransaction())
        {
            var textStyleTbl = database.TextStyleTableId.GetObject(OpenMode.ForRead) as TextStyleTable;
            foreach (ObjectId objId in textStyleTbl)
            {
                var textStyle = objId.GetObject(OpenMode.ForRead) as TextStyleTableRecord;
                editor.WriteMessage($"\nfont file:{textStyle.FileName}, " +
                    $"big font file:{textStyle.BigFontFileName}");
                //...isFontMissing(textStyle.FileName);
            }
        }
    }
    catch (System.Exception ex)
    {
        AcadApplication.ShowAlertDialog("Unhandled Exception:\n" + ex.Message);
        return;
    }
}

 Is it possible to do this via .NET api ? Any suggestion is really appreciated!

Labels (2)
10 REPLIES 10
Message 2 of 11
SENL1362
in reply to: tom.zhengGNS4G

Try these locations:

 

A. For (Big) font files named "*.ttf" look in the folder: System.Environment.SpecialFolder.Fonts

if not found then: missing...

B. For (Big) font files named "*.dbx" look in:

1. Acad.exe folder(Commandline arg[0])

2. Acad Search Path's("ACADPREFIX")

if not found then: missing...

C. For any other fontfile extension (*.shx, or no extension,...)

1. Search A

2. Search B

D. Don't forget to look in the Font MAP file C:\Program files\... *.fmp,  %appdata%\...

It maps fontnames to TTF files.

 

 

Message 3 of 11
tom.zhengGNS4G
in reply to: SENL1362

Thanks for your suggestion. I would give it a try!
But I'm not sure how to get Acad.exe folder with code. (not sure what Commandline arg[0] means). Can you explain that part for me? Thank you
Message 4 of 11
SENL1362
in reply to: tom.zhengGNS4G

var acadCmdline = Environment.CommandLine;
//Or 
var acadArgs = Environment.GetCommandLineArgs();
Message 5 of 11
tom.zhengGNS4G
in reply to: SENL1362

Thanks for your help! I have try your suggestion and it seemed to work good. But I met some issue :

(1) some of dwg files I have tested has text style SIMPLEX and ITALIC, their FileName is also SIMPLEX and ITALIC(with no extension). I can't find them in every directories but in AutoCAD, they are not shown missing. should I skip them? or are they store in Font Map file as you mentioned before? but I'm not sure where would *.fmp file located. (I didn't see any of it in C:/ProgramFiles/... and %appdata%/... )

 

(2) I have tested one file that has a text style named C2. in AutoCAD its font file is txt.shx, but its FileName return txt(with no extension). that made the result being missing (but actually exists in one of search paths). but only one dwg file has this strange issue. I have checked some other dwg files which also contains txt.shx font, but in other files FileName property always return txt.shx

 

below is my code snippet for checking if font file missing based on logic you seggested :

 

 

bool checkIsFontFileMissing(TextStyleTableRecord textStyle)
{
    bool isFontMissing = true;
    bool isBigFontMissing = true;

    string fileName = textStyle.FileName;
    string bigFontFileName = textStyle.BigFontFileName;
    if (fileName == "") isFontMissing = false;
    if (bigFontFileName == "") isBigFontMissing = false;

    bool checkFontFileExist(string file)
    {
        bool findSystemFontFolder()
        {
            string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
            string filePath = System.IO.Path.Combine(folderPath, file);
            return System.IO.File.Exists(filePath);
        }

        bool findAcadFolder()
        {
            string acadPath = Environment.GetCommandLineArgs()[0];
            string acadFolder = System.IO.Path.GetDirectoryName(acadPath);
            string filePath = System.IO.Path.Combine(acadPath, file);

            return System.IO.File.Exists(filePath);
        }

        bool findSearchPath()
        {
            object acadObject = AcadApplication.AcadApplication;
            object preferences = acadObject.GetType().InvokeMember("Preferences", System.Reflection.BindingFlags.GetProperty, null, acadObject, null);
            object files = preferences.GetType().InvokeMember("Files", System.Reflection.BindingFlags.GetProperty, null, preferences, null);
            string searchPathStr = (string)files.GetType().InvokeMember("SupportPath", System.Reflection.BindingFlags.GetProperty, null, files, null);
            string[] searchPaths = searchPathStr.Split(';');

            foreach (string folderPath in searchPaths)
            {
                string filePath = System.IO.Path.Combine(folderPath, file);
                if (System.IO.File.Exists(filePath)) return true;
            }

            return false;
        }

        string extension = System.IO.Path.GetExtension(file);
        if (extension == ".ttf")
        {
            return findSystemFontFolder();
        }
        else if (extension == ".dbx")
        {
            return findAcadFolder() || findSearchPath();
        }
        else
        {
            return findSystemFontFolder() || findAcadFolder() || findSearchPath();
        }
    }
    isFontMissing = !checkFontFileExist(fileName);
    isBigFontMissing = !checkFontFileExist(bigFontFileName);

    return isFontMissing || isBigFontMissing;
}

 

 

 

apologize for the long code. do you have idea about the issues? thanks for your time and help!

Message 6 of 11
SENL1362
in reply to: tom.zhengGNS4G

(1)

Acad.fmp:

C:\Program Files\Autodesk\AutoCAD 20xx\UserDataCache\Support

DwgViewr.fmp:

C:\Program Files\Autodesk\DWG TrueView 2022 - English\UserDataCache\Support

Message 7 of 11
tom.zhengGNS4G
in reply to: SENL1362

Have checked the path you provide, I found a file C:\Program Files\Autodesk\AutoCAD 20xx\UserDataCache\Support\acad.fmp. but not found SIMPLEX and ITALIC in it. I'm wondering if these two are something like built-in font? so they don't have a referenced file.
Message 8 of 11
SENL1362
in reply to: tom.zhengGNS4G

%appdata%\Autodesk\...\Support\Simplex.shx

C:\Program Files\Autodesk\AutoCAD xxx\Fonts\italic.shx

There are a few ways to check Drawing dependencies, for Fonts, the easiest way may be:

1. Create or Open your drawing;

2. Add some Text in your Font;

3. Create Transmittal (Click AutoCAD Logo/Send/eTransmit)

4. View Report

eTransmit.png

 

Message 9 of 11
tom.zhengGNS4G
in reply to: SENL1362

Very informative and helpful! after I test some more files, there're still some strange situations, such as FileName doesn't contain extension(but actually there's file in support search path). but most of them working as expected(same as the result shown in AutoCAD). I'll keep trying when I have time. Thanks for your time and help!
Message 10 of 11
_gile
in reply to: tom.zhengGNS4G

Hi,

 

What about using the HostApplicationServices.FindFile method?

 

Here's an extension method:

 

public static bool TryFindFile(this Database db, string fileName, FindFileHint hint, out string path)
{
    try
    {
        path = HostApplicationServices.Current.FindFile(fileName, db, hint);
        return true;
    }
    catch
    {
        path = null;
        return false;
    }
}

 

 

 

Using example:

 

var db = HostApplicationServices.WorkingDatabase;
if (db.TryFindFile("china.shx", FindFileHint.FontFile, out string path))
{
    // ...
}

 

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 11 of 11
tom.zhengGNS4G
in reply to: _gile

Hi @_gile ! Thanks for your information. I tried your suggestion and implement the function using HostApplicationServices api like below:

(the Active.Database is a helper class in my project which provides an easy way to access Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Database)

bool checkIsFontMissingByHostApplication(string fileName)
{
    bool tryFindFile(FindFileHint hint)
    {
        try
        {
            HostApplicationServices.Current.FindFile(fileName, Active.Database, hint);
            return true;
        }
        catch
        {
            return false;
        }
    }

    return !tryFindFile(FindFileHint.Default) &&
        !tryFindFile(FindFileHint.FontFile) &&
        !tryFindFile(FindFileHint.TrueTypeFontFile) &&
        !tryFindFile(FindFileHint.FontMapFile);
}

this api makes my code much more cleaner and run as expected. but there're 2 files arial.ttf/stylu.ttf can't be found by this approach. these 2 files is located in C:\Windows\Fonts

 

So for now, my workaround is to merge my original approach and HostApplicationServices api together. I have tested some dwg files and this approach works as expected(at least in all my testing file). This approach may be messy and not a good one, but at least it can be a temporary solution. It looks like :

bool checkIsFontFileMissing(TextStyleTableRecord textStyle)
{
    bool checkFontFileExist(string file)
    {
        bool findSystemFontFolder()
        {
            string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
            string filePath = System.IO.Path.Combine(folderPath, file);
            return System.IO.File.Exists(filePath);
        }

        bool findAcadFolder()
        {
            string acadPath = Environment.GetCommandLineArgs()[0];
            string acadFolder = System.IO.Path.GetDirectoryName(acadPath);
            string filePath = System.IO.Path.Combine(acadPath, file);

            return System.IO.File.Exists(filePath);
        }

        bool findSearchPath()
        {
            object acadObject = AcadApplication.AcadApplication;
            object preferences = acadObject.GetType().InvokeMember("Preferences", System.Reflection.BindingFlags.GetProperty, null, acadObject, null);
            object files = preferences.GetType().InvokeMember("Files", System.Reflection.BindingFlags.GetProperty, null, preferences, null);
            string searchPathStr = (string)files.GetType().InvokeMember("SupportPath", System.Reflection.BindingFlags.GetProperty, null, files, null);
            string[] searchPaths = searchPathStr.Split(';');

            foreach (string folderPath in searchPaths)
            {
                string filePath = System.IO.Path.Combine(folderPath, file);
                if (System.IO.File.Exists(filePath)) return true;
            }

            return false;
        }

        bool findByHostApplicationApi()
        {
            bool tryFindFile(FindFileHint hint)
            {
                try
                {
                    HostApplicationServices.Current.FindFile(file, Active.Database, hint);
                    return true;
                }
                catch
                {
                    return false;
                }
            }

            return tryFindFile(FindFileHint.FontFile) ||
                tryFindFile(FindFileHint.TrueTypeFontFile) ||
                tryFindFile(FindFileHint.FontMapFile) ||
                tryFindFile(FindFileHint.Default);
        }

        string extension = System.IO.Path.GetExtension(file);
        if (extension == ".ttf")
        {
            return findSystemFontFolder() || findByHostApplicationApi();
        }
        else if (extension == ".dbx")
        {
            return findAcadFolder() || findSearchPath() || findByHostApplicationApi();
        }
        else
        {
            return findSystemFontFolder() || findAcadFolder() || findSearchPath() || findByHostApplicationApi();
        }
    }

    bool isFontMissing = true;
    bool isBigFontMissing = true;

    string fileName = textStyle.FileName;
    string bigFontFileName = textStyle.BigFontFileName;
    if (fileName == "") isFontMissing = false;
    if (bigFontFileName == "") isBigFontMissing = false;

    isFontMissing = !checkFontFileExist(fileName);
    isBigFontMissing = !checkFontFileExist(bigFontFileName);

    return isFontMissing || isBigFontMissing;
}

 Thanks for all the information you provided! It really helps a lot. I'll update if I find a better approach to do this.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

AutoCAD Inside the Factory


Autodesk Design & Make Report