I am using MigraDoc to generate PDFs in my ASP.NET5 MVC6 Web Application, which is deployed to the Azure cloud. I am using version 1.50 beta-2, but I have also tried using v1.50 beta-1 and v1.32.
I have had success generating PDFs when the app is running locally. However, I have been having significant trouble generating PDFs when the app is running on the cloud server due to not having access to any fonts. Following the PDFsharp docs, I tried creating a "private font" by embedding a font within my code.
I had success generating PDFs on the cloud using PDFsharp directly
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.
}
However, I would now like to take advantage of MigraDoc so that I don't have to do all the typesetting myself.
I followed the excellent guide on Thomas Hövel's blog here (this is the new one for beta-2, although I had also previously followed his earlier post for beta-1). His project works perfectly for me, as-is, locally.
I implemented the example for use in my Web App project. It's exactly the same as Thomas' code except main
is a method in my controller that is run on a button click:
Font Resolver Classes:
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;
}
}
}
Home controller:
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;
}
}
When I run the application and click the button triggering the demo code, I get an error "Font 'Ubuntu' cannot be found" that occurs at renderer.RenderDocument(). How can I get the font resolver to find/recognise the font so I can use MigraDoc to generate PDFs on my ASP.NET MVC application?
The full error message and stack trace is as follows:
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
renderer.RenderDocument();
. The HomeController source you are showing contains the linepdfRenderer.RenderDocument();
. Maybe the code you are running is not the code you want to run - at least it's not the code you are showing here. Makes it difficult to locate the error. – EaselWriteDocument
method to isolate it more clearly. I have updated the code showing this, you can hopefully see whereLine 307: renderer.RenderDocument();
is now in that method. – Railroad