10

I am working on MVC 6 application(DNX Core 5.0 framework). Unfortunately, I don't find any library for pdf export.

Any help will be appreciated.

Tseng
  • 61,549
  • 15
  • 193
  • 205
Antoan Elenkov
  • 711
  • 1
  • 5
  • 20
  • 1
    It's not the same issue. There are many libraries for exporting to pdf, but for "DNX Core 5.0 framework" I found none. – Antoan Elenkov May 02 '16 at 13:45
  • 1
    A framework is nothing more than a wrapper to make things easier on your side. You can still use libraries in your application. – Audite Marlow May 02 '16 at 13:46
  • I will give you example with PdfSharp. When I download the package, I have this build error: "The dependency PDFsharp 1.32.3057 in project SharedKernel does not support framework DNXCore,Version=v5.0." – Antoan Elenkov May 02 '16 at 17:01
  • This describes an all new problem to your initial question. If all PDF libraries give you this build error, wouldn't it be a good idea to look for that problem instead? If you can't solve it, open a new question. – Audite Marlow May 03 '16 at 08:14
  • 1
    My answer to a simlar question might be helpful. I'm using wkhtmltopdf on azure functions. http://stackoverflow.com/questions/33139317/wht-is-the-best-solution-for-html-to-pdf-on-azure-web-app/42136460#42136460 – Tom Makin Feb 09 '17 at 12:18

5 Answers5

14

I finally figured out a way to generate pdf's from .NET Core (without any .NET framework dependencies) is using Node.js from within my .NET Core application. The following example shows how to implementing a HTML to PDF converter in a clean ASP.NET Core Web Application project (Web API template).

Install the NuGet package Microsoft.AspNetCore.NodeServices

In Startup.cs add the line services.AddNodeServices() like this

public void ConfigureServices(IServiceCollection services)
{
    // ... all your existing configuration is here ...

    // Enable Node Services
    services.AddNodeServices();
}

Now install the required Node.js packages:

From the command line change working directory to the root of the .NET Core project and run these commands.

npm init

and follow the instructions to create the package.json file

npm install jsreport-core --save
npm install jsreport-jsrender --save
npm install jsreport-phantom-pdf --save

Create a file pdf.js in the root of the project containing

module.exports = function (callback) {
    var jsreport = require('jsreport-core')();

    jsreport.init().then(function () {
        return jsreport.render({
            template: {
                content: '<h1>Hello {{:foo}}</h1>',
                engine: 'jsrender',
                recipe: 'phantom-pdf'
            },
            data: {
                foo: "world"
            }
        }).then(function (resp) {
            callback(/* error */ null, resp.content.toJSON().data);
        });
    }).catch(function (e) {
        callback(/* error */ e, null);
    })
};

Have a look here for more explanation on jsreport-core.

Now create an action in an Mvc controller that calls this Node.js script

[HttpGet]
public async Task<IActionResult> MyAction([FromServices] INodeServices nodeServices)
{
    var result = await nodeServices.InvokeAsync<byte[]>("./pdf");

    HttpContext.Response.ContentType = "application/pdf";

    string filename = @"report.pdf";
    HttpContext.Response.Headers.Add("x-filename", filename);
    HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "x-filename");
    HttpContext.Response.Body.Write(result, 0, result.Length);
    return new ContentResult();
}

Off course you can do whatever you want with the byte[] returned from nodeServices, in this example I'm just outputting it from a controller action so it can be viewed in the browser.

You could also exchange the data between Node.js and .NET Core by a base64 encoded string using resp.content.toString('base64') in pdf.js and use var result = await nodeServices.InvokeAsync<byte[]>("./pdf"); in the action and then decode the base64 encoded string.


Alternatives

Most pdf generator solutions still depend on .NET 4.5/4.6 framework. None of the two answers above (JsReport and RazorPDFCore) works for .NET Core yet.

There seems to be some paid alternatives available if you don't like to use Node.js:

  • NReco.PdfGenerator.LT
  • EVO HTML to PDF Converter Client for .NET Core
  • Winnovative HTML to PDF Converter Client for .NET Core

I haven't tried any of these though.

I hope we will soon see some open source progress in this area.

Sjolund
  • 519
  • 5
  • 10
  • This solution works locally but not when I upload my website to azure unfortunately :( – CallumVass Apr 14 '17 at 08:32
  • @CallumVass I had issues as well when deploying to IIS. Make sure NodeJS is installed on the Azure instance. Also make sure that node_modules and js-files are deployed as well. Otherwise you could try fiddeling with the project path: `services.AddNodeServices(options => options.ProjectPath = "path/to/project/with/js");`. As an alternative have a look at Stefan Steiger's [answer](http://stackoverflow.com/a/43473181/5747654) - looks like there is a .Net Core library on its way :) – Sjolund Apr 19 '17 at 07:27
  • 1
    Your action implementation could be simplified, since many people will probably just copy the example: `return new FileContentResult(result, "application/pdf") {FileDownloadName = "report.pdf"};` – Michael Petito Jul 07 '17 at 12:54
  • It works! However I want to store the generated PDF in my server folder. Please help – Adrita Sharma Mar 19 '18 at 09:47
10

If you must rely on Core you'll have two options:

1 - Wait a bit

Core is still RC1, slowly moving to RC2, and you won't find much libs really soon. Since .NET Core is taking much attention, first libs should come out in a few months, but I'd guess you'll have to wait for at least RC2 release.

2 - Fork (or similar)

You can grab an open-source project that best fits your needs, fork (if on GitHub) or just download and start updating to .NET Core. I've just done that with DapperExtensions and it's working like a charm. You can even add some spicy just for you ;)


On the other hand, if you just need something that works but with no direct need of embedding into .NET Core, I've managed to make JsReport work fine. It will start it's very own server (embedded server) based on Node but integration is really easy (with AspNet Core very own Dependecy Injection system!) and PDF are created with no further issue.

If that interests you, here are some instructions:

1 - References

Add those to your project.json:

"jsreport.Embedded": "0.8.1",
"jsreport.Client": "0.8.1"

2 - AspNet integration

After, follow instructions from jsReport here. You can configure AspNet DI system as here:

public void ConfigureServices(IServiceCollection services)
{
   // ...
   var _server = new EmbeddedReportingServer();
   _server.StartAsync().Wait();
   services.AddInstance<IEmbeddedReportingServer>(_server);
   services.AddSingleton<IReportingService>((s) => { return s.GetRequiredService<IEmbeddedReportingServer>().ReportingService; });
   // ...
}

To use you'll just have to either receive an IReportingService or manually grab it from Resolver on your controller, for instance.

3 - Usage

public IActionResult SomeReport()
{
   // This is <my> type of usage. It's a bit manual because I'm currently loading reports from DB. You can use it in a diferent way (check jsReport docs).
   var service = Resolver.GetRequiredService<jsreport.Client.IReportingService>();

   var phantomOptions = new jsreport.Client.Entities.Phantom()
   {
      format = "A4",
      orientation = "portrait",
      margin = "0cm"
   };
   phantomOptions.footer = "<h2>Some footer</h2>";
   phantomOptions.footerHeight = "50px";
   phantomOptions.header = "<h2>Some header</h2>";
   phantomOptions.headerHeight = "50px";
   var request = new jsreport.Client.RenderRequest()
   {
      template = new jsreport.Client.Entities.Template()
      {
         content = "<div>Some content for your report</div>",
         recipe = "phantom-pdf",
         name = "Your report name",
         phantom = phantomOptions
      }
   };

   var _report = service.RenderAsync(request).Result;

   // Request file download.
   return File(_report.Content, "application/pdf", "Some fancy name.pdf");
}

4 - Important: your server won't start (missing a zip file)

Due to changes from NuGet on AspNet projects, you have to manually move some content files which are not moved automatically.

First, find your dnx cache for the embedded server. Should be something like:
C:\Users\<name>\.dnx\packages\jsreport.Embedded\0.8.1.

You'll notice a folder called content there. Simply copy it's contents (two files: node.exe and jsreport-net-embedded.zip) into lib\net45.

So, to be plain simple and fool-proof: copy contents (files only) from
C:\Users\<name>\.dnx\packages\jsreport.Embedded\0.8.1\contents
into
C:\Users\<name>\.dnx\packages\jsreport.Embedded\0.8.1\lib\net45.

That should solve startup issues. Remember: first startup will extract files and should take a few minutes. After that, it will be much much faster.

Anderson Matos
  • 3,132
  • 1
  • 23
  • 33
3

I know that this question was asked a while ago, and I know that there have been several answers provided already that may well be right for certain projects. But I recently created a GitHub repository that allows for the creation of PDFs directly from your C# code without any requirement for nodejs, javascript, or razor. The feature set is a bit limited at the moment but it generates PDFs with images (.jpg only at this stage), shapes, and formatted text. The library works with .net core 2.0 and has no dependency on any other PDF generation tool.

Please note that this is my own repository: https://github.com/GZidar/CorePDF

I do plan to add functionality over time but at least for now this may provide the basis for others to include simple PDF capability in their own projects without the need for additional tooling.

G. Zidar
  • 195
  • 2
  • 8
  • Have you considered publish this as a Nuget? – Codendaal May 14 '20 at 10:32
  • @Codendaal there is a nuget for this it is called CorePDF (https://www.nuget.org/packages/CorePDF/0.5.0-beta) and you will need to check the "include prerelease" checkbox. It is flagged as beta because on of the dependencies is beta. – G. Zidar May 15 '20 at 01:20
2

I have amended the RazorAnt/RazorPDF which was working only for older MVC versions to work with ASP.NET Core. Its RazorPDFCore, available on nuget and github:

Example usage

class YourBaseController : RazorPDF.Controller {
    // [...]
    public IActionResult Pdf() {
        var model = /* any model you wish */
        return ViewPdf(model);
    }
}

In your Startup.cs

add the following line before services.AddMVc();

services.AddSingleton<PdfResultExecutor>();

PLEASE NOTE:

You need to inhere RazorPDF.Controller from your base controller before using the ViewPdf() method

Ole K
  • 754
  • 1
  • 9
  • 32
2

Necromancing.

Adding a dependency to NodeJS is subideal IMHO, especially considering .NET Core self-contained deployment.

As per 2017, you could use my port of PdfSharpCore to .NET Core 1.1
Resolves fonts, and it can use images. Comes with a nice sample application. You'll have to replace the DB part, however.

Credits go to:
https://github.com/groege/PdfSharpCore

which is a bit outdated, and doesn't contain a sample on how to use it with images.

Note that you need to register the font-resolver and the imageSource-Implementation before using the respective features:

PdfSharpCore.Fonts.GlobalFontSettings.FontResolver = new FontResolver();

MigraDocCore.DocumentObjectModel.MigraDoc.DocumentObjectModel
    .Shapes.ImageSource.ImageSourceImpl = 
          new PdfSharpCore.ImageSharp.ImageSharpImageSource();
Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
  • I agree that my proposal on using NodeJS isn't ideal, but it was the only solution I could find then. Will definately try your PdfSharpCore port! - Thanks. – Sjolund Apr 19 '17 at 07:37
  • @Stefan Steiger. Am using PDFSharpCore, Want to add image to Pdf but receiving error in Ximage - object reference not set. Also tried using above code but could not find "ImageSharp". Please let me know what missing. – Kenny Nov 06 '17 at 13:01
  • @Stefan Steiger. Do I need to add implementation for ImageSharp similar to font resolver? – Kenny Nov 06 '17 at 13:56
  • @kanayabhattad: You need an old version of imagesharp. There have been too many breaking changes. https://github.com/SixLabors/ImageSharp – Stefan Steiger Nov 07 '17 at 11:52
  • @StefanSteiger I've got the same issue, but I'm not clear on what you mean by needing an old version of ImageSharp. Does the above code remain the same? What version of ImageSharp should I be using? I tried both 1.0.0-beta0002 and 1.0.0-beta0001. Thanks! – matthew_b Nov 20 '17 at 19:24
  • @kanayabhattad Did you manage to solve this issue? I'm experiencing the same thing so any help would be appreciated. – matthew_b Nov 23 '17 at 19:13
  • @matthew_b: I've merged a pull request that updates to Nuget ImageSharp. If you pull now, it should work. – Stefan Steiger Jan 26 '18 at 10:40