TX Text Control .NET for WPF

Font Embedding and Substitution

Topic

Which fonts a PDF shows is decided long before Save is called: when a document is loaded or formatted, every font that isn't available is replaced, and the replacement changes line and page breaks. For a WPF application that must produce the same PDF on every machine, or PDF/A, which embeds all fonts, you restrict the fonts a document may use and choose the fonts that replace missing ones.

How to find the replaced fonts of a loaded document is shown in Check the Fonts of a Document. Text that looks different than expected is covered in Fonts and Document Layout in Troubleshooting.

Where the Fonts Come From

TX Text Control .NET for WPF formats text with the fonts installed on Windows. The fonts it supports depend on the formatting device, the printer set with FormattingPrinter, and GetSupportedFonts returns their names once the control is initialized; before, it returns null.

A font is adapted, which means replaced by a supported font, when:

  • a document is loaded that uses a font that isn't supported,
  • another printer is set with FormattingPrinter, or
  • the fonts are restricted with EmbeddableFontsOnly or TrueTypeFontsOnly of FontSettings.

Restrict the Fonts of a Document

The properties of FontSettings decide which fonts a document may use. All of them are false by default:

PropertyWhen true, a document may use
EmbeddableFontsOnlyFonts that can be embedded without violating their licenses; TX Text Control treats only OpenType and TrueType fonts as embeddable. Required for PDF/A.
TrueTypeFontsOnlyTrueType and OpenType fonts
ScalableFontsOnlyFreely scalable fonts

The settings of the window's editor come from TextControl.FontSettings of TXTextControl.WPF, and a ServerTextControl has its own FontSettings.

Choose the Replacement Fonts

TX Text Control chooses a replacement by itself. The AdaptFont event lets you choose it instead. The following code in MainWindow.xaml.cs restricts the editor to TrueType and OpenType fonts when the control has loaded, replaces other fonts with the first available font of a list, and shows the replaced fonts after a template has been exported as PDF. List<string> needs using System.Collections.Generic; if implicit usings are off:

private readonly string[] preferredFonts = { "Arial", "Segoe UI", "Times New Roman" };
private readonly List<string> replacedFonts = new List<string>();

private void textControl1_Loaded(object sender, RoutedEventArgs e)
{
    textControl1.FontSettings.TrueTypeFontsOnly = true;
    textControl1.FontSettings.AdaptFontEvent = true;
    textControl1.AdaptFont += TextControl1_AdaptFont;
}

private void TextControl1_AdaptFont(object sender, AdaptFontEventArgs e)
{
    // Use the first preferred font that is available
    foreach (string font in preferredFonts)
    {
        if (Array.IndexOf(e.SupportedFonts, font) >= 0)
        {
            e.AdaptedFontName = font;
            break;
        }
    }

    replacedFonts.Add(e.FontName + " -> " + e.AdaptedFontName);
}

private void LoadAndExportButton_Click(object sender, RoutedEventArgs e)
{
    replacedFonts.Clear();

    textControl1.Load("template.docx", StreamType.WordprocessingML);
    textControl1.Save("template.pdf", StreamType.AdobePDF);

    if (replacedFonts.Count > 0)
    {
        MessageBox.Show(this, "Replaced fonts:\n" + string.Join("\n", replacedFonts), "Fonts");
    }
}

The Loaded handler is connected in MainWindow.xaml, where the TextControl is declared with the WPF prefix:

<WPF:TextControl x:Name="textControl1" Loaded="textControl1_Loaded" />

The code works as follows:

  • The settings are made in the Loaded event of the TextControl, when the control is ready.
  • AdaptFontEvent must be true, or AdaptFont doesn't occur. The syntax block of the reference spells the property AdaptFontsEvent, but the compiled name is AdaptFontEvent.
  • FontName is the original font. AdaptedFontName contains the replacement TX Text Control chose, and a new value replaces it.
  • A replacement must be one of the SupportedFonts. If no font of the list is available, the choice of TX Text Control stays.

Fonts in a ServerTextControl

A ServerTextControl doesn't share the settings of the editor. It has its own FontSettings and its own AdaptFont event, and its settings are made after Create and before Load, so that they apply to the fonts of the loaded document:

using (ServerTextControl tx = new ServerTextControl())
{
    tx.Create();

    tx.FontSettings.TrueTypeFontsOnly = true;
    tx.FontSettings.AdaptFontEvent = true;
    tx.AdaptFont += TextControl1_AdaptFont;

    tx.Load("template.docx", StreamType.WordprocessingML);
    tx.Save("template.pdf", StreamType.AdobePDF);
}

Both events pass an AdaptFontEventArgs, so one handler serves both. Its SupportedFonts belong to the ServerTextControl, which has its own FormattingPrinter.

Embed Fonts in PDF/A

PDF/A requires every font to be embedded, so EmbeddableFontsOnly must be true before a document is saved with StreamType.AdobePDFA. Fonts whose license forbids embedding and fonts that depend on a device are then replaced, and AdaptFont occurs for each of them. Save the Window's Document as PDF/A shows the complete save, Archive a Folder of Documents the NonEmbeddableFont error, and A Document Can't Be Saved as PDF/A the error messages.

Get the Same Result on Every Machine

The installed fonts and the default printer differ from one Windows machine to the next. For the same PDF everywhere:

  • Use fonts every target machine has, or install the fonts of your templates with your application where their licenses allow it.
  • Set the formatting printer, as The Layout Depends on the Printer explains.
  • Check templates before deployment. The blog article Check TX Text Control Template Dependencies Before Deployment lists the fonts a template needs.

Next Steps