Dynamically Adding Fonts

I’m having issues making Words find new fonts that are added at runtime in rendering a PDF. I’m needing to add them at runtime because we’re using a fonts folder that fill up dynamically.

@qquark518

To dynamically add fonts at runtime in Aspose.Words while rendering a PDF, you can utilize the FontSettings class to specify the directories where your fonts are located. This allows Aspose.Words to search for and use the fonts from those directories during document processing.

Here’s a step-by-step guide on how to set this up:

  1. Set Up Font Sources: You can specify one or more folders that contain your fonts using the SetFontsFolders method. This method allows you to include fonts from specified directories, and you can choose whether to include fonts from subdirectories.

  2. Load Your Document: After setting the font sources, you can load your document as usual.

  3. Render the Document: When you save or render the document, Aspose.Words will look for the specified fonts in the directories you provided.

Here’s a code example demonstrating how to implement this:

using Aspose.Words;
using Aspose.Words.Fonts;

class Program
{
    static void Main()
    {
        // Create a new document
        Document doc = new Document();

        // Set the font settings to include a folder with custom fonts
        FontSettings fontSettings = new FontSettings();
        fontSettings.SetFontsFolders(new[] { @"C:\MyFonts\" }, true); // Set to true to include subfolders
        doc.FontSettings = fontSettings;

        // Add some text with a specific font
        DocumentBuilder builder = new DocumentBuilder(doc);
        builder.Font.Name = "YourCustomFont"; // Replace with the name of your font
        builder.Writeln("This is a sample text using a custom font.");

        // Save the document as PDF
        doc.Save("Output.pdf");
    }
}

In this example, replace "YourCustomFont" with the actual name of the font you want to use, and ensure that the font files are located in the specified directory. This approach allows you to dynamically manage fonts as they are added to your fonts folder at runtime.

By following these steps, you should be able to successfully render PDFs using dynamically added fonts in Aspose.Words. If you encounter any issues, consider checking the font names and ensuring that the font files are accessible in the specified directories.

For more detailed information, you can refer to the Aspose documentation on manipulating and substituting TrueType fonts.

Sources:
[1]: Manipulate and Substitute TrueType Fonts - Aspose Documentation

Doesent work.
I tried:

  1. Set font settings by FontSettings:
FontSettings.DefaultInstance.SetFontsFolder("/usr/share/fonts/truetype/",true);
  1. Set Document.FontSettings
fontSettings.SetFontsFolders(new[] { @"/usr/share/fonts/truetype/" }, true); // Set to true to include subfolders
doc.FontSettings = fontSettings;

But actually i dont need to change or add new folder, cuz folder, that dynamically filled is default folder.

Probably i need to refresh some cache?

@qquark518 Do you mean you are adding additional fonts in the the fonts folder specified in FontSettings.DefaultInstance while rendering the document?

add fonts before rendering documents

@qquark518 Thank you for additional information. If all required fonts are already in the folder you can use FontSettings.DefaultInstance and do not specify separate font setting for each document.
Could you please elaborate the purpose of adding the fonts into the folder in runtime? Reading fonts from sources is quite resource consuming operation, so Aspose.Words reads them once and caches. If read the fonts each time, this might significantly reduce the performance.

To avoid time losses on adding fonts by DevOps

What we tried recently:

  • Add another folder in the FontSettings.DefaultInstance (ignore FolderFontSource on convert)
  • Add MemoryFontSource FontSettings.DefaultInstance (ignore default folder on convert)

example code:

var defaultSources = FontSettings.DefaultInstance.GetFontsSources();
var newSources = new List<FontSourceBase>(defaultSources.Length);
newSources.AddRange(defaultSources);
newSources.Add(folderFS);//test FolderFontSource
newSources.Add(memFS);// test MemoryFontSource
FontSettings.DefaultInstance.SetFontsSources(newSources.ToArray());

Probably implement FontSourceBase or set config\rule for fonts?

new MemoryFontSource(fileBytes)
rewrite default source that MemoryFontSource as well :smiling_face_with_tear:

@qquark518 Have you tried implementing FontSourceBase.WarningCallback to get notifications if there are problems reading fonts from the specified font sources?

Also, please try using the following code to check what fronts are available in the specified font sources:

/// <summary>
/// Prints the fonts avaialble in the specified font settings.
/// </summary>
public static void PrintAvaialbleFonts(FontSettings fs)
{
    foreach (FontSourceBase fsb in fs.GetFontsSources())
    {
        Console.WriteLine(fsb.Type);
        foreach (PhysicalFontInfo pfi in fsb.GetAvailableFonts())
        {
            Console.WriteLine(pfi.FullFontName);
        }
        Console.WriteLine("================================================");
    }
}

I tried implement FontSourceBase
RuntimeWordFontSource : FontSourceBase
RuntimeWordFontSource load all fonts from system folder, and then can expand list that returns in GetAvailableFont() in runtime

but unfortionatly have exception on
`FontSettings.DefaultInstance.SetFontsSources([runtimeFontSource])

so i decompile protected assembly…

And now I don’t think I’m gonna be able to implement FontSourceBase
Сan you please explain how fonts are stored and what is behind the uh enumeration?

@qquark518 You cannot extend FontSourceBase. This is a base class for font sources available through Aspose.Words API: FileFontSource, FolderFontSource, MemoryFontSource, StreamFontSource and SystemFontSource.

Solved as follows:

  1. Download fonts to the system folder
  2. Create SystemFontSource
  3. SetFontsSources it is at this step the data on fonts are recalculated, the step is obligatory, in spite of the fact that if we received fonts from the already existing SystemFontSource, all necessary fonts would be there, but they don’t work without this step.

Code:

public async Task Update(CancellationToken cancellationToken)
    {
        var fonts = await fontLoadService.GetAllCustomFonts(cancellationToken);
        
        if (fonts.Count == 0)
        {
            return;
        }
        
        CreateDirectoryIfNeeded(fontDirectoryPath);
        var isNewFontLoaded = await SaveFonts(fonts, cancellationToken);
        DisposeFontStreams(fonts.Select(el=>el.Value).ToList());
        
        if (!isNewFontLoaded)
        {
            return;
        }

        foreach (var sourceRefresh in sourceRefreshService)
        {
            sourceRefresh.Refresh();
        }
    }

    /// <summary>
    /// Refresh word source
    /// </summary>
    public void Refresh()
    {
        var systemFontSource = new SystemFontSource();
        FontSettings.DefaultInstance.SetFontsSources([systemFontSource]);
    }

it’s not a frequent operation, so this solution works for us.

@qquark518 It is perfect that you managed to resolve the problem. Thank you for sharing your solution.