Your report design and layout may rely on a font type that is not available in the application's hosting environment. The font may not be installed on the client machine, in a Docker image, in an Azure Virtual Desktop, or in another host/container. In such instances, your report will substitute unavailable fonts with default options. Once a font is substituted, a report may not appear as expected/designed.
DevExpress Reports suite helps you ensure that a report uses correct fonts regardless of the hosting environment. A report notifies you about missing typefaces so you can obtain required font data. Once you obtain the fonts, add them to your report's DXFontRepository and thus make the fonts available to individual report elements/controls.
You can obtain required font data from any font hosting service. This example illustrates a service that downloads missing fonts from Google Fonts.
[!Note]
Review license agreements associated with fonts that you use. Use and redistribution permissions may vary. The service used in this example (Google Fonts) hosts fonts that are open source and without cost. Review Google Fonts FAQ to learn more.
Example Details
The report in this example contains a few fonts that may be missing in many hosting environments: Ga Maamli, Roboto, and Nerko One. The example obtains these fonts if missing and make them available to report controls. When exported to PDF, the report uses the original fonts (result.pdf):
Implementation
The DXFontRepository.QueryNotFoundFont event fires for every unavailable font type. The event handler does the following:
- Identifies the missing typeface and its suggested alternative (
e.RequestedFont
ande.ActualFont
) - Obtains the required font file from Google Fonts
- Prepares a byte array and passes it to
e.FontFileData
This implementation ensures that DXFontRepository
contains all required font types before document generation begins.
[!Important]
Use your personal Google API Key to run this example. For instructions on how to obtain your key, see Google Fonts Developer API.Assign your API Key to the
apiKey
variable in the FontCollectorService.cs file before you launch the example.
Files to Review
Documentation
Does this example address your development requirements/objectives?
(you will be redirected to DevExpress.com to submit your response)
Example Code
C#using System.IO;
using System.Net.Http;
using System.Text.Json;
namespace LoadMissingFonts {
public class FontCollectorService {
class MyFont {
public string? Family { get; set; }
public string? Menu { get; set; }
public Files? Files { get; set; }
}
class MyFontList {
public List<MyFont>? Items { get; set; }
}
class Files {
public string? regular { get; set; }
}
string apiKey = "YOUR_API_KEY";
string fontApiUrl = "https://www.googleapis.com/webfonts/v1/webfonts/?family=";
async Task<byte[]?> LoadFontFromGoogle(string fontName) {
string fontUrl = $"{fontApiUrl}{fontName}&key={apiKey}";
using (HttpClient client = new HttpClient()) {
HttpResponseMessage response = await client.GetAsync(fontUrl).ConfigureAwait(false);
if (!response.IsSuccessStatusCode) {
Console.WriteLine(response.StatusCode);
return null;
}
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
MyFontList? webfontList = JsonSerializer.Deserialize<MyFontList>(content,new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
return await LoadFontFile(webfontList.Items[0].Files.regular).ConfigureAwait(false);
}
}
async Task<byte[]?> LoadFontFile(string fontUrl) {
using (HttpClient client = new HttpClient()) {
HttpResponseMessage response = await client.GetAsync(fontUrl).ConfigureAwait(false);
if (!response.IsSuccessStatusCode) {
Console.WriteLine(response.StatusCode);
return null;
}
using (MemoryStream fileStream = new MemoryStream()) {
await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
return fileStream.ToArray();
}
}
}
public Task<byte[]?> ProcessFont(string fontName) {
return LoadFontFromGoogle(fontName);
}
}
}
C#using DevExpress.Drawing;
using DevExpress.XtraReports.UI;
using FontDemoReport;
namespace LoadMissingFonts {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
using (var report = new DemoReport()) {
DXFontRepository.QueryNotFoundFont += Report_QueryNotFoundFont;
using (var tool = new ReportPrintTool(report)) {
tool.ShowRibbonPreviewDialog();
}
}
Close();
}
private static void Report_QueryNotFoundFont(object sender, NotFoundFontEventArgs e) {
var service = new FontCollectorService();
var fontData = service.ProcessFont(e.RequestedFont).Result;
e.FontFileData = fontData;
}
}
}