嵌入字体的 PDFsharp / MigraDoc 字体解析器:System.ArgumentException:找不到字体 'Ubuntu'

PDFsharp / MigraDoc font resolver for embedded fonts: System.ArgumentException: Font 'Ubuntu' cannot be found

我正在使用 MigraDoc 在部署到 Azure 云的 ASP.NET5 MVC6 Web 应用程序中生成 PDF。我使用的是 1.50 beta-2 版,但我也尝试过使用 v1.50 beta-1 和 v1.32。

当应用程序在本地 运行ning 时,我已成功生成 PDF。但是,由于无法访问任何字体,当应用程序在云服务器上 运行ning 时,我在生成 PDF 时遇到了很大的麻烦。 Following the PDFsharp docs,我尝试通过在代码中嵌入字体来创建 "private font"。

我直接使用 PDFsharp 在云端生成 PDF 成功了

    public static MyResolver FontResolver = new MyResolver();
    public void RenderPdf(CreateDocumentViewModel viewModel)
    {
        GlobalFontSettings.FontResolver = FontResolver;
        //...
        XFont font = new XFont("times-roman", 12, XFontStyle.Regular);
        //This font is then used in DrawString.
    }

不过,我现在想利用 MigraDoc,这样我就不必自己进行所有排版工作了。

我遵循了 Thomas Hövel 博客 here (this is the new one for beta-2, although I had also previously followed his earlier post for beta-1 上的出色指南。他的项目按原样在本地对我来说非常适合。

我实现了用于我的 Web 应用程序项目的示例。 与 Thomas 的代码完全 相同,除了 main 是我控制器中的一个方法,在单击按钮时 运行:

字体解析器类:

public class DemoFontResolver : IFontResolver
{
    public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic)
    {
        // Ignore case of font names.
        var name = familyName.ToLower();

        // Deal with the fonts we know.
        switch (name)
        {
            case "ubuntu":
                if (isBold)
                {
                    if (isItalic)
                        return new FontResolverInfo("Ubuntu#bi");
                    return new FontResolverInfo("Ubuntu#b");
                }
                if (isItalic)
                    return new FontResolverInfo("Ubuntu#i");
                return new FontResolverInfo("Ubuntu#");

            case "janitor":
                return new FontResolverInfo("Janitor#");
        }

        // We pass all other font requests to the default handler.
        // When running on a web server without sufficient permission, you can return a default font at this stage.
        return PlatformFontResolver.ResolveTypeface(familyName, isBold, isItalic);
    }

    /// <summary>
    /// Return the font data for the fonts.
    /// </summary>
    public byte[] GetFont(string faceName)
    {
        switch (faceName)
        {
            case "Janitor#":
                return DemoFontHelper.Janitor;

            case "Ubuntu#":
                return DemoFontHelper.Ubuntu;

            case "Ubuntu#b":
                return DemoFontHelper.UbuntuBold;

            case "Ubuntu#i":
                return DemoFontHelper.UbuntuItalic;

            case "Ubuntu#bi":
                return DemoFontHelper.UbuntuBoldItalic;
        }

        return GetFont(faceName);
    }
}

/// <summary>
/// Helper class that reads font data from embedded resources.
/// </summary>
public static class DemoFontHelper
{
    public static byte[] Janitor
    {
        get { return LoadFontData("RealEstateDocumentGenerator.fonts.janitor.Janitor.ttf"); }
    }

    // Tip: I used JetBrains dotPeek to find the names of the resources (just look how dots in folder names are encoded).
    // Make sure the fonts have compile type "Embedded Resource". Names are case-sensitive.
    public static byte[] Ubuntu
    {
        get { return LoadFontData("RealEstateDocumentGenerator.fonts.ubuntufontfamily0._80.Ubuntu-B.ttf"); }
    }

    public static byte[] UbuntuBold
    {
        get { return LoadFontData("RealEstateDocumentGenerator.fonts.ubuntufontfamily0._80.Ubuntu-B.ttf"); }
    }

    public static byte[] UbuntuItalic
    {
        get { return LoadFontData("RealEstateDocumentGenerator.fonts.ubuntufontfamily0._80.Ubuntu-RI.ttf"); }
    }

    public static byte[] UbuntuBoldItalic
    {
        get { return LoadFontData("RealEstateDocumentGenerator.fonts.ubuntufontfamily0._80.Ubuntu-BI.ttf"); }
    }

    /// <summary>
    /// Returns the specified font from an embedded resource.
    /// </summary>
    static byte[] LoadFontData(string name)
    {
        var assembly = Assembly.GetExecutingAssembly();

        using (Stream stream = assembly.GetManifestResourceStream(name))
        {
            if (stream == null)
                throw new ArgumentException("No resource with name " + name);

            int count = (int)stream.Length;
            byte[] data = new byte[count];
            stream.Read(data, 0, count);
            return data;
        }
    }
}

家庭控制器:

public class HomeController : Controller
{
    [HttpPost]
    public ActionResult CreateDocument()
    {
        DemoProjectMain();
        return View();
    }

    public void DemoProjectMain()
    {
        // That's all it takes to register your own fontresolver
        GlobalFontSettings.FontResolver = new DemoFontResolver();

        // And now the slightly modified MigraDoc Hello World sample.

        // Create a MigraDoc document
        Document document = DemoCreateDocument();
        document.UseCmykColor = true;

        // Create a renderer for the MigraDoc document.
        PdfDocumentRenderer pdfRenderer = new PdfDocumentRenderer(unicode);

        WriteDocument(document, pdfRenderer);
    }

    public void WriteDocument(Document document, PdfDocumentRenderer renderer)
    {

        renderer.Document = document;
        renderer.RenderDocument();

        // Send PDF to browser
        MemoryStream stream = new MemoryStream();
        renderer.PdfDocument.Save(stream, false);
        Response.Clear();
        Response.ContentType = "application/pdf";
        Response.AddHeader("content-length", stream.Length.ToString());
        Response.BinaryWrite(stream.ToArray());
        Response.Flush();
        stream.Close();
        Response.End();
    }

    /// <summary>
    /// Creates an absolutely minimalistic document.
    /// </summary>
    static Document DemoCreateDocument()
    {
        // Create a new MigraDoc document
        Document document = new Document();

        DemoSetupStyles(document);

        // Add a section to the document
        Section section = document.AddSection();

        // Add a paragraph to the section
        Paragraph paragraph = section.AddParagraph();

        paragraph.Format.Font.Color = Color.FromCmyk(100, 30, 20, 50);

        // Add some text to the paragraph
        paragraph.AddFormattedText("Hello, World!", TextFormat.Bold);

        section.AddParagraph("Hello, World!");

        // Demonstration for Heading styles.
        paragraph = section.AddParagraph("Hello, World! (Heading 1)");
        paragraph.Style = StyleNames.Heading1;

        paragraph = section.AddParagraph("Hello, World! (Heading 2)");
        paragraph.Style = StyleNames.Heading2;

        paragraph = section.AddParagraph("Hello, World! (Heading 3)");
        paragraph.Style = StyleNames.Heading3;

        paragraph = section.AddParagraph("Hello, World! (Heading 4)");
        paragraph.Style = StyleNames.Heading4;

        paragraph = section.AddParagraph();

        paragraph.Format.Font.Color = Color.FromCmyk(100, 30, 20, 50);

        // Add some text to the paragraph
        paragraph.AddFormattedText("Hello, World!", TextFormat.Bold);

        section.AddParagraph("Hello, World!");

        return document;
    }

    private static void DemoSetupStyles(Document document)
    {
        // Default font for all styles.
        var style = document.Styles[StyleNames.Normal];
        style.Font.Name = "Ubuntu";

        // Overwrite font for headings 1 & 2.
        style = document.Styles[StyleNames.Heading1];
        style.Font.Name = "Janitor";
        style.Font.Size = 32;

        // Heading 2 inherits font from Heading 1.
        style = document.Styles[StyleNames.Heading2];
        style.Font.Size = 28;

        // Set normal font for Heading 3.
        style = document.Styles[StyleNames.Heading3];
        style.Font.Name = "Ubuntu";
        style.Font.Size = 24;

        style = document.Styles[StyleNames.Heading4];
        style.Font.Size = 20;
    }
}

当我 运行 应用程序并单击触发演示代码的按钮时,我在 renderer.RenderDocument() 处收到错误 "Font 'Ubuntu' cannot be found"。 如何让字体解析器获得 find/recognise 字体,以便我可以使用 MigraDoc 在我的 ASP.NET MVC 应用程序上生成 PDF?

完整的错误消息和堆栈跟踪如下:

Server Error in '/' Application.

Font 'Ubuntu' cannot be found.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.ArgumentException: Font 'Ubuntu' cannot be found.

Source Error:

Line 305:
Line 306:            renderer.Document = document;
Line 307:            renderer.RenderDocument();
Line 308:
Line 309:            // Send PDF to browser

Source File: C:\Users\User\Documents\Visual Studio 2015\Projects\DocumentGenerator\DocumentGenerator\Controllers\HomeController.cs Line: 307

Stack Trace:

[ArgumentException: Font 'Ubuntu' cannot be found.]

System.Drawing.FontFamily.CreateFontFamily(String name, FontCollection fontCollection) +1123173
System.Drawing.FontFamily..ctor(String name) +11
PdfSharp.Drawing.XFontFamily..ctor(String name) +92
MigraDoc.Rendering.FontHandler.GetDescent(XFont font) +129
MigraDoc.Rendering.ParagraphRenderer.CalcVerticalInfo(XFont font) +154
MigraDoc.Rendering.ParagraphRenderer.InitFormat(Area area, FormatInfo previousFormatInfo) +392
MigraDoc.Rendering.ParagraphRenderer.Format(Area area, FormatInfo previousFormatInfo) +62
MigraDoc.Rendering.TopDownFormatter.FormatOnAreas(XGraphics gfx, Boolean topLevel) +738
MigraDoc.Rendering.FormattedDocument.Format(XGraphics gfx) +647
MigraDoc.Rendering.DocumentRenderer.PrepareDocument() +269
MigraDoc.Rendering.PdfDocumentRenderer.PrepareDocumentRenderer(Boolean prepareCompletely) +119
MigraDoc.Rendering.PdfDocumentRenderer.PrepareRenderPages() +19
MigraDoc.Rendering.PdfDocumentRenderer.RenderDocument() +13
DocumentGenerator.Controllers.HomeController.WriteDocument(Document document, PdfDocumentRenderer renderer) in C:\Users\User\Documents\Visual Studio 2015\Projects\DocumentGenerator\DocumentGenerator\Controllers\HomeController.cs:307
DocumentGenerator.Controllers.HomeController.DemoProjectMain() in C:\Users\User\Documents\Visual Studio 2015\Projects\DocumentGenerator\DocumentGenerator\Controllers\HomeController.cs:165
DocumentGenerator.Controllers.HomeController.CreateDocument(CreateDocumentViewModel model, String command) in C:\Users\User\Documents\Visual Studio 015\Projects\DocumentGenerator\DocumentGenerator\Controllers\HomeController.cs:56
lambda_method(Closure , ControllerBase , Object[] ) +146
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +14
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +157
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) +22
System.Web.Mvc.Async.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult) +29
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +32
System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d() +50
System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +225
System.Web.Mvc.Async.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult) +10
System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +34
System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +26
System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +100
System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27
System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +13
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +36
System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller) +12
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +22
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +26
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +21
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +28  
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9723757
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.79.0

您正在使用 MigraDoc 的 GDI 版本。错误信息来自GDI+。

我的示例代码已使用 WPF 构建进行了测试。请尝试在您的服务器上构建 WPF。

据我所知,您仍然必须在 GDI 构建中使用 XPrivateFontCollection。如果您想坚持在服务器上构建 GDI,请删除 IFontResolver 并使用 XPrivateFontCollection。