In the past I have wrote an article on how to export html to a pdf in dot net mvc (non dot net core), I have since needed to achieve the same result but using dot net core. The previous article was http://neil.red/coding/c-how-to-export-html-to-pdf-for-free
The solution I have found for dot net core was to use nodejs and seems a rather painless solution.
First you need to install the Microsoft.AspNetCore.NodeServices package
1 |
Install-Package Microsoft.AspNetCore.NodeServices |
Then in your Startup add this line services.AddNodeServices()
like this
1 2 3 4 5 6 7 |
public void ConfigureServices(IServiceCollection services) { // ... all your existing configuration is here ... // Enable Node Services services.AddNodeServices(); } |
Now you need to install the required NodeJs packages, if you already have a package.json you can simply add to it:
1 2 3 4 5 |
// versions may have changed since creating this "jsreport-core": "^1.2.2", "jsreport-jsrender": "^1.0.1", "jsreport-phantom-pdf": "^1.4.3" |
Or you can install from the command line like
1 2 3 |
// only if you don't have a package.json file in your project root npm init |
Then add the packages
1 2 3 |
npm install jsreport-core --save npm install jsreport-jsrender --save npm install jsreport-phantom-pdf --save |
Then in your root folder e.g. where your .xproj file is not the wwwroot folder in dot net core, add a new file called pdf.js and put in it
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
module.exports = function (callback, data) { var jsreport = require('jsreport-core')(); jsreport.init().then(function () { return jsreport.render({ template: { content: '<h1>{{:foo}}</h1>', engine: 'jsrender', recipe: 'phantom-pdf' }, data: { foo: data } }).then(function (resp) { callback(/* error */ null, resp.content.toJSON().data); }); }).catch(function (e) { callback(/* error */ e, null); }) }; |
Then you need to create a method in a controller to call this nodejs file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[HttpGet] [Route("/exportpdf")] public async Task<IActionResult> ExportPdf([FromServices] INodeServices nodeServices) { var result = await nodeServices.InvokeAsync<byte[]>("./pdf", "the data from a controller"); 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(); } |
This simply says, when you hit the route of /exportpdf, then call the nodejs service ./pdf (pdf.js in the root) and pass in a string of data “the data from a controller”.
The pdf.js will then take that data and use jsreport to convert the html into pdf data to be sent back to the method and returned as a ContentResult with type of application/pdf.
This is only a simple example, but you would be able to pass in all sorts of html string in the place of “the data from a controller” parameter and have the nodejs service convert that html to pdf.
8 Comments
Did you ever get the following error:
Call to Node module failed with error: Error: spawn …node_modules\\phantomjs\\lib\\phantom\\phantomjs.exe ENOENT
No I never got that. I’ve had a quick look and it from this post getting ENOENT in node_modules, it sounds like it could be a permissions error: https://github.com/npm/npm/issues/3664
Perhaps check folder permissions for node_modules or even a fresh npm install?
Just to add to this.. i recently did get this error message of “ENOENT” for something else and the error was a folder permissions problem. It depends on what OS you are running etc how you need to set folder permissions though.
Yes, this works, but how can i include an image is ignored in the output
I think you may need to convert the images from relative paths to absolute urls in the html you pass into the pdf.
how to set header and footer in this above node services.
I haven’t tested this.. but I believe you can put inside the template object in the pdf.js two new properties of: header and footer.. e.g.
template: {
content: ‘
{{:foo}}
‘,
engine: ‘jsrender’,
recipe: ‘phantom-pdf’,
header: ‘header content’,
footer: ‘footer content’
},
Did you find a way to set Header , Footer , Paging ? Please share a sample 🙂