用于嵌入字体的 PDFsharp / MigraDoc 字体解析器:System.ArgumentException:找不到字体“Ubuntu”

Iva*_*van 6 c# asp.net-mvc fonts pdfsharp migradoc

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

\n\n

当应用程序在本地运行时,我已成功生成 PDF。然而,当应用程序在云服务器上运行时,由于无法访问任何字体,我在生成 PDF 时遇到了很大的问题。 按照 PDFsharp 文档,我尝试通过在代码中嵌入字体来创建“私有字体”。

\n\n

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

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

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

\n\n

我在这里遵循了 Thomas H\xc3\xb6vel\ 博客上的优秀指南(这是 beta-2 的新指南,尽管我之前也关注过他之前关于 beta-1 的帖子)。他的项目在本地非常适合我。

\n\n

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

\n\n

字体解析器类:

\n\n
public class DemoFontResolver : IFontResolver\n{\n    public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic)\n    {\n        // Ignore case of font names.\n        var name = familyName.ToLower();\n\n        // Deal with the fonts we know.\n        switch (name)\n        {\n            case "ubuntu":\n                if (isBold)\n                {\n                    if (isItalic)\n                        return new FontResolverInfo("Ubuntu#bi");\n                    return new FontResolverInfo("Ubuntu#b");\n                }\n                if (isItalic)\n                    return new FontResolverInfo("Ubuntu#i");\n                return new FontResolverInfo("Ubuntu#");\n\n            case "janitor":\n                return new FontResolverInfo("Janitor#");\n        }\n\n        // We pass all other font requests to the default handler.\n        // When running on a web server without sufficient permission, you can return a default font at this stage.\n        return PlatformFontResolver.ResolveTypeface(familyName, isBold, isItalic);\n    }\n\n    /// <summary>\n    /// Return the font data for the fonts.\n    /// </summary>\n    public byte[] GetFont(string faceName)\n    {\n        switch (faceName)\n        {\n            case "Janitor#":\n                return DemoFontHelper.Janitor;\n\n            case "Ubuntu#":\n                return DemoFontHelper.Ubuntu;\n\n            case "Ubuntu#b":\n                return DemoFontHelper.UbuntuBold;\n\n            case "Ubuntu#i":\n                return DemoFontHelper.UbuntuItalic;\n\n            case "Ubuntu#bi":\n                return DemoFontHelper.UbuntuBoldItalic;\n        }\n\n        return GetFont(faceName);\n    }\n}\n\n/// <summary>\n/// Helper class that reads font data from embedded resources.\n/// </summary>\npublic static class DemoFontHelper\n{\n    public static byte[] Janitor\n    {\n        get { return LoadFontData("RealEstateDocumentGenerator.fonts.janitor.Janitor.ttf"); }\n    }\n\n    // Tip: I used JetBrains dotPeek to find the names of the resources (just look how dots in folder names are encoded).\n    // Make sure the fonts have compile type "Embedded Resource". Names are case-sensitive.\n    public static byte[] Ubuntu\n    {\n        get { return LoadFontData("RealEstateDocumentGenerator.fonts.ubuntufontfamily0._80.Ubuntu-B.ttf"); }\n    }\n\n    public static byte[] UbuntuBold\n    {\n        get { return LoadFontData("RealEstateDocumentGenerator.fonts.ubuntufontfamily0._80.Ubuntu-B.ttf"); }\n    }\n\n    public static byte[] UbuntuItalic\n    {\n        get { return LoadFontData("RealEstateDocumentGenerator.fonts.ubuntufontfamily0._80.Ubuntu-RI.ttf"); }\n    }\n\n    public static byte[] UbuntuBoldItalic\n    {\n        get { return LoadFontData("RealEstateDocumentGenerator.fonts.ubuntufontfamily0._80.Ubuntu-BI.ttf"); }\n    }\n\n    /// <summary>\n    /// Returns the specified font from an embedded resource.\n    /// </summary>\n    static byte[] LoadFontData(string name)\n    {\n        var assembly = Assembly.GetExecutingAssembly();\n\n        using (Stream stream = assembly.GetManifestResourceStream(name))\n        {\n            if (stream == null)\n                throw new ArgumentException("No resource with name " + name);\n\n            int count = (int)stream.Length;\n            byte[] data = new byte[count];\n            stream.Read(data, 0, count);\n            return data;\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

家庭控制器:

\n\n
public class HomeController : Controller\n{\n    [HttpPost]\n    public ActionResult CreateDocument()\n    {\n        DemoProjectMain();\n        return View();\n    }\n\n    public void DemoProjectMain()\n    {\n        // That\'s all it takes to register your own fontresolver\n        GlobalFontSettings.FontResolver = new DemoFontResolver();\n\n        // And now the slightly modified MigraDoc Hello World sample.\n\n        // Create a MigraDoc document\n        Document document = DemoCreateDocument();\n        document.UseCmykColor = true;\n\n        // Create a renderer for the MigraDoc document.\n        PdfDocumentRenderer pdfRenderer = new PdfDocumentRenderer(unicode);\n\n        WriteDocument(document, pdfRenderer);\n    }\n\n    public void WriteDocument(Document document, PdfDocumentRenderer renderer)\n    {\n\n        renderer.Document = document;\n        renderer.RenderDocument();\n\n        // Send PDF to browser\n        MemoryStream stream = new MemoryStream();\n        renderer.PdfDocument.Save(stream, false);\n        Response.Clear();\n        Response.ContentType = "application/pdf";\n        Response.AddHeader("content-length", stream.Length.ToString());\n        Response.BinaryWrite(stream.ToArray());\n        Response.Flush();\n        stream.Close();\n        Response.End();\n    }\n\n    /// <summary>\n    /// Creates an absolutely minimalistic document.\n    /// </summary>\n    static Document DemoCreateDocument()\n    {\n        // Create a new MigraDoc document\n        Document document = new Document();\n\n        DemoSetupStyles(document);\n\n        // Add a section to the document\n        Section section = document.AddSection();\n\n        // Add a paragraph to the section\n        Paragraph paragraph = section.AddParagraph();\n\n        paragraph.Format.Font.Color = Color.FromCmyk(100, 30, 20, 50);\n\n        // Add some text to the paragraph\n        paragraph.AddFormattedText("Hello, World!", TextFormat.Bold);\n\n        section.AddParagraph("Hello, World!");\n\n        // Demonstration for Heading styles.\n        paragraph = section.AddParagraph("Hello, World! (Heading 1)");\n        paragraph.Style = StyleNames.Heading1;\n\n        paragraph = section.AddParagraph("Hello, World! (Heading 2)");\n        paragraph.Style = StyleNames.Heading2;\n\n        paragraph = section.AddParagraph("Hello, World! (Heading 3)");\n        paragraph.Style = StyleNames.Heading3;\n\n        paragraph = section.AddParagraph("Hello, World! (Heading 4)");\n        paragraph.Style = StyleNames.Heading4;\n\n        paragraph = section.AddParagraph();\n\n        paragraph.Format.Font.Color = Color.FromCmyk(100, 30, 20, 50);\n\n        // Add some text to the paragraph\n        paragraph.AddFormattedText("Hello, World!", TextFormat.Bold);\n\n        section.AddParagraph("Hello, World!");\n\n        return document;\n    }\n\n    private static void DemoSetupStyles(Document document)\n    {\n        // Default font for all styles.\n        var style = document.Styles[StyleNames.Normal];\n        style.Font.Name = "Ubuntu";\n\n        // Overwrite font for headings 1 & 2.\n        style = document.Styles[StyleNames.Heading1];\n        style.Font.Name = "Janitor";\n        style.Font.Size = 32;\n\n        // Heading 2 inherits font from Heading 1.\n        style = document.Styles[StyleNames.Heading2];\n        style.Font.Size = 28;\n\n        // Set normal font for Heading 3.\n        style = document.Styles[StyleNames.Heading3];\n        style.Font.Name = "Ubuntu";\n        style.Font.Size = 24;\n\n        style = document.Styles[StyleNames.Heading4];\n        style.Font.Size = 20;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

当我运行应用程序并单击触发演示代码的按钮时,我在 renderer.RenderDocument() 处收到错误“无法找到 Font \'Ubuntu\'”。 如何让字体解析器查找/识别字体,以便我可以使用 MigraDoc 在 ASP.NET MVC 应用程序上生成 PDF?

\n\n

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

\n\n
\n

\'/\' 应用程序中的服务器错误。

\n\n

找不到字体“Ubuntu”。

\n\n

描述: 执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其在代码中的来源的详细信息。\n

\n\n

异常详细信息:System.ArgumentException:找不到字体“Ubuntu”。

\n\n

来源错误:

\n\n
Line 305:\nLine 306:            renderer.Document = document;\nLine 307:            renderer.RenderDocument();\nLine 308:\nLine 309:            // Send PDF to browser\n
Run Code Online (Sandbox Code Playgroud)\n\n

源文件:C:\\ Users \\ User \\ Documents \\ Visual Studio 2015 \\ Projects \\ DocumentGenerator \\ DocumentGenerator \\ Controllers \\ HomeController.cs \\ n行:307

\n\n

堆栈跟踪:

\n\n

[ArgumentException:找不到字体“Ubuntu”。]

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

版本信息: Microsoft .NET Framework 版本:4.0.30319;\n ASP.NET 版本:4.6.79.0

\n
\n

Je *_*not 4

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

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

AFAIK 您仍然需要在 GDI 构建中使用 XPrivateFontCollection。如果您想坚持在服务器上构建 GDI,请删除 IFontResolver 并使用 XPrivateFontCollection。