Embedding Fonts in EPUB

I’m using Aspose.Words to create EPubs. I have a stylesheet linked in the HTML I use as source that has definitions such as this in it:

@font-face {
font-family: "NotCourierSans";
font-style: normal;
font-weight: normal;
src:url(Fonts/NotCourierSans.otf);
}

Now I’m creating the document. ResourceLoadingCallback is being called properly for the linked stylesheet and all images. There is no callback to retrieve the resources embedded in the stylesheet itself. On export the default fonts (such as arial.ttf) appear. The FontSavingCallback seems to write a font, but as I have the font files on disk I need to get them read.

How can I embed fonts such as “NotCourierSans.otf” into the EPUB?

Thanks, Joerg

Hi Joerg,

Thanks for your inquiry. Unfortunately, Aspose.Words does not support the requested feature (load of @font-face {src:url(Fonts/NotCourierSans.otf);} into Aspose.Words.Document) at the moment. However, I have logged this feature request as WORDSNET-8059 in our issue tracking system. You will be notified via this forum thread once this feature is available. We apologize for your inconvenience.

Please read the features supported on HTML import/export from here:
https://docs.aspose.com/words/net/document-features-supported-on-html-import/
https://docs.aspose.com/words/net/document-features-supported-on-html-export/

Moreover, the HtmlSaveOptions.ExportFontResources property specifies whether font resources should be exported to HTML, MHTML or EPUB. Default is false.

Document doc = new Document(MyDir + "in.html");
// Set the option to export font resources.
HtmlSaveOptions options = new HtmlSaveOptions(SaveFormat.Epub);
options.ExportFontResources = true;
doc.Save(MyDir + "Out.epub", options);

Thanks for your answer. I made a solution by myself and this might help others.

After creating the content in memory I just unpack the ZIP and add the missing stuff. Assume for the following code that the variable aspose is of type Document, msOut a MemoryStream and templates is a dictionary of type IDictionary<string, byte[]> which contains css and font files.

The code makes a copy of the output stream, manipulates it using the new .NET 4.5 ZipArchive class and adds the necessary font files. The OPF file will be changed as well to reflect the missing manifest items in it. It finally replaces the css with the css wrongly changed by Aspose.words.

aspose.Save(msOut, so);
var msModified = new MemoryStream();
msOut.Position = 0;
msOut.CopyTo(msModified);
var fontTemplates = templates
.Where(t => t.InternalName.EndsWith(".otf") || t.InternalName.EndsWith(".ttf"))
.ToList();
if (fontTemplates.Any())
{
    msModified.Position = 0;
    var contentFolderInEpub = "OEBPS"; // this is what aspose uses
                                        // so now msOut has the zipped result, just adding a few file we'll need
    using (var gz = new ZipArchive(msModified, ZipArchiveMode.Update, true))
    {
        // get OPF
        var opfName = String.Concat(contentFolderInEpub, "/", doc.Name, ".opf");
        XDocument opfDoc;
        var opf = gz.GetEntry(opfName);
        using (var opfStream = opf.Open())
        {
            opfStream.Position = 0;
            opfDoc = XDocument.Load(opfStream);
            var defaultNamespace = opfDoc.Root.GetDefaultNamespace();
            var cnt = 1;
            foreach (var font in fontTemplates)
            {
                var mimetype = MimeTypeHelper.GetMimeFromBytes(font.Content);
                // add 
                opfDoc.Root.Element(defaultNamespace + "manifest").Add(
                new XElement(defaultNamespace + "item",
                new XAttribute("id", "font" + cnt++),
                new XAttribute("href", "Fonts/" + font.InternalName),
                new XAttribute("media-type", mimetype)));
            }
        }
        opf.Delete();
        opf = gz.CreateEntry(opfName, CompressionLevel.Optimal);
        using (var sr = new StreamWriter(opf.Open()))
        {
            opfDoc.Save(sr);
        }
        foreach (var font in fontTemplates)
        {
            var fontEntry = gz.CreateEntry(String.Concat(contentFolderInEpub, "/Fonts/", font.InternalName), CompressionLevel.Optimal);
            using (var sr = fontEntry.Open())
            {
                sr.Write(font.Content, 0, font.Content.Length);
            }
        }
        // replace the generated style sheet with our own
        var cssName = String.Concat(contentFolderInEpub, "/styles.css");
        var cssFile = gz.GetEntry(cssName);
        cssFile.Delete();
        foreach (var cssTemplate in cssTemplates)
        {
            cssFile = gz.CreateEntry(String.Concat(contentFolderInEpub, "/", cssTemplate.InternalName), CompressionLevel.Optimal);
            using (var sr = cssFile.Open())
            {
                sr.Write(cssTemplate.Content, 0, cssTemplate.Content.Length);
            }
        }
        // find all HTML files and replace the generic shhet with our collection of sheets
    }
}
var result = msModified.ToArray();

Hi Joerg,

Thanks for your feedback. It is nice to hear from you that you have solved your problem. We always appreciate positive feedback from our customers.

Please feel free to ask if you have any question about Aspose.Words, we will be happy to help you.