:::: MENU ::::

Tuesday, August 17, 2021

There are lots of occasions where you want to create a PDF document, e.g. for a report or an invoice. While there are plenty of options for PDF handling in .NET it turns out none of them actually work with Azure Functions running true serverless in a consumption plan. This is because all libraries I found do rely on GDI+ which is not available in the consumption plan. However most of these solutions work with Azure Functions running in App Plan Basic or higher.

So what is the alternative for running truly serverless?

One of the beauties of serverless systems to me is that you can mix and match runtimes and programming languages and really leverage the advantages of each to compose a distributed system. So while I prefer to build most Function apps using C# and .NET in this case nodejs with Javascript is a better alternative. Since the PDF creation will just be a small component of the overall solution I'm trying to build I can still use C# for all of my other functions.

For Javascript there is a really good library that works both in the browser and the server using nodejs: https://github.com/bpampuch/pdfmake

This library works well with Azure Functions running in the consumption plan and allows easy document composing using a template.

So let's create a new Azure Functions app. An intro into creating Azure Functions in general can be found here: https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-function-vs-code?pivots=programming-language-javascript

After that we need to install the pdfmake library

npm install pdfmake — save

I wanted the actual PDF template to be hosted in Azure blob storage so I can easily update it. Therefore we need to add an input binding to Azure storage to retrieve the template in our function.json:

{    "bindings": [      {        "authLevel": "function",        "type": "httpTrigger",        "direction": "in",        "name": "req",        "methods": [          "post"        ],        "route": "pdf"      },      {        "type": "http",        "direction": "out",        "name": "res"      },      {        "name": "pdfTemplate",        "type": "blob",        "path": "templates/template.json",        "connection": "Storage",        "direction": "in"      }    ]  }

Here is a sample template, I'm using to create a voucher. Find the full reference for creating pdf with pdfmake here: https://pdfmake.github.io/docs/

{      "pageSize": "A4",      "pageOrientation": "portrait",      "content": [        {          "text": "Gutschein",          "fontSize": 27,          "color": "#707070",          "margin": [            0,16          ],          "style": "page"        },        {          "text": "einlösbar bei",          "fontSize": 15,          "color": "#707070",          "margin": [            0,32,0,4          ],          "style": "page"        },        {          "text": "{vendor_name}",          "fontSize": 15,          "bold": true,          "style": "page"        },        {          "text": "{vendor_street}",          "fontSize": 15,          "style": "page"        },        {          "text": "{vendor_zip_city}",          "fontSize": 15,          "style": "page"        }      ]    }

So finally let's put this all together in our function's index.js

const pdfMakePrinter = require('pdfmake');    module.exports = async function (context, req) {      const voucher = context.req.body;      let templateStr = JSON.stringify(context.bindings.pdfTemplate);        templateStr = templateStr.replace('{vendor_name}', voucher.vendorName);      templateStr = templateStr.replace('{vendor_street}', voucher.vendorStreet);      templateStr = templateStr.replace('{vendor_zip_city}', voucher.vendorZipCity);        const pdf = await generatePDF(JSON.parse(templateStr));        context.res = {          body: pdf      };  };    async function generatePDF(docDefinition) {        const fontDescriptors = {           Roboto: {              normal: 'fonts/Roboto-Regular.ttf',              bold: 'fonts/Roboto-Medium.ttf',              italics: 'fonts/Roboto-Italic.ttf',              bolditalics: 'fonts/Roboto-MediumItalic.ttf'          }      };      const printer = new pdfMakePrinter(fontDescriptors);      const doc = printer.createPdfKitDocument(docDefinition);        return new Promise(resolve => {          const chunks = [];          doc.end();          doc.on('data', (chunk) => {              chunks.push(chunk);          });          doc.on('end', () => {              resolve(Buffer.concat(chunks));          });          });      };

Voucher is a json object we are receiving in the body of the incoming post request. The pdf template is providing via an input binding. After replacing some placeholders with real values in the template we can create the pdf file using the method generatePDF and return it as http response.

And that's it! Pretty simple eh? Now I can just call this function from other functions or from where ever.

----